Merge remote-tracking branch 'yshui/next' into next

This commit is contained in:
Arda Atci
2024-02-17 01:35:02 +03:00
50 changed files with 901 additions and 898 deletions

View File

@@ -12,6 +12,7 @@ packages:
- uthash
- libconfig
- libglvnd
- libepoxy
- dbus
- pcre
sources:
@@ -19,7 +20,7 @@ sources:
tasks:
- setup: |
cd picom
CPPFLAGS="-I/usr/local/include" meson -Dunittest=true build
CPPFLAGS="-I/usr/local/include" meson setup -Dunittest=true --werror build
- build: |
cd picom
ninja -C build

23
.builds/openbsd.yml Normal file
View File

@@ -0,0 +1,23 @@
image: openbsd/latest
packages:
- libev
- xcb
- meson
- pkgconf
- cmake
- uthash
- libconfig
- dbus
- pcre2
sources:
- https://github.com/yshui/picom
tasks:
- setup: |
cd picom
CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" meson setup -Dunittest=true --werror build
- build: |
cd picom
ninja -C build
- unittest: |
cd picom
ninja -C build test

View File

@@ -29,7 +29,7 @@ commands:
- ".git"
- run:
name: config
command: CC=<< parameters.cc >> meson << parameters.build-config >> -Dunittest=true --werror . build
command: CC=<< parameters.cc >> meson setup << parameters.build-config >> -Dunittest=true --werror . build
- run:
name: build
command: ninja -vC build

View File

@@ -23,5 +23,9 @@ CheckOptions:
value: 255.0;1.0;
- key: readability-function-cognitive-complexity.IgnoreMacros
value: true
- key: readability-function-cognitive-complexity.Threshold
value: 50
- key: readability-function-cognitive-complexity.DescribeBasicIncrements
value: true
- key: bugprone-signed-char-misuse.CharTypdefsToIgnore
value: int8_t

View File

@@ -3,3 +3,6 @@ root = true
indent_style = tab
indent_size = 8
max_line_length = 90
[*.nix]
indent_style = space
indent_size = 2

View File

@@ -18,11 +18,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
@@ -32,7 +32,7 @@ jobs:
# Autobuild
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View File

@@ -6,8 +6,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }}
- uses: yshui/git-clang-format-lint@v1.14
- uses: yshui/git-clang-format-lint@v1.15
with:
base: ${{ github.event.pull_request.base.sha }}

View File

@@ -6,9 +6,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: yshui/git-clang-format-lint@v1.14
- uses: yshui/git-clang-format-lint@v1.15
with:
base: ${{ github.event.ref }}~1

View File

@@ -4,9 +4,24 @@
* Allow `corner-radius-rules` to override `corner-radius = 0`. Previously setting corner radius to 0 globally disables rounded corners. (#1170)
## Notable changes
* Marginally improve performance when resizing/opening/closing windows. (#1190)
# v11.2 (2024-Feb-13)
## Build changes
* `picom` now depends on `libepoxy` for OpenGL symbol management.
## Bug fixes
* Workaround a NVIDIA problem that causes high CPU usage after suspend/resume (#1172, #1168)
* Fix building on OpenBSD (#1189, #1188)
* Fix occasional freezes (#1040, #1145, #1166)
* Fix `corner-radius-rules` not applying sometimes (#1177)
* Fix window shader not having an effect when frame opacity is enabled (#1174)
* Fix binding root pixmap in case of depth mismatch (#984)
# v11.1 (2024-Jan-28)

View File

@@ -34,6 +34,7 @@ hasufell <julian.ospald at googlemail.com>
i-c-u-p
Ignacio Taranto <ignacio.taranto at eclypsium.com>
Istvan Petres
Ivan Malison <ivanmalison at gmail.com>
Jake <jakeroggenbuck2 at gmail.com>
James Cloos <cloos at jhcloos.com>
Jamey Sharp <jamey at minilop.net>
@@ -43,6 +44,7 @@ Javeed Shaikh <syscrash2k at gmail.com>
Jerónimo Navarro <jnavarro at ancasrl.com.ar>
jialeens <jialeadmin at 163.com>
Johnny Pribyl <pribylsnbits at gmail.com>
Jose Maldonado aka Yukiteru <josemald89 at gmail.com>
Keith Packard <keithp at keithp.com>
Kevin Kelley <kelleyk at kelleyk.net>
ktprograms <ktprograms at gmail.com>
@@ -68,6 +70,7 @@ Peter Mattern <matternp at arcor.de>
Phil Blundell <pb at reciva.com>
Que Quotion <quequotion at bugmenot.com>
Rafael Kitover <rkitover at gmail.com>
Reith
Richard Grenville <pyxlcy at gmail.com>
Rytis Karpuska <rytis.karpuska at gmail.com>
Samuel Hand <samuel.d.hand at gmail.com>

View File

@@ -41,7 +41,7 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth
* 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
@@ -49,13 +49,13 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth
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 xcb-util-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`

View File

@@ -7,33 +7,55 @@
};
};
outputs = {
self, flake-utils, nixpkgs, git-ignore-nix, ...
}: flake-utils.lib.eachDefaultSystem (system: let
overlay = self: super: {
picom = super.picom.overrideAttrs (oldAttrs: rec {
pname = "picom";
buildInputs = [
self.pcre2 self.xorg.xcbutil
] ++ self.lib.remove self.xorg.libXinerama (
self.lib.remove self.pcre oldAttrs.buildInputs
);
src = git-ignore-nix.lib.gitignoreSource ./.;
});
};
pkgs = import nixpkgs { inherit system overlays; config.allowBroken = true; };
overlays = [ overlay ];
in rec {
inherit overlay overlays;
defaultPackage = pkgs.picom.overrideAttrs {
version = "11";
src = ./.;
};
devShell = defaultPackage.overrideAttrs {
buildInputs = defaultPackage.buildInputs ++ [
pkgs.clang-tools_17
pkgs.llvmPackages_17.clang-unwrapped.python
];
hardeningDisable = [ "fortify" ];
};
});
self,
flake-utils,
nixpkgs,
git-ignore-nix,
...
}:
flake-utils.lib.eachDefaultSystem (system: let
overlay = self: super: {
picom = super.picom.overrideAttrs (oldAttrs: rec {
version = "11";
pname = "picom";
buildInputs =
[
self.pcre2
self.xorg.xcbutil
self.libepoxy
]
++ self.lib.remove self.xorg.libXinerama (
self.lib.remove self.pcre oldAttrs.buildInputs
);
src = git-ignore-nix.lib.gitignoreSource ./.;
});
};
pkgs = import nixpkgs {
inherit system overlays;
config.allowBroken = true;
};
overlays = [overlay];
in rec {
inherit
overlay
overlays
;
defaultPackage = pkgs.picom;
devShell = defaultPackage.overrideAttrs {
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"
'';
};
});
}

View File

@@ -2,12 +2,20 @@
#include <xcb/xcb.h>
#include "atom.h"
#include "cache.h"
#include "common.h"
#include "utils.h"
#include "compiler.h"
#include "log.h"
#include "utils.h"
static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
xcb_connection_t *c = ud;
struct atom_entry {
struct cache_handle entry;
xcb_atom_t atom;
};
static inline int atom_getter(struct cache *cache attr_unused, const char *atom_name,
struct cache_handle **value, void *user_data) {
xcb_connection_t *c = user_data;
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
c, xcb_intern_atom(c, 0, to_u16_checked(strlen(atom_name)), atom_name), NULL);
@@ -18,9 +26,32 @@ static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
free(reply);
} else {
log_error("Failed to intern atoms");
*err = 1;
return -1;
}
return (void *)(intptr_t)atom;
struct atom_entry *entry = ccalloc(1, struct atom_entry);
entry->atom = atom;
*value = &entry->entry;
return 0;
}
static inline void
atom_entry_free(struct cache *cache attr_unused, struct cache_handle *handle) {
struct atom_entry *entry = cache_entry(handle, struct atom_entry, entry);
free(entry);
}
xcb_atom_t get_atom(struct atom *a, const char *key, xcb_connection_t *c) {
struct cache_handle *entry = NULL;
if (cache_get_or_fetch(&a->c, key, &entry, c, atom_getter) < 0) {
log_error("Failed to get atom %s", key);
return XCB_NONE;
}
return cache_entry(entry, struct atom_entry, entry)->atom;
}
xcb_atom_t get_atom_cached(struct atom *a, const char *key) {
return cache_entry(cache_get(&a->c, key), struct atom_entry, entry)->atom;
}
/**
@@ -28,10 +59,15 @@ static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
*/
struct atom *init_atoms(xcb_connection_t *c) {
auto atoms = ccalloc(1, struct atom);
atoms->c = new_cache((void *)c, atom_getter, NULL);
#define ATOM_GET(x) atoms->a##x = (xcb_atom_t)(intptr_t)cache_get(atoms->c, #x, NULL)
atoms->c = CACHE_INIT;
#define ATOM_GET(x) atoms->a##x = get_atom(atoms, #x, c)
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
#undef ATOM_GET
return atoms;
}
void destroy_atoms(struct atom *a) {
cache_invalidate_all(&a->c, atom_entry_free);
free(a);
}

View File

@@ -1,6 +1,4 @@
#pragma once
#include <stdlib.h>
#include <xcb/xcb.h>
#include "cache.h"
@@ -23,6 +21,7 @@
WM_CLIENT_MACHINE, \
_NET_ACTIVE_WINDOW, \
_COMPTON_SHADOW, \
COMPTON_VERSION, \
_NET_WM_WINDOW_TYPE, \
_XROOTPMAP_ID, \
ESETROOT_PMAP_ID, \
@@ -52,19 +51,18 @@
#define ATOM_DEF(x) xcb_atom_t a##x
struct atom_entry;
struct atom {
struct cache *c;
struct cache c;
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST1);
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST2);
};
struct atom *init_atoms(xcb_connection_t *);
/// Create a new atom object with a xcb connection. `struct atom` does not hold
/// a reference to the connection.
struct atom *init_atoms(xcb_connection_t *c);
static inline xcb_atom_t get_atom(struct atom *a, const char *key) {
return (xcb_atom_t)(intptr_t)cache_get(a->c, key, NULL);
}
xcb_atom_t get_atom(struct atom *a, const char *key, xcb_connection_t *c);
xcb_atom_t get_atom_cached(struct atom *a, const char *key);
static inline void destroy_atoms(struct atom *a) {
cache_free(a->c);
free(a);
}
void destroy_atoms(struct atom *a);

View File

@@ -246,7 +246,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
auto after_damage_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
log_trace("Getting damage took %" PRIu64 " us", after_damage_us - after_sync_fence_us);
if (ps->next_render > 0) {
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %ld",
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %" PRIu64,
labs((long)after_damage_us - (long)ps->next_render),
after_damage_us < ps->next_render ? "early" : "late",
after_damage_us, ps->next_render);
@@ -527,7 +527,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
&reg_paint_in_bound, &reg_visible, true);
} else {
if (is_animating && w->old_win_image) {
bool is_focused = win_is_focused_raw(ps, w);
bool is_focused = win_is_focused_raw(w);
if (!is_focused && w->focused && w->opacity_is_set)
is_focused = true;
assert(w->old_win_image);

View File

@@ -115,6 +115,10 @@ struct dual_kawase_blur_args {
int strength;
};
typedef struct {
// Intentionally left blank
} *image_handle;
struct backend_operations {
// =========== Initialization ===========
@@ -167,31 +171,27 @@ struct backend_operations {
* Paint the content of an image onto the rendering buffer.
*
* @param backend_data the backend data
* @param image_data the image to paint
* @param image the image to paint, cannot be NULL
* @param dst_x, dst_y the top left corner of the image in the target
* @param mask the mask image, the top left of the mask is aligned with
* the top left of the image
* the top left of the image. Optional, can be
* NULL.
* @param reg_paint the clip region, in target coordinates
* @param reg_visible the visible region, in target coordinates
*/
void (*compose)(backend_t *backend_data, void *image_data, coord_t image_dst,
void *mask, coord_t mask_dst, const region_t *reg_paint,
const region_t *reg_visible, bool lerp);
void (*_compose)(backend_t *backend_data, void *image_data,
int dst_x1, int dst_y1, int dst_x2, int dst_y2,
const region_t *reg_paint, const region_t *reg_visible);
void (*compose)(backend_t *backend_data, image_handle image, coord_t image_dst,
image_handle mask, coord_t mask_dst, const region_t *reg_paint,
const region_t *reg_visible, bool lerp) attr_nonnull(1, 2, 6, 7);
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
/// Blur a given region of the rendering buffer.
///
/// The blur is limited by `mask`. `mask_dst` specifies the top left corner of the
/// mask is.
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx, void *mask,
coord_t mask_dst, const region_t *reg_blur,
/// The blur can be limited by `mask`. `mask_dst` specifies the top left corner of
/// the mask. `mask` can be NULL.
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx,
image_handle mask, coord_t mask_dst, const region_t *reg_blur,
const region_t *reg_visible) attr_nonnull(1, 3, 6, 7);
/// Update part of the back buffer with the rendering buffer, then present the
@@ -207,13 +207,15 @@ struct backend_operations {
* Bind a X pixmap to the backend's internal image data structure.
*
* @param backend_data backend data
* @param pixmap X pixmap to bind
* @param fmt information of the pixmap's visual
* @param owned whether the ownership of the pixmap is transferred to the backend
* @return backend internal data structure bound with this pixmap
* @param pixmap X pixmap to bind
* @param fmt information of the pixmap's visual
* @param owned whether the ownership of the pixmap is transferred to the
* backend.
* @return backend specific image handle for the pixmap. May be
* NULL.
*/
void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned);
image_handle (*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned);
/// Create a shadow context for rendering shadows with radius `radius`.
/// Default implementation: default_create_shadow_context
@@ -229,17 +231,23 @@ struct backend_operations {
/// shadow context is created.
/// Default implementation: default_render_shadow
///
/// @return the shadow image, may be NULL.
///
/// Required.
void *(*render_shadow)(backend_t *backend_data, int width, int height,
struct backend_shadow_context *ctx, struct color color);
image_handle (*render_shadow)(backend_t *backend_data, int width, int height,
struct backend_shadow_context *ctx, struct color color);
/// Create a shadow by blurring a mask. `size` is the size of the blur. The
/// backend can use whichever blur method is the fastest. The shadow produced
/// shoule be consistent with `render_shadow`.
///
/// @param mask the input mask, must not be NULL.
/// @return the shadow image, may be NULL.
///
/// Optional.
void *(*shadow_from_mask)(backend_t *backend_data, void *mask,
struct backend_shadow_context *ctx, struct color color);
image_handle (*shadow_from_mask)(backend_t *backend_data, image_handle mask,
struct backend_shadow_context *ctx,
struct color color);
/// Create a mask image from region `reg`. This region can be used to create
/// shadow, or used as a mask for composing. When used as a mask, it should mask
@@ -250,13 +258,18 @@ struct backend_operations {
/// and outside of the mask. Corner radius should exclude the corners from the
/// mask. Corner radius should be applied before the inversion.
///
/// @return the mask image, may be NULL.
///
/// Required.
void *(*make_mask)(backend_t *backend_data, geometry_t size, const region_t *reg);
image_handle (*make_mask)(backend_t *backend_data, geometry_t size,
const region_t *reg);
// ============ Resource management ===========
/// Free resources associated with an image data structure
void (*release_image)(backend_t *backend_data, void *img_data) attr_nonnull(1, 2);
///
/// @param image the image to be released, cannot be NULL.
void (*release_image)(backend_t *backend_data, image_handle image) attr_nonnull(1, 2);
/// Create a shader object from a shader source.
///
@@ -281,7 +294,9 @@ struct backend_operations {
/// This function is needed because some backend might change the content of the
/// window (e.g. when using a custom shader with the glx backend), so only the
/// backend knows if an image is transparent.
bool (*is_image_transparent)(backend_t *backend_data, void *image_data)
///
/// @param image the image to be checked, must not be NULL.
bool (*is_image_transparent)(backend_t *backend_data, image_handle image)
attr_nonnull(1, 2);
/// Get the age of the buffer content we are currently rendering on top
@@ -316,35 +331,39 @@ struct backend_operations {
*
* @param backend_data backend data
* @param prop the property to change
* @param image_data an image data structure returned by the backend
* @param image an image handle, cannot be NULL.
* @param args property value
* @return whether the operation is successful
* @return whether the operation is successful
*/
bool (*set_image_property)(backend_t *backend_data, enum image_properties prop,
void *image_data, void *args);
image_handle image, void *args) attr_nonnull(1, 3);
/**
* Manipulate an image. Image properties are untouched.
*
* @param backend_data backend data
* @param op the operation to perform
* @param image_data an image data structure returned by the backend
* @param image an image handle, cannot be NULL.
* @param reg_op the clip region, define the part of the image to be
* operated on.
* @param reg_visible define the part of the image that will eventually
* be visible on target. this is a hint to the backend
* for optimization purposes.
* @param args extra arguments, operation specific
* @return whether the operation is successful
* @return whether the operation is successful
*/
bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
const region_t *reg_op, const region_t *reg_visible, void *args);
bool (*image_op)(backend_t *backend_data, enum image_operations op,
image_handle image, const region_t *reg_op,
const region_t *reg_visible, void *args) attr_nonnull(1, 3, 4, 5);
/// Create another instance of the `image_data`. All `image_op` and
/// `set_image_property` calls on the returned image should not affect the
/// original image
void *(*clone_image)(backend_t *base, const void *image_data,
const region_t *reg_visible);
/// Create another instance of the `image`. The newly created image
/// inherits its content and all image properties from the image being
/// cloned. All `image_op` and `set_image_property` calls on the
/// returned image should not affect the original image.
///
/// @param image the image to be cloned, must not be NULL.
image_handle (*clone_image)(backend_t *base, image_handle image,
const region_t *reg_visible) attr_nonnull_all;
/// Create a blur context that can be used to call `blur`
void *(*create_blur_context)(backend_t *base, enum blur_method, void *args);

View File

@@ -293,8 +293,8 @@ shadow_picture_err:
return false;
}
void *default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color) {
image_handle default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color) {
const conv *kernel = (void *)sctx;
xcb_render_picture_t shadow_pixel =
solid_picture(backend_data->c, true, 1, color.red, color.green, color.blue);
@@ -308,7 +308,7 @@ void *default_render_shadow(backend_t *backend_data, int width, int height,
}
auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32);
void *ret = backend_data->ops->bind_pixmap(
auto ret = backend_data->ops->bind_pixmap(
backend_data, shadow, x_get_visual_info(backend_data->c, visual), true);
x_free_picture(backend_data->c, pict);
x_free_picture(backend_data->c, shadow_pixel);
@@ -316,16 +316,16 @@ void *default_render_shadow(backend_t *backend_data, int width, int height,
}
/// Implement render_shadow with shadow_from_mask
void *
image_handle
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color) {
region_t reg;
pixman_region32_init_rect(&reg, 0, 0, (unsigned int)width, (unsigned int)height);
void *mask = backend_data->ops->make_mask(
auto mask = backend_data->ops->make_mask(
backend_data, (geometry_t){.width = width, .height = height}, &reg);
pixman_region32_fini(&reg);
void *shadow = backend_data->ops->shadow_from_mask(backend_data, mask, sctx, color);
auto shadow = backend_data->ops->shadow_from_mask(backend_data, mask, sctx, color);
backend_data->ops->release_image(backend_data, mask);
return shadow;
}
@@ -458,17 +458,17 @@ struct dual_kawase_params *generate_dual_kawase_params(void *args) {
return params;
}
void *default_clone_image(backend_t *base attr_unused, const void *image_data,
const region_t *reg_visible attr_unused) {
image_handle default_clone_image(backend_t *base attr_unused, image_handle image,
const region_t *reg_visible attr_unused) {
auto new_img = ccalloc(1, struct backend_image);
*new_img = *(struct backend_image *)image_data;
*new_img = *(struct backend_image *)image;
new_img->inner->refcount++;
return new_img;
return (image_handle)new_img;
}
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
void *image_data, void *arg) {
struct backend_image *tex = image_data;
image_handle image, void *arg) {
auto tex = (struct backend_image *)image;
int *iargs = arg;
bool *bargs = arg;
double *dargs = arg;
@@ -490,8 +490,8 @@ bool default_set_image_property(backend_t *base attr_unused, enum image_properti
return true;
}
bool default_is_image_transparent(backend_t *base attr_unused, void *image_data) {
struct backend_image *img = image_data;
bool default_is_image_transparent(backend_t *base attr_unused, image_handle image) {
auto img = (struct backend_image *)image;
return img->opacity < 1 || img->inner->has_alpha;
}

View File

@@ -54,11 +54,11 @@ solid_picture(struct x_connection *, bool argb, double a, double r, double g, do
xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opacity,
int width, int height);
void *default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color);
image_handle default_render_shadow(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color);
/// Implement `render_shadow` with `shadow_from_mask`.
void *
image_handle
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
struct backend_shadow_context *sctx, struct color color);
struct backend_shadow_context *
@@ -72,8 +72,8 @@ void init_backend_base(struct backend_base *base, session_t *ps);
struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count);
struct dual_kawase_params *generate_dual_kawase_params(void *args);
void *default_clone_image(backend_t *base, const void *image_data, const region_t *reg);
bool default_is_image_transparent(backend_t *base attr_unused, void *image_data);
image_handle default_clone_image(backend_t *base, image_handle image, const region_t *reg);
bool default_is_image_transparent(backend_t *base attr_unused, image_handle image);
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
void *image_data, void *arg);
image_handle image, void *arg);
struct backend_image *default_new_backend_image(int w, int h);

View File

@@ -50,8 +50,9 @@ void dummy_deinit(struct backend_base *data) {
free(dummy);
}
static void dummy_check_image(struct backend_base *base, const struct dummy_image *img) {
static void dummy_check_image(struct backend_base *base, image_handle image) {
auto dummy = (struct dummy_data *)base;
auto img = (struct dummy_image *)image;
if (img == (struct dummy_image *)&dummy->mask) {
return;
}
@@ -64,13 +65,13 @@ static void dummy_check_image(struct backend_base *base, const struct dummy_imag
assert(*tmp->refcount > 0);
}
void dummy_compose(struct backend_base *base, void *image, coord_t dst attr_unused,
void *mask attr_unused, coord_t mask_dst attr_unused,
void dummy_compose(struct backend_base *base, image_handle image, coord_t dst attr_unused,
image_handle mask attr_unused, coord_t mask_dst attr_unused,
const region_t *reg_paint attr_unused,
const region_t *reg_visible attr_unused, bool lerp attr_unused) {
auto dummy attr_unused = (struct dummy_data *)base;
dummy_check_image(base, image);
assert(mask == NULL || mask == &dummy->mask);
assert(mask == NULL || (struct backend_image *)mask == &dummy->mask);
}
void dummy_fill(struct backend_base *backend_data attr_unused, struct color c attr_unused,
@@ -78,20 +79,20 @@ void dummy_fill(struct backend_base *backend_data attr_unused, struct color c at
}
bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity attr_unused,
void *blur_ctx attr_unused, void *mask attr_unused,
void *blur_ctx attr_unused, image_handle mask attr_unused,
coord_t mask_dst attr_unused, const region_t *reg_blur attr_unused,
const region_t *reg_visible attr_unused) {
return true;
}
void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned) {
image_handle dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned) {
auto dummy = (struct dummy_data *)base;
struct dummy_image *img = NULL;
HASH_FIND_INT(dummy->images, &pixmap, img);
if (img) {
(*img->refcount)++;
return img;
return (image_handle)img;
}
img = ccalloc(1, struct dummy_image);
@@ -102,12 +103,12 @@ void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
img->owned = owned;
HASH_ADD_INT(dummy->images, pixmap, img);
return (void *)img;
return (image_handle)img;
}
void dummy_release_image(backend_t *base, void *image) {
void dummy_release_image(backend_t *base, image_handle image) {
auto dummy = (struct dummy_data *)base;
if (image == &dummy->mask) {
if ((struct backend_image *)image == &dummy->mask) {
return;
}
auto img = (struct dummy_image *)image;
@@ -123,10 +124,9 @@ void dummy_release_image(backend_t *base, void *image) {
}
}
bool dummy_is_image_transparent(struct backend_base *base, void *image) {
auto img = (struct dummy_image *)image;
dummy_check_image(base, img);
return img->transparent;
bool dummy_is_image_transparent(struct backend_base *base, image_handle image) {
dummy_check_image(base, image);
return ((struct dummy_image *)image)->transparent;
}
int dummy_buffer_age(struct backend_base *base attr_unused) {
@@ -134,29 +134,31 @@ int dummy_buffer_age(struct backend_base *base attr_unused) {
}
bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unused,
void *image, const region_t *reg_op attr_unused,
image_handle image, const region_t *reg_op attr_unused,
const region_t *reg_visible attr_unused, void *args attr_unused) {
dummy_check_image(base, image);
return true;
}
void *dummy_make_mask(struct backend_base *base, geometry_t size attr_unused,
const region_t *reg attr_unused) {
return &(((struct dummy_data *)base)->mask);
image_handle dummy_make_mask(struct backend_base *base, geometry_t size attr_unused,
const region_t *reg attr_unused) {
auto dummy = (struct dummy_data *)base;
auto mask = &dummy->mask;
return (image_handle)mask;
}
bool dummy_set_image_property(struct backend_base *base, enum image_properties prop attr_unused,
void *image, void *arg attr_unused) {
image_handle image, void *arg attr_unused) {
dummy_check_image(base, image);
return true;
}
void *dummy_clone_image(struct backend_base *base, const void *image,
const region_t *reg_visible attr_unused) {
auto img = (const struct dummy_image *)image;
dummy_check_image(base, img);
(*img->refcount)++;
return (void *)img;
image_handle dummy_clone_image(struct backend_base *base, image_handle image,
const region_t *reg_visible attr_unused) {
dummy_check_image(base, image);
auto image_impl = (struct dummy_image *)image;
(*image_impl->refcount)++;
return image;
}
void *dummy_create_blur_context(struct backend_base *base attr_unused,

View File

@@ -256,10 +256,11 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec
return true;
}
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible attr_unused,
GLuint source_texture, geometry_t source_size, GLuint target_fbo,
GLuint default_mask, bool high_precision) {
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx,
struct backend_image *mask, coord_t mask_dst, const region_t *reg_blur,
const region_t *reg_visible attr_unused, GLuint source_texture,
geometry_t source_size, GLuint target_fbo, GLuint default_mask,
bool high_precision) {
bool ret = false;
if (source_size.width != bctx->fb_width || source_size.height != bctx->fb_height) {
@@ -400,12 +401,12 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coor
return ret;
}
bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mask_dst,
bool gl_blur(backend_t *base, double opacity, void *ctx, image_handle mask, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible attr_unused) {
auto gd = (struct gl_data *)base;
auto bctx = (struct gl_blur_context *)ctx;
return gl_blur_impl(opacity, bctx, mask, mask_dst, reg_blur, reg_visible,
gd->back_texture,
return gl_blur_impl(opacity, bctx, (struct backend_image *)mask, mask_dst,
reg_blur, reg_visible, gd->back_texture,
(geometry_t){.width = gd->width, .height = gd->height},
gd->back_fbo, gd->default_mask_texture, gd->dithered_present);
}

View File

@@ -36,12 +36,6 @@ struct egl_data {
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 @@ static void egl_release_image(backend_t *base, struct gl_texture *tex) {
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 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
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 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
}
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 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
// 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 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
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;
@@ -275,7 +249,7 @@ end:
return &gd->gl.base;
}
static void *
static image_handle
egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
struct egl_data *gd = (void *)base;
struct egl_pixmap *eglpixmap = NULL;
@@ -302,9 +276,8 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
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 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
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;
return (image_handle)wd;
err:
if (eglpixmap && eglpixmap->image) {
eglDestroyImageProc(gd->display, eglpixmap->image);
eglDestroyImage(gd->display, eglpixmap->image);
}
free(eglpixmap);
@@ -422,41 +395,6 @@ struct backend_operations egl_ops = {
.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 @@ void eglext_init(EGLDisplay dpy) {
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 @@ void eglext_init(EGLDisplay dpy) {
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
}

View File

@@ -1,10 +1,8 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@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 @@ struct eglext_info {
extern struct eglext_info eglext;
#ifdef EGL_MESA_query_driver
extern PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName;
#endif
void eglext_init(EGLDisplay);

View File

@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <GL/gl.h>
#include <GL/glext.h>
#include <epoxy/gl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
@@ -553,11 +552,12 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
}
// TODO(yshui) make use of reg_visible
void gl_compose(backend_t *base, void *image_data, coord_t image_dst, void *mask,
void gl_compose(backend_t *base, image_handle image_data, coord_t image_dst, image_handle mask_,
coord_t mask_dst, const region_t *reg_tgt,
const region_t *reg_visible attr_unused, bool lerp) {
auto gd = (struct gl_data *)base;
struct backend_image *img = image_data;
auto img = (struct backend_image *)image_data;
auto mask = (struct backend_image *)mask_;
auto inner = (struct gl_texture *)img->inner;
// Painting
@@ -719,7 +719,7 @@ void gl_fill(backend_t *base, struct color c, const region_t *clip) {
return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
}
void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
image_handle gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
auto tex = ccalloc(1, struct gl_texture);
auto img = default_new_backend_image(size.width, size.height);
tex->width = size.width;
@@ -750,7 +750,7 @@ void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
return img;
return (image_handle)img;
}
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
@@ -766,8 +766,8 @@ static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
gl_check_err();
}
void gl_release_image(backend_t *base, void *image_data) {
struct backend_image *wd = image_data;
void gl_release_image(backend_t *base, image_handle image) {
auto wd = (struct backend_image *)image;
auto inner = (struct gl_texture *)wd->inner;
inner->refcount--;
assert(inner->refcount >= 0);
@@ -894,24 +894,35 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUseProgram(0);
gd->dithered_present = ps->o.dithered_present;
gd->dummy_prog =
gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL},
(const char *[]){dummy_frag, NULL});
if (!gd->dummy_prog) {
log_error("Failed to create the dummy shader");
return false;
}
if (gd->dithered_present) {
gd->present_prog = gl_create_program_from_strv(
(const char *[]){present_vertex_shader, NULL},
(const char *[]){present_frag, dither_glsl, NULL});
} else {
gd->present_prog = gl_create_program_from_strv(
(const char *[]){present_vertex_shader, NULL},
(const char *[]){dummy_frag, NULL});
gd->present_prog = gd->dummy_prog;
}
if (!gd->present_prog) {
log_error("Failed to create the present shader");
return false;
}
pml = glGetUniformLocationChecked(gd->present_prog, "projection");
glUseProgram(gd->present_prog);
glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
pml = glGetUniformLocationChecked(gd->dummy_prog, "projection");
glUseProgram(gd->dummy_prog);
glUniform1i(glGetUniformLocationChecked(gd->dummy_prog, "tex"), 0);
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0);
if (gd->present_prog != gd->dummy_prog) {
pml = glGetUniformLocationChecked(gd->present_prog, "projection");
glUseProgram(gd->present_prog);
glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
}
gd->shadow_shader.prog =
gl_create_program_from_str(present_vertex_shader, shadow_colorization_frag);
@@ -921,7 +932,6 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUseProgram(gd->shadow_shader.prog);
glUniform1i(glGetUniformLocationChecked(gd->shadow_shader.prog, "tex"), 0);
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0);
glBindFragDataLocation(gd->shadow_shader.prog, 0, "out_color");
gd->brightness_shader.prog =
@@ -934,6 +944,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
glUseProgram(gd->brightness_shader.prog);
glUniform1i(glGetUniformLocationChecked(gd->brightness_shader.prog, "tex"), 0);
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
glUseProgram(0);
// Set up the size and format of the back texture
@@ -970,8 +981,8 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
} 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;
@@ -987,10 +998,25 @@ void gl_deinit(struct gl_data *gd) {
gl_destroy_window_shader(&gd->base, gd->default_shader);
gd->default_shader = NULL;
}
glDeleteProgram(gd->dummy_prog);
if (gd->present_prog != gd->dummy_prog) {
glDeleteProgram(gd->present_prog);
}
gd->dummy_prog = 0;
gd->present_prog = 0;
glDeleteProgram(gd->fill_shader.prog);
glDeleteProgram(gd->brightness_shader.prog);
glDeleteProgram(gd->shadow_shader.prog);
gd->fill_shader.prog = 0;
gd->brightness_shader.prog = 0;
gd->shadow_shader.prog = 0;
glDeleteTextures(1, &gd->default_mask_texture);
glDeleteTextures(1, &gd->back_texture);
glDeleteQueries(2, gd->frame_timing);
gl_check_err();
}
@@ -1022,9 +1048,11 @@ static inline void gl_image_decouple(backend_t *base, struct backend_image *img)
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);
@@ -1033,8 +1061,7 @@ static inline void gl_image_decouple(backend_t *base, struct backend_image *img)
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
assert(gd->present_prog);
glUseProgram(gd->present_prog);
glUseProgram(gd->dummy_prog);
glBindTexture(GL_TEXTURE_2D, inner->texture);
GLuint fbo;
@@ -1204,9 +1231,9 @@ bool gl_last_render_time(backend_t *base, struct timespec *ts) {
return true;
}
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
bool gl_image_op(backend_t *base, enum image_operations op, image_handle image,
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
struct backend_image *tex = image_data;
auto tex = (struct backend_image *)image;
switch (op) {
case IMAGE_OP_APPLY_ALPHA:
gl_image_decouple(base, tex);
@@ -1219,12 +1246,12 @@ bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
}
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
void *image_data, void *args) {
image_handle image, void *args) {
if (prop != IMAGE_PROPERTY_CUSTOM_SHADER) {
return default_set_image_property(backend_data, prop, image_data, args);
return default_set_image_property(backend_data, prop, image, args);
}
struct backend_image *img = image_data;
auto img = (struct backend_image *)image;
auto inner = (struct gl_texture *)img->inner;
inner->shader = args;
return true;
@@ -1263,12 +1290,12 @@ void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shado
free(ctx_);
}
void *gl_shadow_from_mask(backend_t *base, void *mask,
struct backend_shadow_context *sctx, struct color color) {
image_handle gl_shadow_from_mask(backend_t *base, image_handle mask_,
struct backend_shadow_context *sctx, struct color color) {
log_debug("Create shadow from mask");
auto gd = (struct gl_data *)base;
auto img = (struct backend_image *)mask;
auto inner = (struct gl_texture *)img->inner;
auto mask = (struct backend_image *)mask_;
auto inner = (struct gl_texture *)mask->inner;
auto gsctx = (struct gl_shadow_context *)sctx;
int radius = (int)gsctx->radius;
@@ -1298,7 +1325,7 @@ void *gl_shadow_from_mask(backend_t *base, void *mask,
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
source_texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (img->color_inverted) {
if (mask->color_inverted) {
// If the mask is inverted, clear the source_texture to white, so the
// "outside" of the mask would be correct
glClearColor(1, 1, 1, 1);
@@ -1407,7 +1434,7 @@ void *gl_shadow_from_mask(backend_t *base, void *mask,
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
gl_check_err();
return new_img;
return (image_handle)new_img;
}
enum device_status gl_device_status(backend_t *base) {

View File

@@ -1,12 +1,12 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <GL/gl.h>
#include <GL/glext.h>
#include <epoxy/gl.h>
#include <stdbool.h>
#include <string.h>
#include "backend/backend.h"
#include "backend/backend_common.h"
#include "log.h"
#include "region.h"
@@ -29,8 +29,6 @@ static inline GLint glGetUniformLocationChecked(GLuint p, const char *name) {
// Program and uniforms for window shader
typedef struct {
UT_hash_handle hh;
uint32_t id;
GLuint prog;
GLint uniform_opacity;
GLint uniform_invert_color;
@@ -112,6 +110,7 @@ struct gl_data {
GLuint frame_timing[2];
int current_frame_timing;
GLuint present_prog;
GLuint dummy_prog;
bool dithered_present;
@@ -145,13 +144,13 @@ void *gl_create_window_shader(backend_t *backend_data, const char *source);
void gl_destroy_window_shader(backend_t *backend_data, void *shader);
uint64_t gl_get_shader_attributes(backend_t *backend_data, void *shader);
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
void *image_data, void *args);
image_handle image, void *args);
bool gl_last_render_time(backend_t *backend_data, struct timespec *time);
/**
* @brief Render a region with texture data.
*/
void gl_compose(backend_t *, void *image_data, coord_t image_dst, void *mask,
void gl_compose(backend_t *, image_handle image_data, coord_t image_dst, image_handle mask_,
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible, bool lerp);
void gl_resize(struct gl_data *, int width, int height);
@@ -161,32 +160,32 @@ void gl_deinit(struct gl_data *gd);
GLuint gl_new_texture(GLenum target);
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
bool gl_image_op(backend_t *base, enum image_operations op, image_handle image,
const region_t *reg_op, const region_t *reg_visible, void *arg);
void gl_release_image(backend_t *base, void *image_data);
void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg);
void gl_release_image(backend_t *base, image_handle image);
image_handle gl_make_mask(backend_t *base, geometry_t size, const region_t *reg);
void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visible);
image_handle gl_clone(backend_t *base, image_handle image, const region_t *reg_visible);
bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible);
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst,
const region_t *reg_blur, const region_t *reg_visible attr_unused,
GLuint source_texture, geometry_t source_size, GLuint target_fbo,
GLuint default_mask, bool high_precision);
bool gl_blur(backend_t *base, double opacity, void *ctx, image_handle mask,
coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible);
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx,
struct backend_image *mask, coord_t mask_dst, const region_t *reg_blur,
const region_t *reg_visible attr_unused, GLuint source_texture,
geometry_t source_size, GLuint target_fbo, GLuint default_mask,
bool high_precision);
void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
void gl_destroy_blur_context(backend_t *base, void *ctx);
struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius);
void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx);
void *gl_shadow_from_mask(backend_t *base, void *mask,
struct backend_shadow_context *sctx, struct color color);
image_handle gl_shadow_from_mask(backend_t *base, image_handle mask,
struct backend_shadow_context *sctx, struct color color);
void gl_get_blur_size(void *blur_context, int *width, int *height);
void gl_fill(backend_t *base, struct color, const region_t *clip);
void gl_present(backend_t *base, const region_t *);
bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color *output);
enum device_status gl_device_status(backend_t *base);
/**
@@ -265,26 +264,6 @@ static inline bool gl_check_fb_complete_(const char *func, int line, GLenum fb)
#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;

View File

@@ -17,8 +17,10 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <uthash.h>
#include <xcb/composite.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include "backend/backend.h"
#include "backend/backend_common.h"
@@ -44,6 +46,13 @@ struct _glx_data {
struct gl_data gl;
xcb_window_t target_win;
GLXContext ctx;
struct glx_fbconfig_cache *cached_fbconfigs;
};
struct glx_fbconfig_cache {
UT_hash_handle hh;
struct xvisual_info visual_info;
struct glx_fbconfig_info info;
};
#define glXGetFBConfigAttribChecked(a, b, attr, c) \
@@ -54,9 +63,12 @@ struct _glx_data {
} \
} while (0)
struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisual_info m) {
log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size,
m.blue_size, m.green_size, m.alpha_size, m.visual_depth);
bool glx_find_fbconfig(struct x_connection *c, struct xvisual_info m,
struct glx_fbconfig_info *info) {
log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth: %d, visual id: %#x", m.red_size,
m.blue_size, m.green_size, m.alpha_size, m.visual_depth, m.visual);
info->cfg = NULL;
int ncfg;
// clang-format off
@@ -114,7 +126,8 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
int visual;
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_VISUAL_ID, &visual);
if (m.visual_depth != -1 &&
x_get_visual_depth(c, (xcb_visualid_t)visual) != m.visual_depth) {
xcb_aux_get_depth_of_visual(c->screen_info, (xcb_visualid_t)visual) !=
m.visual_depth) {
// FBConfig and the correspondent X Visual might not have the same
// depth. (e.g. 32 bit FBConfig with a 24 bit Visual). This is
// quite common, seen in both open source and proprietary drivers.
@@ -143,16 +156,13 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
min_cost = depthbuf + stencil + bufsize * (doublebuf + 1);
}
free(cfg);
if (!found) {
return NULL;
if (found) {
info->cfg = ret;
info->texture_tgts = texture_tgts;
info->texture_fmt = texture_fmt;
info->y_inverted = y_inverted;
}
auto info = cmalloc(struct glx_fbconfig_info);
info->cfg = ret;
info->texture_tgts = texture_tgts;
info->texture_fmt = texture_fmt;
info->y_inverted = y_inverted;
return info;
return found;
}
/**
@@ -197,6 +207,12 @@ void glx_deinit(backend_t *base) {
gd->ctx = 0;
}
struct glx_fbconfig_cache *cached_fbconfig = NULL, *tmp = NULL;
HASH_ITER(hh, gd->cached_fbconfigs, cached_fbconfig, tmp) {
HASH_DEL(gd->cached_fbconfigs, cached_fbconfig);
free(cached_fbconfig);
}
free(gd);
}
@@ -364,9 +380,10 @@ end:
return &gd->gl.base;
}
static void *
static image_handle
glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
struct _glx_pixmap *glxpixmap = NULL;
auto gd = (struct _glx_data *)base;
// Retrieve pixmap parameters, if they aren't provided
if (fmt.visual_depth > OPENGL_MAX_DEPTH) {
log_error("Requested depth %d higher than max possible depth %d.",
@@ -394,38 +411,52 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
wd->inner = (struct backend_image_inner_base *)inner;
free(r);
auto fbcfg = glx_find_fbconfig(base->c, fmt);
if (!fbcfg) {
log_error("Couldn't find FBConfig with requested visual %x", fmt.visual);
goto err;
struct glx_fbconfig_cache *cached_fbconfig = NULL;
HASH_FIND(hh, gd->cached_fbconfigs, &fmt, sizeof(fmt), cached_fbconfig);
if (!cached_fbconfig) {
struct glx_fbconfig_info fbconfig;
if (!glx_find_fbconfig(base->c, fmt, &fbconfig)) {
log_error("Couldn't find FBConfig with requested visual %#x",
fmt.visual);
goto err;
}
cached_fbconfig = cmalloc(struct glx_fbconfig_cache);
cached_fbconfig->visual_info = fmt;
cached_fbconfig->info = fbconfig;
HASH_ADD(hh, gd->cached_fbconfigs, visual_info, sizeof(fmt), cached_fbconfig);
} else {
log_debug("Found cached FBConfig for RGBA%d%d%d%d, depth: %d, visual id: "
"%#x",
fmt.red_size, fmt.blue_size, fmt.green_size, fmt.alpha_size,
fmt.visual_depth, fmt.visual);
}
struct glx_fbconfig_info *fbconfig = &cached_fbconfig->info;
// Choose a suitable texture target for our pixmap.
// Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
// of the bits in texture_tgts
if (!(fbcfg->texture_tgts & GLX_TEXTURE_2D_BIT_EXT)) {
if (!(fbconfig->texture_tgts & GLX_TEXTURE_2D_BIT_EXT)) {
log_error("Cannot bind pixmap to GL_TEXTURE_2D, giving up");
goto err;
}
log_debug("depth %d, rgba %d", fmt.visual_depth,
(fbcfg->texture_fmt == GLX_TEXTURE_FORMAT_RGBA_EXT));
(fbconfig->texture_fmt == GLX_TEXTURE_FORMAT_RGBA_EXT));
GLint attrs[] = {
GLX_TEXTURE_FORMAT_EXT,
fbcfg->texture_fmt,
fbconfig->texture_fmt,
GLX_TEXTURE_TARGET_EXT,
GLX_TEXTURE_2D_EXT,
0,
};
inner->y_inverted = fbcfg->y_inverted;
inner->y_inverted = fbconfig->y_inverted;
glxpixmap = cmalloc(struct _glx_pixmap);
glxpixmap->pixmap = pixmap;
glxpixmap->glpixmap = glXCreatePixmap(base->c->dpy, fbcfg->cfg, pixmap, attrs);
glxpixmap->glpixmap = glXCreatePixmap(base->c->dpy, fbconfig->cfg, pixmap, attrs);
glxpixmap->owned = owned;
free(fbcfg);
if (!glxpixmap->glpixmap) {
log_error("Failed to create glpixmap for pixmap %#010x", pixmap);
@@ -444,7 +475,7 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
glBindTexture(GL_TEXTURE_2D, 0);
gl_check_err();
return wd;
return (image_handle)wd;
err:
if (glxpixmap && glxpixmap->glpixmap) {
glXDestroyPixmap(base->c->dpy, glxpixmap->glpixmap);
@@ -545,62 +576,17 @@ struct backend_operations glx_ops = {
.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 +600,4 @@ void glxext_init(Display *dpy, int screen) {
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
}

View File

@@ -1,17 +1,9 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@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>
@@ -27,21 +19,8 @@ struct glx_fbconfig_info {
int y_inverted;
};
/// The search criteria for glx_find_fbconfig
struct glx_fbconfig_criteria {
/// Bit width of the red component
int red_size;
/// Bit width of the green component
int green_size;
/// Bit width of the blue component
int blue_size;
/// Bit width of the alpha component
int alpha_size;
/// The depth of X visual
int visual_depth;
};
struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *, struct xvisual_info);
bool glx_find_fbconfig(struct x_connection *c, struct xvisual_info m,
struct glx_fbconfig_info *info);
struct glxext_info {
bool initialized;
@@ -59,19 +38,4 @@ struct glxext_info {
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);

View File

@@ -56,6 +56,10 @@ typedef struct _xrender_data {
int target_width, target_height;
xcb_special_event_t *present_event;
/// Cache an X region to avoid creating and destroying it every frame. A
/// workaround for yshui/picom#1166.
xcb_xfixes_region_t present_region;
} xrender_data;
struct _xrender_blur_context {
@@ -355,10 +359,12 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
pixman_region32_fini(&reg);
}
static void compose(backend_t *base, void *img_data, coord_t dst, void *mask, coord_t mask_dst,
const region_t *reg_paint, const region_t *reg_visible, bool lerp attr_unused) {
static void compose(backend_t *base, image_handle image_, coord_t dst, image_handle mask_,
coord_t mask_dst, const region_t *reg_paint, const region_t *reg_visible) {
struct _xrender_data *xd = (void *)base;
return compose_impl(xd, img_data, dst, mask, mask_dst, reg_paint, reg_visible,
auto image = (struct xrender_image *)image_;
auto mask = (struct xrender_image *)mask_;
return compose_impl(xd, image, dst, mask, mask_dst, reg_paint, reg_visible,
xd->back[2]);
}
@@ -380,9 +386,10 @@ static void fill(backend_t *base, struct color c, const region_t *clip) {
.height = to_u16_checked(extent->y2 - extent->y1)}});
}
static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask,
static bool blur(backend_t *backend_data, double opacity, void *ctx_, image_handle mask_,
coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible) {
struct _xrender_blur_context *bctx = ctx_;
auto bctx = (struct _xrender_blur_context *)ctx_;
auto mask = (struct xrender_image *)mask_;
if (bctx->method == BLUR_METHOD_NONE) {
return true;
}
@@ -513,7 +520,7 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
return true;
}
static void *
static image_handle
bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
xcb_generic_error_t *e;
auto r = xcb_get_geometry_reply(base->c->c, xcb_get_geometry(base->c->c, pixmap), &e);
@@ -548,7 +555,7 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool
free(img);
return NULL;
}
return img;
return (image_handle)img;
}
static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) {
x_free_picture(base->c, inner->pict);
@@ -572,13 +579,13 @@ release_rounded_corner_cache(backend_t *base, struct xrender_rounded_rectangle_c
}
}
static void release_image(backend_t *base, void *image) {
struct xrender_image *img = image;
static void release_image(backend_t *base, image_handle image) {
auto img = (struct xrender_image *)image;
release_rounded_corner_cache(base, img->rounded_rectangle);
img->rounded_rectangle = NULL;
img->base.inner->refcount -= 1;
if (img->base.inner->refcount == 0) {
release_image_inner(base, (void *)img->base.inner);
release_image_inner(base, (struct _xrender_image_data_inner *)img->base.inner);
}
free(img);
}
@@ -597,6 +604,7 @@ static void deinit(backend_t *backend_data) {
xcb_free_pixmap(xd->base.c->c, xd->back_pixmap[i]);
}
}
x_destroy_region(xd->base.c, xd->present_region);
if (xd->present_event) {
xcb_unregister_for_special_event(xd->base.c->c, xd->present_event);
}
@@ -624,16 +632,15 @@ static void present(backend_t *base, const region_t *region) {
XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0,
0, orig_x, orig_y, region_width, region_height);
auto xregion = x_create_region(base->c, region);
// Make sure we got reply from PresentPixmap before waiting for events,
// to avoid deadlock
auto e = xcb_request_check(
base->c->c, xcb_present_pixmap_checked(
xd->base.c->c, xd->target_win,
xd->back_pixmap[xd->curr_back], 0, XCB_NONE, xregion, 0,
0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
x_destroy_region(base->c, xregion);
base->c->c,
xcb_present_pixmap_checked(
base->c->c, xd->target_win, xd->back_pixmap[xd->curr_back], 0, XCB_NONE,
x_set_region(base->c, xd->present_region, region) ? xd->present_region
: XCB_NONE,
0, 0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
if (e) {
log_error("Failed to present pixmap");
free(e);
@@ -707,7 +714,7 @@ new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
return new_inner;
}
static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
static image_handle make_mask(backend_t *base, geometry_t size, const region_t *reg) {
struct _xrender_data *xd = (void *)base;
// Give the mask a 1 pixel wide border to emulate the clamp to border behavior of
// OpenGL textures.
@@ -750,7 +757,7 @@ static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
img->base.dim = 0;
img->base.inner = (struct backend_image_inner_base *)inner;
img->rounded_rectangle = NULL;
return img;
return (image_handle)img;
}
static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) {
@@ -778,10 +785,10 @@ static bool decouple_image(backend_t *base, struct backend_image *img, const reg
return true;
}
static bool image_op(backend_t *base, enum image_operations op, void *image,
static bool image_op(backend_t *base, enum image_operations op, image_handle image,
const region_t *reg_op, const region_t *reg_visible, void *arg) {
struct _xrender_data *xd = (void *)base;
struct backend_image *img = image;
auto img = (struct backend_image *)image;
region_t reg;
double *dargs = arg;
@@ -929,6 +936,10 @@ static backend_t *backend_xrender_init(session_t *ps, xcb_window_t target) {
xd->vsync = false;
}
if (xd->vsync) {
xd->present_region = x_create_region(&ps->c, &ps->screen_reg);
}
// We might need to do double buffering for vsync, and buffer 0 and 1 are for
// double buffering.
int first_buffer_index = xd->vsync ? 0 : 2;
@@ -956,19 +967,19 @@ err:
return NULL;
}
void *clone_image(backend_t *base attr_unused, const void *image_data,
const region_t *reg_visible attr_unused) {
image_handle clone_image(backend_t *base attr_unused, image_handle image,
const region_t *reg_visible attr_unused) {
auto new_img = ccalloc(1, struct xrender_image);
*new_img = *(struct xrender_image *)image_data;
*new_img = *(struct xrender_image *)image;
new_img->base.inner->refcount++;
if (new_img->rounded_rectangle) {
new_img->rounded_rectangle->refcount++;
}
return new_img;
return (image_handle)new_img;
}
static bool
set_image_property(backend_t *base, enum image_properties op, void *image, void *args) {
static bool set_image_property(backend_t *base, enum image_properties op,
image_handle image, void *args) {
auto xrimg = (struct xrender_image *)image;
if (op == IMAGE_PROPERTY_CORNER_RADIUS &&
((double *)args)[0] != xrimg->base.corner_radius) {

View File

@@ -1047,7 +1047,7 @@ static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
// Get target atom if it's not a predefined one
if (pleaf->predef == C2_L_PUNDEFINED) {
pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt);
pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt, ps->c.c);
if (!pleaf->tgtatom) {
log_error("Failed to get atom for target \"%s\".", pleaf->tgt);
return false;
@@ -1380,14 +1380,10 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
case C2_L_PWIDTHB: predef_target = w->widthb; break;
case C2_L_PHEIGHTB: predef_target = w->heightb; break;
case C2_L_PBDW: predef_target = w->g.border_width; break;
case C2_L_PFULLSCREEN:
predef_target = win_is_fullscreen(ps, w);
break;
case C2_L_PFULLSCREEN: predef_target = w->is_fullscreen; break;
case C2_L_POVREDIR: predef_target = w->a.override_redirect; break;
case C2_L_PARGB: predef_target = win_has_alpha(w); break;
case C2_L_PFOCUSED:
predef_target = win_is_focused_raw(ps, w);
break;
case C2_L_PFOCUSED: predef_target = win_is_focused_raw(w); break;
case C2_L_PWMWIN: predef_target = w->wmwin; break;
case C2_L_PBSHAPED: predef_target = w->bounding_shaped; break;
case C2_L_PROUNDED: predef_target = w->rounded_corners; break;
@@ -1513,7 +1509,8 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
else {
char **strlst = NULL;
int nstr = 0;
if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, &nstr)) {
if (wid_get_text_prop(&ps->c, ps->atoms, wid, pleaf->tgtatom,
&strlst, &nstr)) {
if (pleaf->index < 0 && nstr > 0 && strlen(strlst[0]) > 0) {
ntargets = to_u32_checked(nstr);
targets = (const char **)strlst;

View File

@@ -1,95 +1,43 @@
#include <uthash.h>
#include "compiler.h"
#include "utils.h"
#include "cache.h"
struct cache_entry {
char *key;
void *value;
UT_hash_handle hh;
};
struct cache {
cache_getter_t getter;
cache_free_t free;
void *user_data;
struct cache_entry *entries;
};
void cache_set(struct cache *c, const char *key, void *data) {
struct cache_entry *e = NULL;
struct cache_handle *cache_get(struct cache *c, const char *key) {
struct cache_handle *e;
HASH_FIND_STR(c->entries, key, e);
CHECK(!e);
e = ccalloc(1, struct cache_entry);
e->key = strdup(key);
e->value = data;
HASH_ADD_STR(c->entries, key, e);
return e;
}
void *cache_get(struct cache *c, const char *key, int *err) {
struct cache_entry *e;
HASH_FIND_STR(c->entries, key, e);
if (e) {
return e->value;
int cache_get_or_fetch(struct cache *c, const char *key, struct cache_handle **value,
void *user_data, cache_getter_t getter) {
*value = cache_get(c, key);
if (*value) {
return 0;
}
int tmperr;
if (!err) {
err = &tmperr;
int err = getter(c, key, value, user_data);
assert(err <= 0);
if (err < 0) {
return err;
}
(*value)->key = strdup(key);
*err = 0;
e = ccalloc(1, struct cache_entry);
e->key = strdup(key);
e->value = c->getter(c->user_data, key, err);
if (*err) {
free(e->key);
free(e);
return NULL;
}
HASH_ADD_STR(c->entries, key, e);
return e->value;
HASH_ADD_STR(c->entries, key, *value);
return 1;
}
static inline void _cache_invalidate(struct cache *c, struct cache_entry *e) {
if (c->free) {
c->free(c->user_data, e->value);
}
static inline void
cache_invalidate_impl(struct cache *c, struct cache_handle *e, cache_free_t free_fn) {
free(e->key);
HASH_DEL(c->entries, e);
free(e);
}
void cache_invalidate(struct cache *c, const char *key) {
struct cache_entry *e;
HASH_FIND_STR(c->entries, key, e);
if (e) {
_cache_invalidate(c, e);
if (free_fn) {
free_fn(c, e);
}
}
void cache_invalidate_all(struct cache *c) {
struct cache_entry *e, *tmpe;
void cache_invalidate_all(struct cache *c, cache_free_t free_fn) {
struct cache_handle *e, *tmpe;
HASH_ITER(hh, c->entries, e, tmpe) {
_cache_invalidate(c, e);
cache_invalidate_impl(c, e, free_fn);
}
}
void *cache_free(struct cache *c) {
void *ret = c->user_data;
cache_invalidate_all(c);
free(c);
return ret;
}
struct cache *new_cache(void *ud, cache_getter_t getter, cache_free_t f) {
auto c = ccalloc(1, struct cache);
c->user_data = ud;
c->getter = getter;
c->free = f;
return c;
}

View File

@@ -1,32 +1,43 @@
#pragma once
#include <uthash.h>
#include "utils.h"
#define cache_entry(ptr, type, member) container_of(ptr, type, member)
struct cache;
struct cache_handle;
typedef void *(*cache_getter_t)(void *user_data, const char *key, int *err);
typedef void (*cache_free_t)(void *user_data, void *data);
/// User-provided function to fetch a value for the cache, when it's not present.
/// Should return 0 if the value is fetched successfully, and a negative number if the
/// value cannot be fetched. Getter doesn't need to initialize fields of `struct
/// cache_handle`.
typedef int (*cache_getter_t)(struct cache *, const char *key,
struct cache_handle **value, void *user_data);
typedef void (*cache_free_t)(struct cache *, struct cache_handle *value);
/// Create a cache with `getter`, and a free function `f` which is used to free the cache
/// value when they are invalidated.
///
/// `user_data` will be passed to `getter` and `f` when they are called.
struct cache *new_cache(void *user_data, cache_getter_t getter, cache_free_t f);
struct cache {
struct cache_handle *entries;
};
/// Fetch a value from the cache. If the value doesn't present in the cache yet, the
static const struct cache CACHE_INIT = {NULL};
struct cache_handle {
char *key;
UT_hash_handle hh;
};
/// Get a value from the cache. If the value doesn't present in the cache yet, the
/// getter will be called, and the returned value will be stored into the cache.
void *cache_get(struct cache *, const char *key, int *err);
/// Returns 0 if the value is already present in the cache, 1 if the value is fetched
/// successfully, and a negative number if the value cannot be fetched.
int cache_get_or_fetch(struct cache *, const char *key, struct cache_handle **value,
void *user_data, cache_getter_t getter);
/// Invalidate a value in the cache.
void cache_invalidate(struct cache *, const char *key);
/// Get a value from the cache. If the value doesn't present in the cache, NULL will be
/// returned.
struct cache_handle *cache_get(struct cache *, const char *key);
/// Invalidate all values in the cache.
void cache_invalidate_all(struct cache *);
/// Invalidate all values in the cache and free it. Returns the user data passed to
/// `new_cache`
void *cache_free(struct cache *);
/// Insert a key-value pair into the cache. Only used for internal testing. Takes
/// ownership of `data`
///
/// If `key` already exists in the cache, this function will abort the program.
void cache_set(struct cache *c, const char *key, void *data);
/// Invalidate all values in the cache. After this call, `struct cache` holds no allocated
/// memory, and can be discarded.
void cache_invalidate_all(struct cache *, cache_free_t free_fn);

View File

@@ -190,7 +190,7 @@ typedef struct session {
/// Picture of the root window background.
paint_t root_tile_paint;
/// The backend data the root pixmap bound to
void *root_image;
image_handle root_image;
/// A region of the size of the screen.
region_t screen_reg;
/// Picture of root window. Destination of painting in no-DBE painting
@@ -208,7 +208,7 @@ typedef struct session {
/// Custom GLX program used for painting window.
// XXX should be in struct glx_session
glx_prog_main_t glx_prog_win;
struct glx_fbconfig_info *argb_fbconfig;
struct glx_fbconfig_info argb_fbconfig;
#endif
/// Sync fence to sync draw operations
xcb_sync_fence_t sync_fence;

View File

@@ -105,9 +105,9 @@
#ifndef unreachable
# if defined(__GNUC__) || defined(__clang__)
# define unreachable() __builtin_unreachable()
# define unreachable() assert(false); __builtin_unreachable()
# else
# define unreachable() do {} while(0)
# define unreachable() assert(false); do {} while(0)
# endif
#endif

View File

@@ -43,10 +43,7 @@ const char *xdg_config_home(void) {
return NULL;
}
xdgh = cvalloc(strlen(home) + strlen(default_dir) + 1);
strcpy(xdgh, home);
strcat(xdgh, default_dir);
xdgh = mstrjoin(home, default_dir);
} else {
xdgh = strdup(xdgh);
}

View File

@@ -906,7 +906,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
}
if (!strcmp("RawFocused", target)) {
cdbus_reply(ps, msg, cdbus_append_bool_variant,
(bool[]){win_is_focused_raw(ps, w)});
(bool[]){win_is_focused_raw(w)});
return true;
}
@@ -976,7 +976,7 @@ static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
cdbus_m_win_get_do(leader, cdbus_reply_wid);
if (!strcmp("focused_raw", target)) {
cdbus_reply_bool(ps, msg, win_is_focused_raw(ps, w));
cdbus_reply_bool(ps, msg, win_is_focused_raw(w));
return true;
}
cdbus_m_win_get_do(fade_force, cdbus_reply_enum);

View File

@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2019, Yuxuan Shui <yshuiv7@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.
///
/// 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
// 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 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
}
// 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 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
}
}
}
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
}
}
@@ -475,9 +479,10 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
// 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 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
// 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);
}
}
@@ -560,6 +565,13 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
}
}
if (!ps->o.no_ewmh_fullscreen && ev->atom == ps->atoms->a_NET_WM_STATE) {
auto w = find_toplevel(ps, ev->window);
if (w) {
win_set_property_stale(w, ev->atom);
}
}
// Check for other atoms we are tracking
for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
if (platom->atom == ev->atom) {
@@ -586,16 +598,28 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
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,

View File

@@ -2,18 +2,7 @@
#include <stdbool.h>
#include <stddef.h>
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) \
({ \
const __typeof__(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); \
})
#include "utils.h"
struct list_node {
struct list_node *next, *prev;

View File

@@ -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 @@ struct log_target *stderr_logger_new(void) {
}
#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 @@ static const struct log_ops gl_string_marker_logger_ops = {
.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

View File

@@ -23,7 +23,7 @@ required_xcb_packages = [
# Some XCB packages are here because their versioning differs (see check below).
required_packages = [
'pixman-1', 'x11', 'x11-xcb', 'xcb-image', 'xcb-renderutil', 'xcb-util',
'xext'
'xext', 'threads',
]
foreach i : required_packages
@@ -58,8 +58,8 @@ if get_option('vsync_drm')
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)]
srcs += [ 'opengl.c' ]
endif

View File

@@ -181,7 +181,7 @@ bool glx_init(session_t *ps, bool need_render) {
// 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
@@ -279,7 +279,7 @@ void glx_destroy(session_t *ps) {
free(ps->psglx);
ps->psglx = NULL;
ps->argb_fbconfig = NULL;
ps->argb_fbconfig = (struct glx_fbconfig_info){0};
}
/**

View File

@@ -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>
@@ -228,10 +228,7 @@ static inline void free_texture(session_t *ps, glx_texture_t **pptex) {
*/
static inline void free_paint_glx(session_t *ps, paint_t *ppaint) {
free_texture(ps, &ppaint->ptex);
#ifdef CONFIG_OPENGL
free(ppaint->fbcfg);
#endif
ppaint->fbcfg = NULL;
ppaint->fbcfg = (struct glx_fbconfig_info){0};
}
/**

View File

@@ -19,6 +19,7 @@
#include <fcntl.h>
#include <inttypes.h>
#include <math.h>
#include <pthread.h>
#include <sched.h>
#include <stddef.h>
#include <stdio.h>
@@ -34,6 +35,7 @@
#include <xcb/randr.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xcb_aux.h>
#include <xcb/xfixes.h>
#include <ev.h>
@@ -209,11 +211,13 @@ collect_vblank_interval_statistics(struct vblank_event *e, void *ud) {
if (frame_count == 1) {
render_statistics_add_vblank_time_sample(
&ps->render_stats, frame_time);
log_trace("Frame count %lu, frame time: %d us, ust: "
"%" PRIu64 "",
log_trace("Frame count %" PRIu64 ", frame time: %d us, "
"ust: "
"%" PRIu64,
frame_count, frame_time, e->ust);
} else {
log_trace("Frame count %lu, frame time: %d us, msc: "
log_trace("Frame count %" PRIu64 ", frame time: %d us, "
"msc: "
"%" PRIu64 ", not adding sample.",
frame_count, frame_time, e->ust);
}
@@ -787,6 +791,8 @@ err:
/// Handle configure event of the root window
static void configure_root(session_t *ps) {
// TODO(yshui) re-initializing backend should be done outside of the
// critical section. Probably set a flag and do it in draw_callback_impl.
auto r = XCB_AWAIT(xcb_get_geometry, ps->c.c, ps->c.screen_info->root);
if (!r) {
log_fatal("Failed to fetch root geometry");
@@ -826,6 +832,13 @@ static void configure_root(session_t *ps) {
top_w->reg_ignore_valid = false;
}
// Whether a window is fullscreen depends on the new screen
// size. So we need to refresh the fullscreen state of all
// windows.
win_stack_foreach_managed(w, &ps->window_stack) {
win_update_is_fullscreen(ps, w);
}
if (ps->redirected) {
for (int i = 0; i < ps->ndamage; i++) {
pixman_region32_clear(&ps->damage_ring[i]);
@@ -1243,7 +1256,7 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
// is not correctly set.
if (ps->o.unredir_if_possible && is_highest) {
if (w->mode == WMODE_SOLID && !ps->o.force_win_blend &&
win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) {
w->is_fullscreen && !w->unredir_if_possible_excluded) {
unredir_possible = true;
}
}
@@ -1358,7 +1371,7 @@ void root_damaged(session_t *ps) {
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);
: x_get_visual_for_depth(ps->c.screen_info, r->depth);
free(r);
ps->root_image = ps->backend_data->ops->bind_pixmap(
@@ -1488,11 +1501,10 @@ static int register_cm(session_t *ps) {
}
// Set COMPTON_VERSION
e = xcb_request_check(
ps->c.c, xcb_change_property_checked(
ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
get_atom(ps->atoms, "COMPTON_VERSION"), XCB_ATOM_STRING, 8,
(uint32_t)strlen(PICOM_VERSION), PICOM_VERSION));
e = xcb_request_check(ps->c.c, xcb_change_property_checked(
ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
ps->atoms->aCOMPTON_VERSION, XCB_ATOM_STRING, 8,
(uint32_t)strlen(PICOM_VERSION), PICOM_VERSION));
if (e) {
log_error_x_error(e, "Failed to set COMPTON_VERSION.");
free(e);
@@ -1508,7 +1520,7 @@ static int register_cm(session_t *ps) {
log_fatal("Failed to allocate memory");
return -1;
}
atom = get_atom(ps->atoms, buf);
atom = get_atom(ps->atoms, buf, ps->c.c);
free(buf);
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(
@@ -1672,7 +1684,7 @@ static bool redirect_start(session_t *ps) {
return false;
}
x_sync(&ps->c);
xcb_aux_sync(ps->c.c);
if (!initialize_backend(ps)) {
return false;
@@ -1731,7 +1743,7 @@ static bool redirect_start(session_t *ps) {
}
// Must call XSync() here
x_sync(&ps->c);
xcb_aux_sync(ps->c.c);
ps->redirected = true;
ps->first_frame = true;
@@ -1774,15 +1786,38 @@ static void unredirect(session_t *ps) {
}
// Must call XSync() here
x_sync(&ps->c);
xcb_aux_sync(ps->c.c);
ps->redirected = false;
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);
}
@@ -1792,13 +1827,6 @@ static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents
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);
@@ -2426,6 +2454,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
c2_list_postprocess(ps, ps->o.opacity_rules) &&
c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
c2_list_postprocess(ps, ps->o.focus_blacklist) &&
c2_list_postprocess(ps, ps->o.corner_radius_rules) &&
c2_list_postprocess(ps, ps->o.animation_blacklist))) {
log_error("Post-processing of conditionals failed, some of your rules "
"might not work");
@@ -2765,21 +2794,24 @@ void set_rr_scheduling(void) {
int ret;
struct sched_param param;
ret = sched_getparam(0, &param);
int old_policy;
ret = pthread_getschedparam(pthread_self(), &old_policy, &param);
if (ret != 0) {
log_debug("Failed to get old scheduling priority");
return;
}
param.sched_priority = priority;
ret = sched_setscheduler(0, SCHED_RR, &param);
ret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
if (ret != 0) {
log_info("Failed to set real-time scheduling priority to %d. Consider "
"giving picom the CAP_SYS_NICE capability",
"giving picom the CAP_SYS_NICE capability or equivalent "
"support.",
priority);
return;
}
log_info("Set real-time scheduling priority to %d", priority);
}
@@ -2796,11 +2828,6 @@ static void session_destroy(session_t *ps) {
unredirect(ps);
}
#ifdef CONFIG_OPENGL
free(ps->argb_fbconfig);
ps->argb_fbconfig = NULL;
#endif
file_watch_destroy(ps->loop, ps->file_watch_handle);
ps->file_watch_handle = NULL;
@@ -2843,6 +2870,7 @@ static void session_destroy(session_t *ps) {
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
@@ -2944,7 +2972,7 @@ static void session_destroy(session_t *ps) {
#endif
// Flush all events
x_sync(&ps->c);
xcb_aux_sync(ps->c.c);
ev_io_stop(ps->loop, &ps->xiow);
if (ps->o.legacy_backends) {
free_conv((conv *)ps->shadow_context);

View File

@@ -6,6 +6,7 @@
#include <xcb/composite.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_renderutil.h>
@@ -55,19 +56,20 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
struct glx_fbconfig_info *fbcfg;
if (!visual) {
assert(depth == 32);
if (!ps->argb_fbconfig) {
ps->argb_fbconfig = glx_find_fbconfig(
&ps->c, (struct xvisual_info){.red_size = 8,
.green_size = 8,
.blue_size = 8,
.alpha_size = 8,
.visual_depth = 32});
if (!ps->argb_fbconfig.cfg) {
glx_find_fbconfig(&ps->c,
(struct xvisual_info){.red_size = 8,
.green_size = 8,
.blue_size = 8,
.alpha_size = 8,
.visual_depth = 32},
&ps->argb_fbconfig);
}
if (!ps->argb_fbconfig) {
if (!ps->argb_fbconfig.cfg) {
log_error("Failed to find appropriate FBConfig for 32 bit depth");
return false;
}
fbcfg = ps->argb_fbconfig;
fbcfg = &ps->argb_fbconfig;
} else {
auto m = x_get_visual_info(&ps->c, visual);
if (m.visual_depth < 0) {
@@ -79,14 +81,14 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
return false;
}
if (!ppaint->fbcfg) {
ppaint->fbcfg = glx_find_fbconfig(&ps->c, m);
if (!ppaint->fbcfg.cfg) {
glx_find_fbconfig(&ps->c, m, &ppaint->fbcfg);
}
if (!ppaint->fbcfg) {
if (!ppaint->fbcfg.cfg) {
log_error("Failed to find appropriate FBConfig for X pixmap");
return false;
}
fbcfg = ppaint->fbcfg;
fbcfg = &ppaint->fbcfg;
}
if (force || !glx_tex_bound(ppaint->ptex, ppaint->pixmap)) {
@@ -623,7 +625,7 @@ static bool get_root_tile(session_t *ps) {
} 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);
: x_get_visual_for_depth(ps->c.screen_info, r->depth);
free(r);
}
@@ -1229,7 +1231,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
if (ps->o.vsync) {
// Make sure all previous requests are processed to achieve best
// effect
x_sync(&ps->c);
xcb_aux_sync(ps->c.c);
#ifdef CONFIG_OPENGL
if (glx_has_context(ps)) {
if (ps->o.vsync_use_glfinish) {
@@ -1288,7 +1290,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
break;
#ifdef CONFIG_OPENGL
case BKEND_XR_GLX_HYBRID:
x_sync(&ps->c);
xcb_aux_sync(ps->c.c);
if (ps->o.vsync_use_glfinish) {
glFinish();
} else {
@@ -1313,7 +1315,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
default: assert(0);
}
x_sync(&ps->c);
xcb_aux_sync(ps->c.c);
#ifdef CONFIG_OPENGL
if (glx_has_context(ps)) {
@@ -1528,7 +1530,7 @@ void deinit_render(session_t *ps) {
free_root_tile(ps);
#ifdef CONFIG_OPENGL
free(ps->root_tile_paint.fbcfg);
ps->root_tile_paint.fbcfg = (struct glx_fbconfig_info){0};
if (bkend_use_glx(ps)) {
glx_destroy(ps);
}

View File

@@ -21,7 +21,7 @@ typedef struct paint {
xcb_render_picture_t pict;
glx_texture_t *ptex;
#ifdef CONFIG_OPENGL
struct glx_fbconfig_info *fbcfg;
struct glx_fbconfig_info fbcfg;
#endif
} paint_t;

View File

@@ -59,11 +59,11 @@ safe_isnan(double a) {
/// being always true or false.
#define ASSERT_IN_RANGE(var, lower, upper) \
do { \
auto __tmp attr_unused = (var); \
auto __assert_in_range_tmp attr_unused = (var); \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
assert(__tmp >= lower); \
assert(__tmp <= upper); \
assert(__assert_in_range_tmp >= lower); \
assert(__assert_in_range_tmp <= upper); \
_Pragma("GCC diagnostic pop"); \
} while (0)
@@ -113,11 +113,26 @@ safe_isnan(double a) {
#define to_u32_checked(val) \
({ \
auto __to_tmp = (val); \
int64_t max attr_unused = UINT32_MAX; /* silence clang tautological \
comparison warning*/ \
ASSERT_IN_RANGE(__to_tmp, 0, max); \
int64_t __to_u32_max attr_unused = UINT32_MAX; /* silence clang \
tautological \
comparison warning */ \
ASSERT_IN_RANGE(__to_tmp, 0, __to_u32_max); \
(uint32_t) __to_tmp; \
})
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) \
({ \
const __typeof__(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); \
})
/**
* Normalize an int value to a specific range.
*

View File

@@ -11,11 +11,11 @@
#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>
#endif
@@ -96,8 +96,6 @@ struct sgi_video_sync_thread_args {
pthread_cond_t start_cnd;
};
static PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
static bool check_sgi_video_sync_extension(Display *dpy, int screen) {
const char *glx_ext = glXQueryExtensionsString(dpy, screen);
const char *needle = "GLX_SGI_video_sync";
@@ -112,11 +110,6 @@ static bool check_sgi_video_sync_extension(Display *dpy, int screen) {
return false;
}
glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)(void *)glXGetProcAddress(
(const GLubyte *)"glXWaitVideoSyncSGI");
if (!glXWaitVideoSyncSGI) {
return false;
}
return true;
}
@@ -334,7 +327,7 @@ sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused
};
sched->base.vblank_event_requested = false;
sched->last_msc = msc;
log_verbose("Received vblank event for msc %lu", event.msc);
log_verbose("Received vblank event for msc %" PRIu64, event.msc);
vblank_scheduler_invoke_callbacks(&sched->base, &event);
}
#endif
@@ -418,7 +411,7 @@ static void handle_present_complete_notify(struct present_vblank_scheduler *self
auto now_us = (unsigned long)(now.tv_sec * 1000000L + now.tv_nsec / 1000);
double delay_sec = 0.0;
if (now_us < cne->ust) {
log_trace("The end of this vblank is %lu us into the "
log_trace("The end of this vblank is %" PRIu64 " us into the "
"future",
cne->ust - now_us);
delay_sec = (double)(cne->ust - now_us) / 1000000.0;
@@ -573,4 +566,4 @@ bool vblank_handle_x_events(struct vblank_scheduler *self) {
return fn(self);
}
return true;
}
}

141
src/win.c
View File

@@ -73,6 +73,11 @@ static void
win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client);
static void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
static void win_update_prop_shadow(session_t *ps, struct managed_win *w);
/**
* Update window EWMH fullscreen state.
*/
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
struct managed_win *w);
/**
* Update leader of a window.
*/
@@ -134,10 +139,10 @@ static inline bool attr_pure win_is_real_visible(const struct managed_win *w) {
* Update focused state of a window.
*/
static void win_update_focused(session_t *ps, struct managed_win *w) {
if (UNSET != w->focused_force) {
if (w->focused_force != UNSET) {
w->focused = w->focused_force;
} else {
w->focused = win_is_focused_raw(ps, w);
w->focused = win_is_focused_raw(w);
// Use wintype_focus, and treat WM windows and override-redirected
// windows specially
@@ -205,7 +210,7 @@ static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
continue;
}
auto mw = (struct managed_win *)w;
if (win_get_leader(ps, mw) == leader && win_is_focused_raw(ps, mw)) {
if (win_get_leader(ps, mw) == leader && win_is_focused_raw(mw)) {
return true;
}
}
@@ -465,6 +470,12 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
win_update_prop_shadow(ps, w);
}
if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_STATE)) {
if (win_update_prop_fullscreen(&ps->c, ps->atoms, w)) {
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
}
}
if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLIENT_LEADER) ||
win_fetch_and_unset_property_stale(w, ps->atoms->aWM_TRANSIENT_FOR)) {
win_update_leader(ps, w);
@@ -645,7 +656,8 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
// Whether the window was visible before we process the mapped flag. i.e.
// is the window just mapped.
bool was_visible = win_is_real_visible(w);
log_trace("Processing flags for window %#010x (%s), was visible: %d, flags: %#lx",
log_trace("Processing flags for window %#010x (%s), was visible: %d, flags: "
"%#" PRIx64,
w->base.id, w->name, was_visible, w->flags);
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
@@ -754,6 +766,9 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
w->g = w->pending_g;
}
// Whether a window is fullscreen changes based on its geometry
win_update_is_fullscreen(ps, w);
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
win_on_win_size_change(ps, w);
win_update_bounding_shape(ps, w);
@@ -889,12 +904,14 @@ int win_update_name(session_t *ps, struct managed_win *w) {
return 0;
}
if (!(wid_get_text_prop(ps, w->client_win, ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
if (!(wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
log_debug("(%#010x): _NET_WM_NAME unset, falling back to "
"WM_NAME.",
w->client_win);
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_NAME, &strlst, &nstr)) {
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
ps->atoms->aWM_NAME, &strlst, &nstr)) {
log_debug("Unsetting window name for %#010x", w->client_win);
free(w->name);
w->name = NULL;
@@ -921,7 +938,8 @@ static int win_update_role(session_t *ps, struct managed_win *w) {
char **strlst = NULL;
int nstr = 0;
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
return -1;
}
@@ -1078,7 +1096,7 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
} else {
// Respect active_opacity only when the window is physically
// focused
if (win_is_focused_raw(ps, w)) {
if (win_is_focused_raw(w)) {
opacity = ps->o.active_opacity;
} else if (!w->focused) {
// Respect inactive_opacity in some cases
@@ -1279,6 +1297,30 @@ void win_update_prop_shadow(session_t *ps, struct managed_win *w) {
}
}
/**
* Update window EWMH fullscreen state.
*/
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
struct managed_win *w) {
auto prop = x_get_prop(c, w->client_win, atoms->a_NET_WM_STATE, 12, XCB_ATOM_ATOM, 0);
if (!prop.nitems) {
return false;
}
bool is_fullscreen = false;
for (uint32_t i = 0; i < prop.nitems; i++) {
if (prop.atom[i] == atoms->a_NET_WM_STATE_FULLSCREEN) {
is_fullscreen = true;
break;
}
}
free_winprop(&prop);
bool changed = w->is_ewmh_fullscreen != is_fullscreen;
w->is_ewmh_fullscreen = is_fullscreen;
return changed;
}
static void win_determine_clip_shadow_above(session_t *ps, struct managed_win *w) {
bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above ||
c2_match(ps, w, ps->o.shadow_clip_list, NULL));
@@ -1419,7 +1461,7 @@ static void win_determine_rounded_corners(session_t *ps, struct managed_win *w)
// Don't round full screen windows & excluded windows,
// unless we find a corner override in corner_radius_rules
if (!radius_override && ((w && win_is_fullscreen(ps, w)) ||
if (!radius_override && ((w && w->is_fullscreen) ||
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL))) {
w->corner_radius = 0;
log_debug("Not rounding corners for window %#010x", w->base.id);
@@ -1486,9 +1528,10 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
*/
void win_on_factor_change(session_t *ps, struct managed_win *w) {
log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
// Focus needs to be updated first, as other rules might depend on the
// focused state of the window
// Focus and is_fullscreen needs to be updated first, as other rules might depend
// on the focused state of the window
win_update_focused(ps, w);
win_update_is_fullscreen(ps, w);
win_determine_shadow(ps, w);
win_determine_clip_shadow_above(ps, w);
@@ -2000,6 +2043,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS,
ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW,
ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR,
ps->atoms->a_NET_WM_STATE,
};
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
@@ -2029,7 +2073,7 @@ static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_wind
// Update the old and new window group and active_leader if the
// window could affect their state.
xcb_window_t cache_leader = win_get_leader(ps, w);
if (win_is_focused_raw(ps, w) && cache_leader_old != cache_leader) {
if (win_is_focused_raw(w) && cache_leader_old != cache_leader) {
ps->active_leader = cache_leader;
group_on_factor_change(ps, cache_leader_old);
@@ -2113,7 +2157,8 @@ bool win_update_class(session_t *ps, struct managed_win *w) {
w->class_general = NULL;
// Retrieve the property string list
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_CLASS, &strlst, &nstr)) {
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win, ps->atoms->aWM_CLASS,
&strlst, &nstr)) {
return false;
}
@@ -2142,7 +2187,7 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) {
xcb_window_t leader = win_get_leader(ps, w);
// If the window gets focused, replace the old active_leader
if (win_is_focused_raw(ps, w) && leader != ps->active_leader) {
if (win_is_focused_raw(w) && leader != ps->active_leader) {
xcb_window_t active_leader_old = ps->active_leader;
ps->active_leader = leader;
@@ -2151,7 +2196,7 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) {
group_on_factor_change(ps, leader);
}
// If the group get unfocused, remove it from active_leader
else if (!win_is_focused_raw(ps, w) && leader &&
else if (!win_is_focused_raw(w) && leader &&
leader == ps->active_leader && !group_is_focused(ps, leader)) {
ps->active_leader = XCB_NONE;
group_on_factor_change(ps, leader);
@@ -2164,7 +2209,7 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) {
#ifdef CONFIG_DBUS
// Send D-Bus signal
if (ps->o.dbus) {
if (win_is_focused_raw(ps, w)) {
if (win_is_focused_raw(w)) {
cdbus_ev_win_focusin(ps, &w->base);
} else {
cdbus_ev_win_focusout(ps, &w->base);
@@ -2182,15 +2227,18 @@ void win_set_focused(session_t *ps, struct managed_win *w) {
return;
}
if (win_is_focused_raw(ps, w)) {
if (w->is_ewmh_focused) {
assert(ps->active_win == w);
return;
}
auto old_active_win = ps->active_win;
ps->active_win = w;
assert(win_is_focused_raw(ps, w));
w->is_ewmh_focused = true;
if (old_active_win) {
assert(old_active_win->is_ewmh_focused);
old_active_win->is_ewmh_focused = false;
win_on_focus_change(ps, old_active_win);
}
win_on_focus_change(ps, w);
@@ -2283,14 +2331,12 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
// Window shape changed, we should free old wpaint and shadow pict
// log_trace("free out dated pict");
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
win_set_flags(w, WIN_FLAGS_IMAGES_STALE | WIN_FLAGS_FACTOR_CHANGED);
win_release_mask(ps->backend_data, w);
ps->pending_updates = true;
free_paint(ps, &w->paint);
free_paint(ps, &w->shadow_paint);
win_on_factor_change(ps, w);
}
/**
@@ -3027,41 +3073,6 @@ struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wi
return (struct managed_win *)w;
}
/**
* Check if a rectangle includes the whole screen.
*/
static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid, int hei) {
return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height);
}
/**
* Check if a window is full-screen using EWMH
*
* TODO(yshui) cache this property
*/
static inline bool
win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) {
xcb_get_property_cookie_t prop =
xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12);
xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL);
if (!reply) {
return false;
}
if (reply->length) {
xcb_atom_t *val = xcb_get_property_value(reply);
for (uint32_t i = 0; i < reply->length; i++) {
if (val[i] != a->a_NET_WM_STATE_FULLSCREEN) {
continue;
}
free(reply);
return true;
}
}
free(reply);
return false;
}
/// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags) {
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
@@ -3146,13 +3157,15 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
*
* It's not using w->border_size for performance measures.
*/
bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) {
if (!ps->o.no_ewmh_fullscreen &&
win_is_fullscreen_xcb(ps->c.c, ps->atoms, w->client_win)) {
return true;
void win_update_is_fullscreen(const session_t *ps, struct managed_win *w) {
if (!ps->o.no_ewmh_fullscreen && w->is_ewmh_fullscreen) {
w->is_fullscreen = true;
return;
}
return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) &&
(!w->bounding_shaped || w->rounded_corners);
w->is_fullscreen = w->g.x <= 0 && w->g.y <= 0 &&
(w->g.x + w->widthb) >= ps->root_width &&
(w->g.y + w->heightb) >= ps->root_height &&
(!w->bounding_shaped || w->rounded_corners);
}
/**
@@ -3178,8 +3191,8 @@ bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *
* Check if a window is focused, without using any focus rules or forced focus
* settings
*/
bool win_is_focused_raw(const session_t *ps, const struct managed_win *w) {
return w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->active_win == w;
bool win_is_focused_raw(const struct managed_win *w) {
return w->a.map_state == XCB_MAP_STATE_VIEWABLE && w->is_ewmh_focused;
}
// Find the managed window immediately below `i` in the window stack

View File

@@ -113,10 +113,10 @@ struct managed_win {
struct win base;
/// backend data attached to this window. Only available when
/// `state` is not UNMAPPED
void *win_image;
void *old_win_image; // Old window image for interpolating window contents during animations
void *shadow_image;
void *mask_image;
image_handle win_image;
image_handle old_win_image; // Old window image for interpolating window contents during animations
image_handle shadow_image;
image_handle mask_image;
/// Pointer to the next higher window to paint.
struct managed_win *prev_trans;
/// Number of windows above this window
@@ -233,6 +233,14 @@ struct managed_win {
char *class_general;
/// <code>WM_WINDOW_ROLE</code> value of the window.
char *role;
/// Whether the window sets the EWMH fullscreen property.
bool is_ewmh_fullscreen;
/// Whether the window should be considered fullscreen. Based on
/// `is_ewmh_fullscreen`, or the windows spatial relation with the
/// root window. Which one is used is determined by user configuration.
bool is_fullscreen;
/// Whether the window is the EWMH active window.
bool is_ewmh_focused;
// Opacity-related members
/// Current window opacity.
@@ -383,6 +391,8 @@ void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw);
*/
// XXX was win_border_size
void win_update_bounding_shape(session_t *ps, struct managed_win *w);
/// Recheck if a window is fullscreen
void win_update_is_fullscreen(const session_t *ps, struct managed_win *w);
/**
* Check if a window has BYPASS_COMPOSITOR property set
*/
@@ -458,17 +468,10 @@ struct managed_win *find_toplevel(session_t *ps, xcb_window_t id);
*/
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid);
/**
* Check if a window is a fullscreen window.
*
* It's not using w->border_size for performance measures.
*/
bool attr_pure win_is_fullscreen(const session_t *ps, const struct managed_win *w);
/**
* Check if a window is focused, without using any focus rules or forced focus settings
*/
bool attr_pure win_is_focused_raw(const session_t *ps, const struct managed_win *w);
bool attr_pure win_is_focused_raw(const struct managed_win *w);
/// check if window has ARGB visual
bool attr_pure win_has_alpha(const struct managed_win *w);

96
src/x.c
View File

@@ -16,6 +16,7 @@
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_renderutil.h>
#include <xcb/xfixes.h>
@@ -94,7 +95,7 @@ void x_connection_init(struct x_connection *c, Display *dpy) {
c->previous_xerror_handler = XSetErrorHandler(xerror);
c->screen = DefaultScreen(dpy);
c->screen_info = x_screen_of_display(c, c->screen);
c->screen_info = xcb_aux_get_screen(c->c, c->screen);
}
/**
@@ -181,10 +182,9 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
/**
* Get the value of a text property of a window.
*/
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
int *pnstr) {
assert(ps->server_grabbed);
auto prop_info = x_get_prop_info(&ps->c, wid, prop);
bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
xcb_atom_t prop, char ***pstrlst, int *pnstr) {
auto prop_info = x_get_prop_info(c, wid, prop);
auto type = prop_info.type;
auto format = prop_info.format;
auto length = prop_info.length;
@@ -193,8 +193,7 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
return false;
}
if (type != XCB_ATOM_STRING && type != ps->atoms->aUTF8_STRING &&
type != ps->atoms->aC_STRING) {
if (type != XCB_ATOM_STRING && type != atoms->aUTF8_STRING && type != atoms->aC_STRING) {
log_warn("Text property %d of window %#010x has unsupported type: %d",
prop, wid, type);
return false;
@@ -209,7 +208,7 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
xcb_generic_error_t *e = NULL;
auto word_count = (length + 4 - 1) / 4;
auto r = xcb_get_property_reply(
ps->c.c, xcb_get_property(ps->c.c, 0, wid, prop, type, 0, word_count), &e);
c->c, xcb_get_property(c->c, 0, wid, prop, type, 0, word_count), &e);
if (!r) {
log_debug_x_error(e, "Failed to get window property for %#010x", wid);
free(e);
@@ -321,15 +320,11 @@ xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standa
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;
}
xcb_visualid_t x_get_visual_for_depth(xcb_screen_t *screen, uint8_t depth) {
xcb_depth_iterator_t depth_it = xcb_screen_allowed_depths_iterator(screen);
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;
}
}
@@ -345,24 +340,6 @@ x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
return pictfmt->id;
}
int x_get_visual_depth(struct x_connection *c, xcb_visualid_t visual) {
auto setup = xcb_get_setup(c->c);
for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
xcb_screen_next(&screen)) {
for (auto depth = xcb_screen_allowed_depths_iterator(screen.data);
depth.rem; xcb_depth_next(&depth)) {
const int len = xcb_depth_visuals_length(depth.data);
const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
for (int i = 0; i < len; i++) {
if (visual == visuals[i].visual_id) {
return depth.data->depth;
}
}
}
}
return -1;
}
xcb_render_picture_t
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *c,
const xcb_render_pictforminfo_t *pictfmt,
@@ -474,6 +451,34 @@ bool x_fetch_region(struct x_connection *c, xcb_xfixes_region_t r, pixman_region
return ret;
}
bool x_set_region(struct x_connection *c, xcb_xfixes_region_t dst, const region_t *src) {
if (!src || dst == XCB_NONE) {
return false;
}
int32_t nrects = 0;
const rect_t *rects = pixman_region32_rectangles((region_t *)src, &nrects);
if (!rects || nrects < 1) {
return false;
}
xcb_rectangle_t *xrects = ccalloc(nrects, xcb_rectangle_t);
for (int32_t i = 0; i < nrects; i++) {
xrects[i] =
(xcb_rectangle_t){.x = to_i16_checked(rects[i].x1),
.y = to_i16_checked(rects[i].y1),
.width = to_u16_checked(rects[i].x2 - rects[i].x1),
.height = to_u16_checked(rects[i].y2 - rects[i].y1)};
}
bool success =
XCB_AWAIT_VOID(xcb_xfixes_set_region, c->c, dst, to_u32_checked(nrects), xrects);
free(xrects);
return success;
}
uint32_t x_create_region(struct x_connection *c, const region_t *reg) {
if (!reg) {
return XCB_NONE;
@@ -577,9 +582,7 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c
const char *name = "Unknown";
#define CASESTRRET(s) \
case s: \
name = #s; \
break
case s: name = #s; break
#define CASESTRRET2(s) \
case XCB_##s: name = #s; break
@@ -850,8 +853,8 @@ void x_create_convolution_kernel(const conv *kernel, double center,
/// Returns {-1, -1, -1, -1, -1, 0} on failure
struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t visual) {
auto pictfmt = x_get_pictform_for_visual(c, visual);
auto depth = x_get_visual_depth(c, visual);
if (!pictfmt || depth == -1) {
auto depth = xcb_aux_get_depth_of_visual(c->screen_info, visual);
if (!pictfmt || depth == 0) {
log_error("Invalid visual %#03x", visual);
return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
}
@@ -876,19 +879,6 @@ struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t vis
};
}
xcb_screen_t *x_screen_of_display(struct x_connection *c, int screen) {
xcb_screen_iterator_t iter;
iter = xcb_setup_roots_iterator(xcb_get_setup(c->c));
for (; iter.rem; --screen, xcb_screen_next(&iter)) {
if (screen == 0) {
return iter.data;
}
}
return NULL;
}
void x_update_monitors(struct x_connection *c, struct x_monitors *m) {
x_free_monitor_info(m);

24
src/x.h
View File

@@ -27,6 +27,7 @@ typedef struct winprop {
int16_t *p16;
int32_t *p32;
uint32_t *c32; // 32bit cardinal
xcb_atom_t *atom;
};
unsigned long nitems;
xcb_atom_t type;
@@ -207,17 +208,6 @@ void x_discard_pending(struct x_connection *c, uint32_t sequence);
/// This function logs X errors, or aborts the program based on severity of the error.
void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev);
/**
* Send a request to X server and get the reply to make sure all previous
* requests are processed, and their replies received
*
* xcb_get_input_focus is used here because it is the same request used by
* libX11
*/
static inline void x_sync(struct x_connection *c) {
free(xcb_get_input_focus_reply(c->c, xcb_get_input_focus(c->c), NULL));
}
/**
* Get a specific attribute of a window.
*
@@ -271,12 +261,11 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
* array
* @param[out] pnstr Number of strings in the array
*/
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
int *pnstr);
bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
xcb_atom_t prop, char ***pstrlst, int *pnstr);
const xcb_render_pictforminfo_t *
x_get_pictform_for_visual(struct x_connection *, xcb_visualid_t);
int x_get_visual_depth(struct x_connection *, xcb_visualid_t);
xcb_render_picture_t
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *,
@@ -321,6 +310,9 @@ x_create_picture_with_visual(struct x_connection *, int w, int h, xcb_visualid_t
/// Fetch a X region and store it in a pixman region
bool x_fetch_region(struct x_connection *, xcb_xfixes_region_t r, region_t *res);
/// Set an X region to a pixman region
bool x_set_region(struct x_connection *c, xcb_xfixes_region_t dst, const region_t *src);
/// Create a X region from a pixman region
uint32_t x_create_region(struct x_connection *c, const region_t *reg);
@@ -406,13 +398,11 @@ struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t vis
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_visualid_t x_get_visual_for_depth(xcb_screen_t *screen, uint8_t depth);
xcb_render_pictformat_t
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std);
xcb_screen_t *x_screen_of_display(struct x_connection *c, int screen);
/// Populates a `struct x_monitors` with the current monitor configuration.
void x_update_monitors(struct x_connection *, struct x_monitors *);
/// Free memory allocated for a `struct x_monitors`.

View File

@@ -58,6 +58,14 @@ struct test_file_metadata __attribute__((weak)) * test_file_head;
} \
} while (0)
#define TEST_NOTEQUAL(a, b) \
do { \
if ((a) == (b)) { \
SET_FAILURE(#a " == " #b, false); \
return; \
} \
} while (0)
#define TEST_TRUE(a) \
do { \
if (!(a)) { \
@@ -69,11 +77,13 @@ struct test_file_metadata __attribute__((weak)) * test_file_head;
#define TEST_STREQUAL(a, b) \
do { \
if (strcmp(a, b) != 0) { \
const char *part2 = " != " #b; \
size_t len = strlen(a) + strlen(part2) + 3; \
char *buf = malloc(len); \
snprintf(buf, len, "\"%s\"%s", a, part2); \
SET_FAILURE(buf, true); \
const char *test_strequal__part2 = " != " #b; \
size_t test_strequal__len = \
strlen(a) + strlen(test_strequal__part2) + 3; \
char *test_strequal__buf = malloc(test_strequal__len); \
snprintf(test_strequal__buf, test_strequal__len, "\"%s\"%s", a, \
test_strequal__part2); \
SET_FAILURE(test_strequal__buf, true); \
return; \
} \
} while (0)
@@ -81,11 +91,27 @@ struct test_file_metadata __attribute__((weak)) * test_file_head;
#define TEST_STRNEQUAL(a, b, len) \
do { \
if (strncmp(a, b, len) != 0) { \
const char *part2 = " != " #b; \
size_t len2 = len + strlen(part2) + 3; \
char *buf = malloc(len2); \
snprintf(buf, len2, "\"%.*s\"%s", (int)len, a, part2); \
SET_FAILURE(buf, true); \
const char *test_strnequal__part2 = " != " #b; \
size_t test_strnequal__len2 = \
len + strlen(test_strnequal__part2) + 3; \
char *test_strnequal__buf = malloc(test_strnequal__len2); \
snprintf(test_strnequal__buf, test_strnequal__len2, \
"\"%.*s\"%s", (int)len, a, test_strnequal__part2); \
SET_FAILURE(test_strnequal__buf, true); \
return; \
} \
} while (0)
#define TEST_STREQUAL3(str, expected, len) \
do { \
if (len != strlen(expected) || strncmp(str, expected, len) != 0) { \
const char *test_strequal3__part2 = " != " #expected; \
size_t test_strequal3__len2 = \
len + strlen(test_strequal3__part2) + 3; \
char *test_strequal3__buf = malloc(test_strequal3__len2); \
snprintf(test_strequal3__buf, test_strequal3__len2, \
"\"%.*s\"%s", (int)len, str, test_strequal3__part2); \
SET_FAILURE(test_strequal3__buf, true); \
return; \
} \
} while (0)
@@ -199,6 +225,9 @@ static inline void __attribute__((constructor(102))) run_tests(void) {
#define TEST_EQUAL(a, b) \
(void)(a); \
(void)(b)
#define TEST_NOTEQUAL(a, b) \
(void)(a); \
(void)(b)
#define TEST_TRUE(a) (void)(a)
#define TEST_STREQUAL(a, b) \
(void)(a); \
@@ -207,5 +236,8 @@ static inline void __attribute__((constructor(102))) run_tests(void) {
(void)(a); \
(void)(b); \
(void)(len)
#define TEST_STREQUAL3(str, expected, len) \
(void)(str); \
(void)(expected); \
(void)(len)
#endif