Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package picom for openSUSE:Factory checked in at 2024-02-13 22:44:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/picom (Old) and /work/SRC/openSUSE:Factory/.picom.new.1815 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "picom" Tue Feb 13 22:44:40 2024 rev:10 rq:1146416 version:11.2 Changes: -------- --- /work/SRC/openSUSE:Factory/picom/picom.changes 2024-02-06 16:36:41.567702615 +0100 +++ /work/SRC/openSUSE:Factory/.picom.new.1815/picom.changes 2024-02-13 22:45:08.942254978 +0100 @@ -1,0 +2,16 @@ +Tue Feb 13 13:23:17 UTC 2024 - Alexei Sorokin <sor.ale...@meowr.ru> + +- Update to version 11.2: + * picom now depends on libepoxy for OpenGL symbol management. + * Workaround a NVIDIA problem that causes high CPU usage after + suspend/resume. + * Fix occasional freezes. + * Fix corner-radius-rules not applying sometimes. + * Fix window shader not having an effect when frame opacity is + enabled. + * Fix binding root pixmap in case of depth mismatch + (boo#1217786). +- Remove picom-11.1-fix-nvidia-high-cpu-usage.patch: fixed + upstream. + +------------------------------------------------------------------- Old: ---- picom-11.1-fix-nvidia-high-cpu-usage.patch picom-11.1.tar.gz New: ---- picom-11.2.tar.gz BETA DEBUG BEGIN: Old: (boo#1217786). - Remove picom-11.1-fix-nvidia-high-cpu-usage.patch: fixed upstream. BETA DEBUG END: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ picom.spec ++++++ --- /var/tmp/diff_new_pack.ugmAC8/_old 2024-02-13 22:45:09.626279593 +0100 +++ /var/tmp/diff_new_pack.ugmAC8/_new 2024-02-13 22:45:09.630279737 +0100 @@ -17,15 +17,13 @@ Name: picom -Version: 11.1 +Version: 11.2 Release: 0 Summary: Stand-alone compositor for X11 License: MIT AND MPL-2.0 Group: System/X11/Utilities URL: https://github.com/yshui/picom Source: https://github.com/yshui/picom/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz -# PATCH-FIX-UPSTREAM picom-11.1-fix-nvidia-high-cpu-usage.patch yshu...@gmail.com -- Workaround a NVIDIA problem that causes high CPU usage after suspend/resume -Patch0: picom-11.1-fix-nvidia-high-cpu-usage.patch BuildRequires: asciidoc BuildRequires: c_compiler BuildRequires: hicolor-icon-theme @@ -34,6 +32,7 @@ BuildRequires: uthash-devel BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(egl) +BuildRequires: pkgconfig(epoxy) BuildRequires: pkgconfig(gl) BuildRequires: pkgconfig(libconfig) BuildRequires: pkgconfig(libdrm) ++++++ picom-11.1.tar.gz -> picom-11.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/.builds/freebsd.yml new/picom-11.2/.builds/freebsd.yml --- old/picom-11.1/.builds/freebsd.yml 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/.builds/freebsd.yml 2024-02-13 11:36:02.000000000 +0100 @@ -12,6 +12,7 @@ - uthash - libconfig - libglvnd + - libepoxy - dbus - pcre sources: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/.editorconfig new/picom-11.2/.editorconfig --- old/picom-11.1/.editorconfig 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/.editorconfig 2024-02-13 11:36:02.000000000 +0100 @@ -3,3 +3,6 @@ indent_style = tab indent_size = 8 max_line_length = 90 +[*.nix] +indent_style = space +indent_size = 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/README.md new/picom-11.2/README.md --- old/picom-11.1/README.md 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/README.md 2024-02-13 11:36:02.000000000 +0100 @@ -39,7 +39,7 @@ * pixman * libdbus (optional, disable with the `-Ddbus=false` meson configure flag) * libconfig (optional, disable with the `-Dconfig_file=false` meson configure flag) -* libGL, libEGL (optional, disable with the `-Dopengl=false` meson configure flag) +* libGL, libEGL, libepoxy (optional, disable with the `-Dopengl=false` meson configure flag) * libpcre2 (optional, disable with the `-Dregex=false` meson configure flag) * libev * uthash @@ -47,13 +47,13 @@ On Debian based distributions (e.g. Ubuntu), the needed packages are ``` -libconfig-dev libdbus-1-dev libegl-dev libev-dev libgl-dev libpcre2-dev libpixman-1-dev libx11-xcb-dev libxcb1-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-dpms0-dev libxcb-glx0-dev libxcb-image0-dev libxcb-present-dev libxcb-randr0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev libxcb-util-dev libxcb-xfixes0-dev libxext-dev meson ninja-build uthash-dev +libconfig-dev libdbus-1-dev libegl-dev libev-dev libgl-dev libepoxy-dev libpcre2-dev libpixman-1-dev libx11-xcb-dev libxcb1-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-dpms0-dev libxcb-glx0-dev libxcb-image0-dev libxcb-present-dev libxcb-randr0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev libxcb-util-dev libxcb-xfixes0-dev libxext-dev meson ninja-build uthash-dev ``` On Fedora, the needed packages are ``` -dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel meson pcre2-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel +dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel libepoxy-devel meson pcre2-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel xcb-util-devel ``` To build the documents, you need `asciidoc` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/flake.lock new/picom-11.2/flake.lock --- old/picom-11.1/flake.lock 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/flake.lock 2024-02-13 11:36:02.000000000 +0100 @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1689068808, - "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -25,11 +25,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1703887061, + "narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5", "type": "github" }, "original": { @@ -41,11 +41,12 @@ }, "nixpkgs": { "locked": { - "lastModified": 1691186842, - "narHash": "sha256-wxBVCvZUwq+XS4N4t9NqsHV4E64cPVqQ2fdDISpjcw0=", - "path": "/nix/store/d42v5grfq77vr10r336kks0qjp0wij8d-source", - "rev": "18036c0be90f4e308ae3ebcab0e14aae0336fe42", - "type": "path" + "lastModified": 1707689078, + "narHash": "sha256-UUGmRa84ZJHpGZ1WZEBEUOzaPOWG8LZ0yPg1pdDF/yM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f9d39fb9aff0efee4a3d5f4a6d7c17701d38a1d8", + "type": "github" }, "original": { "id": "nixpkgs", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/flake.nix new/picom-11.2/flake.nix --- old/picom-11.1/flake.nix 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/flake.nix 2024-02-13 11:36:02.000000000 +0100 @@ -24,12 +24,22 @@ overlays = [ overlay ]; in rec { inherit overlay overlays; - defaultPackage = pkgs.picom; + defaultPackage = pkgs.picom.overrideAttrs (o: { + version = "11"; + src = ./.; + buildInputs = o.buildInputs ++ [ pkgs.libepoxy ]; + }); devShell = defaultPackage.overrideAttrs { - buildInputs = defaultPackage.buildInputs ++ [ - pkgs.clang-tools - pkgs.llvmPackages_14.clang-unwrapped.python - ]; + buildInputs = defaultPackage.buildInputs ++ (with pkgs; [ + clang-tools_17 + llvmPackages_17.clang-unwrapped.python + ]); + hardeningDisable = [ "fortify" ]; + shellHook = '' + # Workaround a NixOS limitation on sanitizers: + # See: https://github.com/NixOS/nixpkgs/issues/287763 + export LD_LIBRARY_PATH+=":/run/opengl-driver/lib" + ''; }; }); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/backend/driver.c new/picom-11.2/src/backend/driver.c --- old/picom-11.1/src/backend/driver.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/backend/driver.c 2024-02-13 11:36:02.000000000 +0100 @@ -20,10 +20,13 @@ } enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver) { + enum vblank_scheduler_type type = VBLANK_SCHEDULER_PRESENT; +#ifdef CONFIG_OPENGL if (driver & DRIVER_NVIDIA) { - return VBLANK_SCHEDULER_SGI_VIDEO_SYNC; + type = VBLANK_SCHEDULER_SGI_VIDEO_SYNC; } - return VBLANK_SCHEDULER_PRESENT; +#endif + return type; } enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/backend/gl/egl.c new/picom-11.2/src/backend/gl/egl.c --- old/picom-11.1/src/backend/gl/egl.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/backend/gl/egl.c 2024-02-13 11:36:02.000000000 +0100 @@ -36,12 +36,6 @@ EGLContext ctx; }; -static PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glEGLImageTargetTexStorage = NULL; -static PFNEGLCREATEIMAGEKHRPROC eglCreateImageProc = NULL; -static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageProc = NULL; -static PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplayProc = NULL; -static PFNEGLCREATEPLATFORMWINDOWSURFACEPROC eglCreatePlatformWindowSurfaceProc = NULL; - const char *eglGetErrorString(EGLint error) { #define CASE_STR(value) \ case value: return #value; @@ -74,7 +68,7 @@ struct egl_pixmap *p = tex->user_data; // Release binding if (p->image != EGL_NO_IMAGE) { - eglDestroyImageProc(gd->display, p->image); + eglDestroyImage(gd->display, p->image); p->image = EGL_NO_IMAGE; } @@ -134,18 +128,6 @@ bool success = false; struct egl_data *gd = NULL; -#define get_proc(name, type) \ - name##Proc = (type)eglGetProcAddress(#name); \ - if (!name##Proc) { \ - log_error("Failed to get EGL function " #name); \ - goto end; \ - } - get_proc(eglCreateImage, PFNEGLCREATEIMAGEKHRPROC); - get_proc(eglDestroyImage, PFNEGLDESTROYIMAGEKHRPROC); - get_proc(eglGetPlatformDisplay, PFNEGLGETPLATFORMDISPLAYPROC); - get_proc(eglCreatePlatformWindowSurface, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC); -#undef get_proc - // Check if we have the X11 platform const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (strstr(exts, "EGL_EXT_platform_x11") == NULL) { @@ -154,12 +136,12 @@ } gd = ccalloc(1, struct egl_data); - gd->display = eglGetPlatformDisplayProc(EGL_PLATFORM_X11_EXT, ps->c.dpy, - (EGLAttrib[]){ - EGL_PLATFORM_X11_SCREEN_EXT, - ps->c.screen, - EGL_NONE, - }); + gd->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, ps->c.dpy, + (EGLint[]){ + EGL_PLATFORM_X11_SCREEN_EXT, + ps->c.screen, + EGL_NONE, + }); if (gd->display == EGL_NO_DISPLAY) { log_error("Failed to get EGL display."); goto end; @@ -212,7 +194,7 @@ // clang-format on gd->target_win = - eglCreatePlatformWindowSurfaceProc(gd->display, config, &target, NULL); + eglCreatePlatformWindowSurfaceEXT(gd->display, config, &target, NULL); if (gd->target_win == EGL_NO_SURFACE) { log_error("Failed to create EGL surface."); goto end; @@ -243,14 +225,6 @@ goto end; } - glEGLImageTargetTexStorage = - (PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC)eglGetProcAddress("glEGLImageTargetTexS" - "torageEXT"); - if (glEGLImageTargetTexStorage == NULL) { - log_error("Failed to get glEGLImageTargetTexStorageEXT."); - goto end; - } - gd->gl.decouple_texture_user_data = egl_decouple_user_data; gd->gl.release_user_data = egl_release_image; @@ -302,9 +276,8 @@ eglpixmap = cmalloc(struct egl_pixmap); eglpixmap->pixmap = pixmap; - eglpixmap->image = - eglCreateImageProc(gd->display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, - (EGLClientBuffer)(uintptr_t)pixmap, NULL); + eglpixmap->image = eglCreateImage(gd->display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, + (EGLClientBuffer)(uintptr_t)pixmap, NULL); eglpixmap->owned = owned; if (eglpixmap->image == EGL_NO_IMAGE) { @@ -324,14 +297,14 @@ wd->dim = 0; wd->inner->refcount = 1; glBindTexture(GL_TEXTURE_2D, inner->texture); - glEGLImageTargetTexStorage(GL_TEXTURE_2D, eglpixmap->image, NULL); + glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, eglpixmap->image, NULL); glBindTexture(GL_TEXTURE_2D, 0); gl_check_err(); return wd; err: if (eglpixmap && eglpixmap->image) { - eglDestroyImageProc(gd->display, eglpixmap->image); + eglDestroyImage(gd->display, eglpixmap->image); } free(eglpixmap); @@ -422,41 +395,6 @@ .max_buffer_age = 5, // Why? }; -PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName; -/** - * Check if a EGL extension exists. - */ -static inline bool egl_has_extension(EGLDisplay dpy, const char *ext) { - const char *egl_exts = eglQueryString(dpy, EGL_EXTENSIONS); - if (!egl_exts) { - log_error("Failed get EGL extension list."); - return false; - } - - auto inlen = strlen(ext); - const char *curr = egl_exts; - bool match = false; - while (curr && !match) { - const char *end = strchr(curr, ' '); - if (!end) { - // Last extension string - match = strcmp(ext, curr) == 0; - } else if (curr + inlen == end) { - // Length match, do match string - match = strncmp(ext, curr, (unsigned long)(end - curr)) == 0; - } - curr = end ? end + 1 : NULL; - } - - if (!match) { - log_info("Missing EGL extension %s.", ext); - } else { - log_info("Found EGL extension %s.", ext); - } - - return match; -} - struct eglext_info eglext = {0}; void eglext_init(EGLDisplay dpy) { @@ -464,7 +402,10 @@ return; } eglext.initialized = true; -#define check_ext(name) eglext.has_##name = egl_has_extension(dpy, #name) +#define check_ext(name) \ + eglext.has_##name = epoxy_has_egl_extension(dpy, #name); \ + log_info("Extension " #name " - %s", eglext.has_##name ? "present" : "absent") + check_ext(EGL_EXT_buffer_age); check_ext(EGL_EXT_create_context_robustness); check_ext(EGL_KHR_image_pixmap); @@ -472,16 +413,4 @@ check_ext(EGL_MESA_query_driver); #endif #undef check_ext - - // Checking if the returned function pointer is NULL is not really necessary, - // or maybe not even useful, since eglGetProcAddress might always return - // something. We are doing it just for completeness' sake. - -#ifdef EGL_MESA_query_driver - eglGetDisplayDriverName = - (PFNEGLGETDISPLAYDRIVERNAMEPROC)eglGetProcAddress("eglGetDisplayDriverName"); - if (!eglGetDisplayDriverName) { - eglext.has_EGL_MESA_query_driver = false; - } -#endif } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/backend/gl/egl.h new/picom-11.2/src/backend/gl/egl.h --- old/picom-11.1/src/backend/gl/egl.h 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/backend/gl/egl.h 2024-02-13 11:36:02.000000000 +0100 @@ -1,10 +1,8 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui <yshu...@gmail.com> #pragma once -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GL/gl.h> -#include <GL/glext.h> +#include <epoxy/egl.h> +#include <epoxy/gl.h> #include <stdbool.h> #include <xcb/render.h> #include <xcb/xcb.h> @@ -24,8 +22,4 @@ extern struct eglext_info eglext; -#ifdef EGL_MESA_query_driver -extern PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName; -#endif - void eglext_init(EGLDisplay); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/backend/gl/gl_common.c new/picom-11.2/src/backend/gl/gl_common.c --- old/picom-11.1/src/backend/gl/gl_common.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/backend/gl/gl_common.c 2024-02-13 11:36:02.000000000 +0100 @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui <yshu...@gmail.com> -#include <GL/gl.h> -#include <GL/glext.h> +#include <epoxy/gl.h> #include <stdbool.h> #include <stdio.h> #include <string.h> @@ -961,8 +960,8 @@ } else { gd->is_nvidia = false; } - gd->has_robustness = gl_has_extension("GL_ARB_robustness"); - gd->has_egl_image_storage = gl_has_extension("GL_EXT_EGL_image_storage"); + gd->has_robustness = epoxy_has_gl_extension("GL_ARB_robustness"); + gd->has_egl_image_storage = epoxy_has_gl_extension("GL_EXT_EGL_image_storage"); gl_check_err(); return true; @@ -1013,9 +1012,11 @@ auto new_tex = ccalloc(1, struct gl_texture); new_tex->texture = gl_new_texture(GL_TEXTURE_2D); - new_tex->y_inverted = true; + new_tex->y_inverted = inner->y_inverted; + new_tex->has_alpha = inner->has_alpha; new_tex->height = inner->height; new_tex->width = inner->width; + new_tex->shader = inner->shader; new_tex->refcount = 1; new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/backend/gl/gl_common.h new/picom-11.2/src/backend/gl/gl_common.h --- old/picom-11.1/src/backend/gl/gl_common.h 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/backend/gl/gl_common.h 2024-02-13 11:36:02.000000000 +0100 @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui <yshu...@gmail.com> #pragma once -#include <GL/gl.h> -#include <GL/glext.h> +#include <epoxy/gl.h> #include <stdbool.h> #include <string.h> @@ -265,26 +264,6 @@ #define gl_check_fb_complete(fb) gl_check_fb_complete_(__func__, __LINE__, (fb)) -/** - * Check if a GL extension exists. - */ -static inline bool gl_has_extension(const char *ext) { - int nexts = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &nexts); - for (int i = 0; i < nexts || !nexts; i++) { - const char *exti = (const char *)glGetStringi(GL_EXTENSIONS, (GLuint)i); - if (exti == NULL) { - break; - } - if (strcmp(ext, exti) == 0) { - return true; - } - } - gl_clear_err(); - log_info("Missing GL extension %s.", ext); - return false; -} - static const GLuint vert_coord_loc = 0; static const GLuint vert_in_texcoord_loc = 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/backend/gl/glx.c new/picom-11.2/src/backend/gl/glx.c --- old/picom-11.1/src/backend/gl/glx.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/backend/gl/glx.c 2024-02-13 11:36:02.000000000 +0100 @@ -545,62 +545,17 @@ .max_buffer_age = 5, // Why? }; -/** - * Check if a GLX extension exists. - */ -static inline bool glx_has_extension(Display *dpy, int screen, const char *ext) { - const char *glx_exts = glXQueryExtensionsString(dpy, screen); - if (!glx_exts) { - log_error("Failed get GLX extension list."); - return false; - } - - auto inlen = strlen(ext); - const char *curr = glx_exts; - bool match = false; - while (curr && !match) { - const char *end = strchr(curr, ' '); - if (!end) { - // Last extension string - match = strcmp(ext, curr) == 0; - } else if (curr + inlen == end) { - // Length match, do match string - match = strncmp(ext, curr, (unsigned long)(end - curr)) == 0; - } - curr = end ? end + 1 : NULL; - } - - if (!match) { - log_info("Missing GLX extension %s.", ext); - } else { - log_info("Found GLX extension %s.", ext); - } - - return match; -} - struct glxext_info glxext = {0}; -PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI; -PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI; -PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML; -PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML; -PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; -PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; -PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA; -PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT; -PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT; -PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB; - -#ifdef GLX_MESA_query_renderer -PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA; -#endif void glxext_init(Display *dpy, int screen) { if (glxext.initialized) { return; } glxext.initialized = true; -#define check_ext(name) glxext.has_##name = glx_has_extension(dpy, screen, #name) +#define check_ext(name) \ + glxext.has_##name = epoxy_has_glx_extension(dpy, screen, #name); \ + log_info("Extension " #name " - %s", glxext.has_##name ? "present" : "absent") + check_ext(GLX_SGI_video_sync); check_ext(GLX_SGI_swap_control); check_ext(GLX_OML_sync_control); @@ -614,36 +569,4 @@ check_ext(GLX_MESA_query_renderer); #endif #undef check_ext - -#define lookup(name) ((name) = (__typeof__(name))glXGetProcAddress((GLubyte *)#name)) - // Checking if the returned function pointer is NULL is not really necessary, - // or maybe not even useful, since glXGetProcAddress might always return - // something. We are doing it just for completeness' sake. - if (!lookup(glXGetVideoSyncSGI) || !lookup(glXWaitVideoSyncSGI)) { - glxext.has_GLX_SGI_video_sync = false; - } - if (!lookup(glXSwapIntervalEXT)) { - glxext.has_GLX_EXT_swap_control = false; - } - if (!lookup(glXSwapIntervalMESA)) { - glxext.has_GLX_MESA_swap_control = false; - } - if (!lookup(glXSwapIntervalSGI)) { - glxext.has_GLX_SGI_swap_control = false; - } - if (!lookup(glXWaitForMscOML) || !lookup(glXGetSyncValuesOML)) { - glxext.has_GLX_OML_sync_control = false; - } - if (!lookup(glXBindTexImageEXT) || !lookup(glXReleaseTexImageEXT)) { - glxext.has_GLX_EXT_texture_from_pixmap = false; - } - if (!lookup(glXCreateContextAttribsARB)) { - glxext.has_GLX_ARB_create_context = false; - } -#ifdef GLX_MESA_query_renderer - if (!lookup(glXQueryCurrentRendererIntegerMESA)) { - glxext.has_GLX_MESA_query_renderer = false; - } -#endif -#undef lookup } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/backend/gl/glx.h new/picom-11.2/src/backend/gl/glx.h --- old/picom-11.1/src/backend/gl/glx.h 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/backend/gl/glx.h 2024-02-13 11:36:02.000000000 +0100 @@ -1,17 +1,9 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui <yshu...@gmail.com> #pragma once -#include <stdbool.h> -// Older version of glx.h defines function prototypes for these extensions... -// Rename them to avoid conflicts -#define glXSwapIntervalMESA glXSwapIntervalMESA_ -#define glXBindTexImageEXT glXBindTexImageEXT_ -#define glXReleaseTexImageEXT glXReleaseTexImageEXT -#include <GL/glx.h> -#undef glXSwapIntervalMESA -#undef glXBindTexImageEXT -#undef glXReleaseTexImageEXT #include <X11/Xlib.h> +#include <epoxy/glx.h> +#include <stdbool.h> #include <xcb/render.h> #include <xcb/xcb.h> @@ -59,19 +51,4 @@ extern struct glxext_info glxext; -extern PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI; -extern PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI; -extern PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML; -extern PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML; -extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; -extern PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; -extern PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA; -extern PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT; -extern PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT; -extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB; - -#ifdef GLX_MESA_query_renderer -extern PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA; -#endif - void glxext_init(Display *, int screen); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/event.c new/picom-11.2/src/event.c --- old/picom-11.1/src/event.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/event.c 2024-02-13 11:36:02.000000000 +0100 @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2019, Yuxuan Shui <yshu...@gmail.com> +#include <stdint.h> #include <stdio.h> #include <X11/Xlibint.h> @@ -47,8 +48,14 @@ /// When top half finished, we enter the render stage, where no server state should be /// queried. All rendering should be done with our internal knowledge of the server state. /// - -// TODO(yshui) the things described above +/// P.S. There is another reason to avoid sending any request to the server as much as +/// possible. To make sure requests are sent, flushes are needed. And `xcb_flush`/`XFlush` +/// functions may read more events from the server into their queues. This is +/// undesirable, see the comments on `handle_queued_x_events` in picom.c for more details. + +// TODO(yshui) the things described above. This is mostly done, maybe some of +// the functions here is still making unnecessary queries, we need +// to do some auditing to be sure. /** * Get a window's name from window ID. @@ -350,19 +357,14 @@ } // Reset event mask in case something wrong happens - xcb_change_window_attributes( - ps->c.c, ev->window, XCB_CW_EVENT_MASK, - (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)}); + uint32_t evmask = determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN); if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { log_debug("Window %#010x doesn't have WM_STATE property, it is " "probably not a client window. But we will listen for " "property change in case it gains one.", ev->window); - xcb_change_window_attributes( - ps->c.c, ev->window, XCB_CW_EVENT_MASK, - (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) | - XCB_EVENT_MASK_PROPERTY_CHANGE}); + evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE; } else { auto w_real_top = find_managed_window_or_parent(ps, ev->parent); if (w_real_top && w_real_top->state != WSTATE_UNMAPPED && @@ -386,6 +388,8 @@ } } } + XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window, + XCB_CW_EVENT_MASK, (const uint32_t[]){evmask}); } } @@ -475,9 +479,10 @@ // Check whether it could be a client window if (!find_toplevel(ps, ev->window)) { // Reset event mask anyway - xcb_change_window_attributes(ps->c.c, ev->window, XCB_CW_EVENT_MASK, - (const uint32_t[]){determine_evmask( - ps, ev->window, WIN_EVMODE_UNKNOWN)}); + const uint32_t evmask = + determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN); + XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window, + XCB_CW_EVENT_MASK, (const uint32_t[]){evmask}); auto w_top = find_managed_window_or_parent(ps, ev->window); // ev->window might have not been managed yet, in that case w_top @@ -492,8 +497,8 @@ // If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but // there are always some stupid applications. (#144) if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) { - struct managed_win *w = NULL; - if ((w = find_toplevel(ps, ev->window))) { + struct managed_win *w = find_toplevel(ps, ev->window); + if (w) { win_set_property_stale(w, ev->atom); } } @@ -586,16 +591,28 @@ region_t parts; pixman_region32_init(&parts); + // If this is the first time this window is damaged, we would redraw the + // whole window, so we don't need to fetch the damage region. But we still need + // to make sure the X server receives the DamageSubtract request, hence the + // `xcb_request_check` here. + // Otherwise, we fetch the damage regions. That means we will receive a reply + // from the X server, which implies it has received our DamageSubtract request. if (!w->ever_damaged) { - win_extents(w, &parts); - if (!ps->o.show_all_xerrors) { - set_ignore_cookie(&ps->c, xcb_damage_subtract(ps->c.c, w->damage, - XCB_NONE, XCB_NONE)); + auto e = xcb_request_check( + ps->c.c, xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE, XCB_NONE)); + if (e) { + if (ps->o.show_all_xerrors) { + x_print_error(e->sequence, e->major_code, e->minor_code, + e->error_code); + } + free(e); } + win_extents(w, &parts); } else { + auto cookie = + xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE, ps->damaged_region); if (!ps->o.show_all_xerrors) { - set_ignore_cookie(&ps->c, xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE, - ps->damaged_region)); + set_ignore_cookie(&ps->c, cookie); } x_fetch_region(&ps->c, ps->damaged_region, &parts); pixman_region32_translate(&parts, w->g.x + w->g.border_width, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/log.c new/picom-11.2/src/log.c --- old/picom-11.1/src/log.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/log.c 2024-02-13 11:36:02.000000000 +0100 @@ -9,7 +9,7 @@ #include <unistd.h> #ifdef CONFIG_OPENGL -#include <GL/gl.h> +#include <epoxy/gl.h> #include "backend/gl/gl_common.h" #include "backend/gl/glx.h" #endif @@ -338,21 +338,14 @@ } #ifdef CONFIG_OPENGL -/// An opengl logger that can be used for logging into opengl debugging tools, -/// such as apitrace -struct gl_string_marker_logger { - struct log_target tgt; - PFNGLSTRINGMARKERGREMEDYPROC gl_string_marker; -}; -static void -gl_string_marker_logger_write(struct log_target *tgt, const char *str, size_t len) { - auto g = (struct gl_string_marker_logger *)tgt; +static void gl_string_marker_logger_write(struct log_target *tgt attr_unused, + const char *str, size_t len) { // strip newlines at the end of the string while (len > 0 && str[len - 1] == '\n') { len--; } - g->gl_string_marker((GLsizei)len, str); + glStringMarkerGREMEDY((GLsizei)len, str); } static const struct log_ops gl_string_marker_logger_ops = { @@ -361,20 +354,16 @@ .destroy = logger_trivial_destroy, }; +/// Create an opengl logger that can be used for logging into opengl debugging tools, +/// such as apitrace struct log_target *gl_string_marker_logger_new(void) { - if (!gl_has_extension("GL_GREMEDY_string_marker")) { + if (!epoxy_has_gl_extension("GL_GREMEDY_string_marker")) { return NULL; } - void *fnptr = glXGetProcAddress((GLubyte *)"glStringMarkerGREMEDY"); - if (!fnptr) { - return NULL; - } - - auto ret = cmalloc(struct gl_string_marker_logger); - ret->tgt.ops = &gl_string_marker_logger_ops; - ret->gl_string_marker = fnptr; - return &ret->tgt; + auto ret = cmalloc(struct log_target); + ret->ops = &gl_string_marker_logger_ops; + return ret; } #else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/meson.build new/picom-11.2/src/meson.build --- old/picom-11.1/src/meson.build 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/meson.build 2024-02-13 11:36:02.000000000 +0100 @@ -58,8 +58,8 @@ endif if get_option('opengl') - cflags += ['-DCONFIG_OPENGL', '-DGL_GLEXT_PROTOTYPES'] - deps += [dependency('gl', required: true), dependency('egl', required: true), dependency('threads', required:true)] + cflags += ['-DCONFIG_OPENGL'] + deps += [dependency('epoxy', required: true), dependency('threads', required:true)] srcs += [ 'opengl.c' ] endif @@ -86,6 +86,10 @@ cflags += ['-DHAS_KQUEUE'] endif +if host_system == 'openbsd' + deps += [dependency('threads', required: true)] +endif + subdir('backend') picom = executable('picom', srcs, c_args: cflags, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/opengl.c new/picom-11.2/src/opengl.c --- old/picom-11.1/src/opengl.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/opengl.c 2024-02-13 11:36:02.000000000 +0100 @@ -181,7 +181,7 @@ // must precede FBConfig fetching if (need_render) { psglx->has_texture_non_power_of_two = - gl_has_extension("GL_ARB_texture_non_power_of_two"); + epoxy_has_gl_extension("GL_ARB_texture_non_power_of_two"); } // Render preparations diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/opengl.h new/picom-11.2/src/opengl.h --- old/picom-11.1/src/opengl.h 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/opengl.h 2024-02-13 11:36:02.000000000 +0100 @@ -18,9 +18,9 @@ #include "render.h" #include "win.h" -#include <GL/gl.h> -#include <GL/glx.h> #include <ctype.h> +#include <epoxy/gl.h> +#include <epoxy/glx.h> #include <locale.h> #include <stdlib.h> #include <string.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/picom.c new/picom-11.2/src/picom.c --- old/picom-11.1/src/picom.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/picom.c 2024-02-13 11:36:02.000000000 +0100 @@ -33,6 +33,9 @@ #include <xcb/render.h> #include <xcb/sync.h> #include <xcb/xfixes.h> +#ifdef __OpenBSD__ +#include <pthread.h> +#endif #include <ev.h> #include <test.h> @@ -1158,14 +1161,43 @@ } auto pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms); if (pixmap != XCB_NONE) { + xcb_get_geometry_reply_t *r = xcb_get_geometry_reply( + ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL); + if (!r) { + goto err; + } + + // We used to assume that pixmaps pointed by the root background + // pixmap atoms are owned by the root window and have the same + // depth and hence the same visual that we can use to bind them. + // However, some applications break this assumption, e.g. the + // Xfce's desktop manager xfdesktop that sets the _XROOTPMAP_ID + // atom to a pixmap owned by it that seems to always have 32 bpp + // depth when the common root window's depth is 24 bpp. So use the + // root window's visual only if the root background pixmap's depth + // matches the root window's depth. Otherwise, find a suitable + // visual for the root background pixmap's depth and use it. + // + // We can't obtain a suitable visual for the root background + // pixmap the same way as the win_bind_pixmap function because it + // requires a window and we have only a pixmap. We also can't not + // bind the root background pixmap in case of depth mismatch + // because some options rely on it's content, e.g. + // transparent-clipping. + xcb_visualid_t visual = + r->depth == ps->c.screen_info->root_depth + ? ps->c.screen_info->root_visual + : x_get_visual_for_depth(&ps->c, r->depth); + free(r); + ps->root_image = ps->backend_data->ops->bind_pixmap( - ps->backend_data, pixmap, - x_get_visual_info(&ps->c, ps->c.screen_info->root_visual), false); + ps->backend_data, pixmap, x_get_visual_info(&ps->c, visual), false); if (ps->root_image) { ps->backend_data->ops->set_image_property( ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE, ps->root_image, (int[]){ps->root_width, ps->root_height}); } else { + err: log_error("Failed to bind root back pixmap"); } } @@ -1577,9 +1609,32 @@ log_debug("Screen unredirected."); } -// Handle queued events before we go to sleep +/// Handle queued events before we go to sleep. +/// +/// This function is called by ev_prepare watcher, which is called just before +/// the event loop goes to sleep. X damage events are incremental, which means +/// if we don't handle the ones X server already sent us, we won't get new ones. +/// And if we don't get new ones, we won't render, i.e. we would freeze. libxcb +/// keeps an internal queue of events, so we have to be 100% sure no events are +/// left in that queue before we go to sleep. static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents attr_unused) { session_t *ps = session_ptr(w, event_check); + // Flush because if we go into sleep when there is still requests in the + // outgoing buffer, they will not be sent for an indefinite amount of + // time. Use XFlush here too, we might still use some Xlib functions + // because OpenGL. + // + // Also note, after we have flushed here, we won't flush again in this + // function before going into sleep. This is because `xcb_flush`/`XFlush` + // may _read_ more events from the server (yes, this is ridiculous, I + // know). And we can't have that, see the comments above this function. + // + // This means if functions called ev_handle need to send some events, + // they need to carefully make sure those events are flushed, one way or + // another. + XFlush(ps->c.dpy); + xcb_flush(ps->c.c); + if (ps->vblank_scheduler) { vblank_handle_x_events(ps->vblank_scheduler); } @@ -1589,13 +1644,6 @@ ev_handle(ps, ev); free(ev); }; - // Flush because if we go into sleep when there is still - // requests in the outgoing buffer, they will not be sent - // for an indefinite amount of time. - // Use XFlush here too, we might still use some Xlib functions - // because OpenGL. - XFlush(ps->c.dpy); - xcb_flush(ps->c.c); int err = xcb_connection_has_error(ps->c.c); if (err) { log_fatal("X11 server connection broke (error %d)", err); @@ -2220,6 +2268,7 @@ c2_list_postprocess(ps, ps->o.window_shader_fg_rules) && c2_list_postprocess(ps, ps->o.opacity_rules) && c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) && + c2_list_postprocess(ps, ps->o.corner_radius_rules) && c2_list_postprocess(ps, ps->o.focus_blacklist))) { log_error("Post-processing of conditionals failed, some of your rules " "might not work"); @@ -2558,14 +2607,26 @@ int ret; struct sched_param param; +#ifndef __OpenBSD__ ret = sched_getparam(0, ¶m); +#else + int old_policy; + ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m); +#endif + if (ret != 0) { log_debug("Failed to get old scheduling priority"); return; } param.sched_priority = priority; + +#ifndef __OpenBSD__ ret = sched_setscheduler(0, SCHED_RR, ¶m); +#else + ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); +#endif + if (ret != 0) { log_info("Failed to set real-time scheduling priority to %d.", priority); return; @@ -2633,6 +2694,7 @@ c2_list_free(&ps->o.paint_blacklist, NULL); c2_list_free(&ps->o.unredir_if_possible_blacklist, NULL); c2_list_free(&ps->o.rounded_corners_blacklist, NULL); + c2_list_free(&ps->o.corner_radius_rules, NULL); c2_list_free(&ps->o.window_shader_fg_rules, free); // Free tracked atom list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/render.c new/picom-11.2/src/render.c --- old/picom-11.1/src/render.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/render.c 2024-02-13 11:36:02.000000000 +0100 @@ -604,20 +604,27 @@ bool fill = false; xcb_pixmap_t pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms); - // Make sure the pixmap we got is valid - if (pixmap && !x_validate_pixmap(&ps->c, pixmap)) { - pixmap = XCB_NONE; + xcb_get_geometry_reply_t *r; + if (pixmap) { + r = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL); } // Create a pixmap if there isn't any - if (!pixmap) { + xcb_visualid_t visual; + if (!pixmap || !r) { pixmap = x_create_pixmap(&ps->c, (uint8_t)ps->c.screen_info->root_depth, 1, 1); if (pixmap == XCB_NONE) { log_error("Failed to create pixmaps for root tile."); return false; } + visual = ps->c.screen_info->root_visual; fill = true; + } else { + visual = r->depth == ps->c.screen_info->root_depth + ? ps->c.screen_info->root_visual + : x_get_visual_for_depth(&ps->c, r->depth); + free(r); } // Create Picture @@ -625,7 +632,7 @@ .repeat = true, }; ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap( - &ps->c, ps->c.screen_info->root_visual, pixmap, XCB_RENDER_CP_REPEAT, &pa); + &ps->c, visual, pixmap, XCB_RENDER_CP_REPEAT, &pa); // Fill pixmap if needed if (fill) { @@ -646,8 +653,7 @@ ps->root_tile_paint.pixmap = pixmap; #ifdef CONFIG_OPENGL if (BKEND_GLX == ps->o.backend) { - return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, - ps->c.screen_info->root_visual, false); + return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, visual, false); } #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/vblank.c new/picom-11.2/src/vblank.c --- old/picom-11.1/src/vblank.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/vblank.c 2024-02-13 11:36:02.000000000 +0100 @@ -11,14 +11,13 @@ #ifdef CONFIG_OPENGL // Enable sgi_video_sync_vblank_scheduler -#include <GL/glx.h> #include <X11/X.h> #include <X11/Xlib-xcb.h> #include <X11/Xlib.h> #include <X11/Xutil.h> +#include <epoxy/glx.h> #include <pthread.h> -#include "backend/gl/glx.h" #endif #include "compiler.h" @@ -63,9 +62,9 @@ struct vblank_scheduler_ops { size_t size; - void (*init)(struct vblank_scheduler *self); + bool (*init)(struct vblank_scheduler *self); void (*deinit)(struct vblank_scheduler *self); - void (*schedule)(struct vblank_scheduler *self); + bool (*schedule)(struct vblank_scheduler *self); bool (*handle_x_events)(struct vblank_scheduler *self); }; @@ -78,13 +77,14 @@ // Since glXWaitVideoSyncSGI blocks, we need to run it in a separate thread. // ... and all the thread shenanigans that come with it. - _Atomic unsigned int last_msc; - _Atomic uint64_t last_ust; + _Atomic unsigned int current_msc; + _Atomic uint64_t current_ust; ev_async notify; pthread_t sync_thread; bool running, error; + unsigned int last_msc; - /// Protects `running`, `error` and `base.vblank_event_requested` + /// Protects `running`, and `base.vblank_event_requested` pthread_mutex_t vblank_requested_mtx; pthread_cond_t vblank_requested_cnd; }; @@ -110,11 +110,6 @@ return false; } - glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)(void *)glXGetProcAddress( - (const GLubyte *)"glXWaitVideoSyncSGI"); - if (!glXWaitVideoSyncSGI) { - return false; - } return true; } @@ -207,8 +202,8 @@ struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - atomic_store(&self->last_msc, last_msc); - atomic_store(&self->last_ust, + atomic_store(&self->current_msc, last_msc); + atomic_store(&self->current_ust, (uint64_t)(now.tv_sec * 1000000 + now.tv_nsec / 1000)); ev_async_send(self->base.loop, &self->notify); pthread_mutex_lock(&self->vblank_requested_mtx); @@ -239,34 +234,30 @@ return NULL; } -static void sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) { +static bool sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) { auto self = (struct sgi_video_sync_vblank_scheduler *)base; - log_verbose("Requesting vblank event for msc %d", self->last_msc + 1); + if (self->error) { + return false; + } + log_verbose("Requesting vblank event for msc %d", self->current_msc + 1); pthread_mutex_lock(&self->vblank_requested_mtx); assert(!base->vblank_event_requested); base->vblank_event_requested = true; pthread_cond_signal(&self->vblank_requested_cnd); pthread_mutex_unlock(&self->vblank_requested_mtx); + return true; } static void -sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) { - auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify); - auto event = (struct vblank_event){ - .msc = atomic_load(&sched->last_msc), - .ust = atomic_load(&sched->last_ust), - }; - sched->base.vblank_event_requested = false; - log_verbose("Received vblank event for msc %lu", event.msc); - vblank_scheduler_invoke_callbacks(&sched->base, &event); -} +sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents); -static void sgi_video_sync_scheduler_init(struct vblank_scheduler *base) { +static bool sgi_video_sync_scheduler_init(struct vblank_scheduler *base) { auto self = (struct sgi_video_sync_vblank_scheduler *)base; auto args = (struct sgi_video_sync_thread_args){ .self = self, .start_status = -1, }; + bool succeeded = true; pthread_mutex_init(&args.start_mtx, NULL); pthread_cond_init(&args.start_cnd, NULL); @@ -286,11 +277,15 @@ if (args.start_status != 0) { log_fatal("Failed to start sgi_video_sync_thread, error code: %d", args.start_status); - abort(); + succeeded = false; + } else { + log_info("Started sgi_video_sync_thread"); } + self->error = !succeeded; + self->last_msc = 0; pthread_mutex_destroy(&args.start_mtx); pthread_cond_destroy(&args.start_cnd); - log_info("Started sgi_video_sync_thread"); + return succeeded; } static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) { @@ -306,15 +301,45 @@ pthread_mutex_destroy(&self->vblank_requested_mtx); pthread_cond_destroy(&self->vblank_requested_cnd); } + +static void +sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) { + auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify); + auto msc = atomic_load(&sched->current_msc); + if (sched->last_msc == msc) { + // NVIDIA spams us with duplicate vblank events after a suspend/resume + // cycle. Recreating the X connection and GLX context seems to fix this. + // Oh NVIDIA. + log_warn("Duplicate vblank event found with msc %d. Possible NVIDIA bug?", msc); + log_warn("Resetting the vblank scheduler"); + sgi_video_sync_scheduler_deinit(&sched->base); + sched->base.vblank_event_requested = false; + if (!sgi_video_sync_scheduler_init(&sched->base)) { + log_error("Failed to reset the vblank scheduler"); + } else { + sgi_video_sync_scheduler_schedule(&sched->base); + } + return; + } + auto event = (struct vblank_event){ + .msc = msc, + .ust = atomic_load(&sched->current_ust), + }; + sched->base.vblank_event_requested = false; + sched->last_msc = msc; + log_verbose("Received vblank event for msc %lu", event.msc); + vblank_scheduler_invoke_callbacks(&sched->base, &event); +} #endif -static void present_vblank_scheduler_schedule(struct vblank_scheduler *base) { +static bool present_vblank_scheduler_schedule(struct vblank_scheduler *base) { auto self = (struct present_vblank_scheduler *)base; log_verbose("Requesting vblank event for window 0x%08x, msc %" PRIu64, base->target_window, self->last_msc + 1); assert(!base->vblank_event_requested); x_request_vblank_event(base->c, base->target_window, self->last_msc + 1); base->vblank_event_requested = true; + return true; } static void present_vblank_callback(EV_P attr_unused, ev_timer *w, int attr_unused revents) { @@ -327,7 +352,7 @@ vblank_scheduler_invoke_callbacks(&sched->base, &event); } -static void present_vblank_scheduler_init(struct vblank_scheduler *base) { +static bool present_vblank_scheduler_init(struct vblank_scheduler *base) { auto self = (struct present_vblank_scheduler *)base; base->type = VBLANK_SCHEDULER_PRESENT; ev_timer_init(&self->callback_timer, present_vblank_callback, 0, 0); @@ -339,6 +364,7 @@ set_cant_fail_cookie(base->c, select_input); self->event = xcb_register_for_special_xge(base->c->c, &xcb_present_id, self->event_id, NULL); + return true; } static void present_vblank_scheduler_deinit(struct vblank_scheduler *base) { @@ -439,17 +465,19 @@ #endif }; -static void vblank_scheduler_schedule_internal(struct vblank_scheduler *self) { +static bool vblank_scheduler_schedule_internal(struct vblank_scheduler *self) { assert(self->type < LAST_VBLANK_SCHEDULER); auto fn = vblank_scheduler_ops[self->type].schedule; assert(fn != NULL); - fn(self); + return fn(self); } bool vblank_scheduler_schedule(struct vblank_scheduler *self, vblank_callback_t vblank_callback, void *user_data) { if (self->callback_count == 0 && self->wind_down == 0) { - vblank_scheduler_schedule_internal(self); + if (!vblank_scheduler_schedule_internal(self)) { + return false; + } } if (self->callback_count == self->callback_capacity) { size_t new_capacity = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/x.c new/picom-11.2/src/x.c --- old/picom-11.1/src/x.c 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/x.c 2024-02-13 11:36:02.000000000 +0100 @@ -321,6 +321,21 @@ return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id); } +xcb_visualid_t x_get_visual_for_depth(struct x_connection *c, uint8_t depth) { + xcb_screen_iterator_t screen_it = xcb_setup_roots_iterator(xcb_get_setup(c->c)); + for (; screen_it.rem; xcb_screen_next(&screen_it)) { + xcb_depth_iterator_t depth_it = + xcb_screen_allowed_depths_iterator(screen_it.data); + for (; depth_it.rem; xcb_depth_next(&depth_it)) { + if (depth_it.data->depth == depth) { + return xcb_depth_visuals_iterator(depth_it.data).data->visual_id; + } + } + } + + return XCB_NONE; +} + xcb_render_pictformat_t x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) { x_get_server_pictfmts(c); @@ -717,27 +732,6 @@ return XCB_NONE; } -/** - * Validate a pixmap. - * - * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there - * are better ways. - */ -bool x_validate_pixmap(struct x_connection *c, xcb_pixmap_t pixmap) { - if (pixmap == XCB_NONE) { - return false; - } - - auto r = xcb_get_geometry_reply(c->c, xcb_get_geometry(c->c, pixmap), NULL); - if (!r) { - return false; - } - - bool ret = r->width && r->height; - free(r); - return ret; -} - /// We don't use the _XSETROOT_ID root window property as a source of the background /// pixmap because it most likely points to a dummy pixmap used to keep the colormap /// associated with the background pixmap alive but we listen for it's changes and update diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picom-11.1/src/x.h new/picom-11.2/src/x.h --- old/picom-11.1/src/x.h 2024-01-28 19:17:19.000000000 +0100 +++ new/picom-11.2/src/x.h 2024-02-13 11:36:02.000000000 +0100 @@ -359,8 +359,6 @@ xcb_pixmap_t x_create_pixmap(struct x_connection *, uint8_t depth, int width, int height); -bool x_validate_pixmap(struct x_connection *, xcb_pixmap_t pxmap); - /** * Free a <code>winprop_t</code>. * @@ -411,6 +409,8 @@ xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standard_t std); +xcb_visualid_t x_get_visual_for_depth(struct x_connection *c, uint8_t depth); + xcb_render_pictformat_t x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std);