diff --git a/CHANGELOG.md b/CHANGELOG.md index d74a484cec..aeac25003b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,12 +26,14 @@ ! fullscreen border_width = 0 ``` +* picom now has a rudimentary plugin system. At the moment, the only thing you can do with it is loading custom backends. ## Notable changes * `override_redirect` in rules now only matches top-level windows that doesn't have a client window. Some window managers (e.g. awesome) set override_redirect for all window manager frame windows, causing this rule to match against everything (#625). * Marginally improve performance when resizing/opening/closing windows. (#1190) * Type and format specifiers are no longer used in rules. These specifiers are what you put after the colon (':') in rules, e.g. the `:32c` in `"_GTK_FRAME_EXTENTS@:32c"`. Now this information is ignored and the property is matched regardless of format or type. +* `backend` is now a required option. picom will not start if one is not specified explicitly. ## Deprecated features diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000000..419437e7dc --- /dev/null +++ b/include/meson.build @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) Yuxuan Shui +subdirs('picom') diff --git a/include/picom/api.h b/include/picom/api.h new file mode 100644 index 0000000000..719fd09a80 --- /dev/null +++ b/include/picom/api.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#pragma once + +#include +#include + +#define PICOM_API_MAJOR (0UL) +#define PICOM_API_MINOR (1UL) + +struct backend_base; + +/// The entry point of a backend plugin. Called after the backend is initialized. +typedef void (*picom_backend_plugin_entrypoint)(struct backend_base *backend, void *user_data); +struct picom_api { + /// Add a plugin for a specific backend. The plugin's entry point will be called + /// when the specified backend is initialized. + /// + /// @param backend_name The name of the backend to add the plugin to. + /// @param major The major version of the backend API interface this plugin + /// is compatible with. + /// @param minor The minor version of the backend API interface this plugin + /// is compatible with. + /// @param entrypoint The entry point of the plugin. + /// @param user_data The user data to pass to the plugin's entry point. + bool (*add_backend_plugin)(const char *backend_name, uint64_t major, uint64_t minor, + picom_backend_plugin_entrypoint entrypoint, + void *user_data); +}; + +const struct picom_api * +picom_api_get_interfaces(uint64_t major, uint64_t minor, const char *context); diff --git a/include/picom/backend.h b/include/picom/backend.h new file mode 100644 index 0000000000..74e6f1e4af --- /dev/null +++ b/include/picom/backend.h @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#pragma once + +#include +#include +#include + +#include "types.h" + +#define PICOM_BACKEND_MAJOR (1UL) +#define PICOM_BACKEND_MINOR (0UL) +#define PICOM_BACKEND_MAKE_VERSION(major, minor) ((major) * 1000 + (minor)) + +typedef pixman_region32_t region_t; + +struct xvisual_info { + /// Bit depth of the red component + int red_size; + /// Bit depth of the green component + int green_size; + /// Bit depth of the blue component + int blue_size; + /// Bit depth of the alpha component + int alpha_size; + /// The depth of X visual + int visual_depth; + + xcb_visualid_t visual; +}; + +typedef struct session session_t; +struct managed_win; + +struct ev_loop; +struct backend_operations; + +typedef struct backend_base backend_t; + +// This mimics OpenGL's ARB_robustness extension, which enables detection of GPU context +// resets. +// See: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_robustness.txt, section +// 2.6 "Graphics Reset Recovery". +enum device_status { + DEVICE_STATUS_NORMAL, + DEVICE_STATUS_RESETTING, +}; + +enum shader_attributes { + // Whether the shader needs to be render regardless of whether the window is + // updated. + SHADER_ATTRIBUTE_ANIMATED = 1, +}; + +struct gaussian_blur_args { + int size; + double deviation; +}; + +struct box_blur_args { + int size; +}; + +struct kernel_blur_args { + struct conv **kernels; + int kernel_count; +}; + +struct dual_kawase_blur_args { + int size; + int strength; +}; + +typedef struct image_handle { + // Intentionally left blank +} *image_handle; + +/// A mask for various backend operations. +/// +/// The mask is composed of both a mask region and a mask image. The resulting mask +/// is the intersection of the two. The mask image can be modified by the `corner_radius` +/// and `inverted` properties. Note these properties have no effect on the mask region. +struct backend_mask_image { + /// Mask image, can be NULL. + /// + /// Mask image must be an image that was created with the + /// `BACKEND_IMAGE_FORMAT_MASK` format. Using an image with a wrong format as mask + /// is undefined behavior. + image_handle image; + /// Corner radius of the mask image, the corners of the mask image will be + /// rounded. + double corner_radius; + /// Origin of the mask image, in the source image's coordinate. + ivec2 origin; + /// Whether the mask image should be inverted. + bool inverted; +}; + +struct backend_blur_args { + /// The blur context + void *blur_context; + /// The source mask for the blur operation, may be NULL. Only parts of the source + /// image covered by the mask should participate in the blur operation. + const struct backend_mask_image *source_mask; + /// Region of the target image that will be covered by the blur operation, in the + /// source image's coordinate. + const region_t *target_mask; + /// Source image + image_handle source_image; + /// Opacity of the blurred image + double opacity; +}; + +struct backend_blit_args { + /// Source image, can be NULL. + image_handle source_image; + /// Mask for the source image. may be NULL. Only contents covered by the mask + /// should participate in the blit operation. This applies to the source image + /// before it's scaled. + const struct backend_mask_image *source_mask; + /// Mask for the target image. Only regions of the target image covered by this + /// mask should be modified. This is the target's coordinate system. + const region_t *target_mask; + /// Custom shader for this blit operation. + void *shader; + /// Opacity of the source image. + double opacity; + /// Dim level of the source image. + double dim; + /// Brightness limit of the source image. Source image + /// will be normalized so that the maximum brightness is + /// this value. + double max_brightness; + /// Scale factor for the horizontal and vertical direction (X for horizontal, + /// Y for vertical). + vec2 scale; + /// Corner radius of the source image BEFORE scaling. The corners of + /// the source image will be rounded. + double corner_radius; + /// Effective size of the source image BEFORE scaling, set where the corners + /// of the image are. + ivec2 effective_size; + /// Border width of the source image BEFORE scaling. This is used with + /// `corner_radius` to create a border for the rounded corners. + /// Setting this has no effect if `corner_radius` is 0. + int border_width; + /// Whether the source image should be inverted. + bool color_inverted; +}; + +enum backend_image_format { + /// A format that can be used for normal rendering, and binding + /// X pixmaps. + /// Images created with `bind_pixmap` have this format automatically. + BACKEND_IMAGE_FORMAT_PIXMAP, + /// Like `BACKEND_IMAGE_FORMAT_PIXMAP`, but the image has a higher + /// precision. Support is optional. + BACKEND_IMAGE_FORMAT_PIXMAP_HIGH, + /// A format that can be used for masks. + BACKEND_IMAGE_FORMAT_MASK, +}; + +enum backend_image_capability { + /// Image can be sampled from. This is required for `blit` and `blur` source + /// images. All images except the back buffer should have this capability. + /// Note that `copy_area` should work without this capability, this is so that + /// blurring the back buffer could be done. + BACKEND_IMAGE_CAP_SRC = 1 << 0, + /// Image can be rendered to. This is required for target images of any operation. + /// All images except bound X pixmaps should have this capability. + BACKEND_IMAGE_CAP_DST = 1 << 1, +}; + +enum backend_command_op { + BACKEND_COMMAND_INVALID = -1, + BACKEND_COMMAND_BLIT, + BACKEND_COMMAND_BLUR, + BACKEND_COMMAND_COPY_AREA, +}; + +/// Symbolic references used as render command source images. The actual `image_handle` +/// will later be filled in by the renderer using this symbolic reference. +enum backend_command_source { + BACKEND_COMMAND_SOURCE_WINDOW, + BACKEND_COMMAND_SOURCE_SHADOW, + BACKEND_COMMAND_SOURCE_BACKGROUND, +}; + +// TODO(yshui) might need better names + +struct backend_command { + enum backend_command_op op; + ivec2 origin; + enum backend_command_source source; + union { + struct { + struct backend_blit_args blit; + /// Region of the screen that will be covered by this blit + /// operations, in screen coordinates. + region_t opaque_region; + }; + struct { + image_handle source_image; + const region_t *region; + } copy_area; + struct backend_blur_args blur; + }; + /// Source mask for the operation. + /// If the `source_mask` of the operation's argument points to this, a mask image + /// will be created for the operation for the renderer. + struct backend_mask_image source_mask; + /// Target mask for the operation. + region_t target_mask; +}; + +enum backend_quirk { + /// Backend cannot do blur quickly. The compositor will avoid using blur to create + /// shadows on this backend + BACKEND_QUIRK_SLOW_BLUR = 1 << 0, +}; + +struct backend_operations { + // =========== Initialization =========== + + /// Initialize the backend, prepare for rendering to the target window. + backend_t *(*init)(session_t *, xcb_window_t) __attribute__((nonnull(1))); + void (*deinit)(backend_t *backend_data) __attribute__((nonnull(1))); + + /// Called when rendering will be stopped for an unknown amount of + /// time (e.g. when screen is unredirected). Free some resources. + /// + /// Optional, not yet used + void (*pause)(backend_t *backend_data, session_t *ps); + + /// Called before rendering is resumed + /// + /// Optional, not yet used + void (*resume)(backend_t *backend_data, session_t *ps); + + /// Called when root window size changed. All existing image data ever + /// returned by this backend should remain valid after this call + /// returns. + /// + /// Optional + void (*root_change)(backend_t *backend_data, session_t *ps); + + // =========== Rendering ============ + + /// Called before when a new frame starts. + /// + /// Optional + void (*prepare)(backend_t *backend_data, const region_t *reg_damage); + + /// Multiply the alpha channel of the target image by a given value. + /// + /// @param backend_data backend data + /// @param target an image handle, cannot be NULL. + /// @param alpha the alpha value to multiply + /// @param region the region to apply the alpha, in the target image's + /// coordinate. + bool (*apply_alpha)(struct backend_base *backend_data, image_handle target, + double alpha, const region_t *region) + __attribute__((nonnull(1, 2, 4))); + + /// Copy pixels from a source image on to the target image. + /// + /// Some effects may be applied. If the region specified by the mask + /// contains parts that are outside the source image, the source image + /// will be repeated to fit. + /// + /// Source and target MUST NOT be the same image. + /// + /// @param backend_data backend data + /// @param origin the origin of the operation, in the target image's + /// coordinate. + /// @param target an image handle, cannot be NULL. + /// @param args arguments for blit + /// @return whether the operation is successful + bool (*blit)(struct backend_base *backend_data, ivec2 origin, image_handle target, + const struct backend_blit_args *args) __attribute__((nonnull(1, 3, 4))); + + /// Blur a given region of a source image and store the result in the + /// target image. + /// + /// The blur operation might access pixels outside the mask region, the + /// amount of pixels accessed can be queried with `get_blur_size`. If + /// pixels outside the source image are accessed, the result will be + /// clamped to the edge of the source image. + /// + /// Source and target may be the same image. + /// + /// @param backend_data backend data + /// @param origin the origin of the operation, in the target image's + /// coordinate. + /// @param target an image handle, cannot be NULL. + /// @param args argument for blur + /// @return whether the operation is successful + bool (*blur)(struct backend_base *backend_data, ivec2 origin, image_handle target, + const struct backend_blur_args *args) __attribute__((nonnull(1, 3, 4))); + + /// Direct copy of pixels from a source image on to the target image. + /// This is a simpler version of `blit`, without any effects. Note unlike `blit`, + /// if `region` tries to sample from outside the source image, instead of + /// repeating, the result will be clamped to the edge of the source image. + /// Blending should not be applied for the copy. + /// + /// Source and target MUST NOT be the same image. + /// + /// @param backend_data backend data + /// @param origin the origin of the operation, in the target image's + /// coordinate. + /// @param target an image handle, cannot be NULL. + /// @param source an image handle, cannot be NULL. + /// @param region the region to copy, in the target image's coordinate. + /// @return whether the operation is successful + bool (*copy_area)(struct backend_base *backend_data, ivec2 origin, + image_handle target, image_handle source, const region_t *region) + __attribute__((nonnull(1, 3, 4, 5))); + + /// Similar to `copy_area`, but is specialized for copying from a higher + /// precision format to a lower precision format. It has 2 major differences from + /// `copy_area`: + /// + /// 1. This function _may_ use dithering when copying from a higher precision + /// format to a lower precision format. But this is not required. + /// 2. This function only needs to support copying from an image with the SRC + /// capability. Unlike `copy_area`, which supports copying from any image. + /// + /// It's perfectly legal to have this pointing to the same function as + /// `copy_area`, if the backend doesn't support dithering. + /// + /// @param backend_data backend data + /// @param origin the origin of the operation, in the target image's + /// coordinate. + /// @param target an image handle, cannot be NULL. + /// @param source an image handle, cannot be NULL. + /// @param region the region to copy, in the target image's coordinate. + /// @return whether the operation is successful + bool (*copy_area_quantize)(struct backend_base *backend_data, ivec2 origin, + image_handle target, image_handle source, + const region_t *region) + __attribute__((nonnull(1, 3, 4, 5))); + + /// Initialize an image with a given color value. If the image has a mask format, + /// only the alpha channel of the color is used. + /// + /// @param backend_data backend data + /// @param target an image handle, cannot be NULL. + /// @param color the color to fill the image with + /// @return whether the operation is successful + bool (*clear)(struct backend_base *backend_data, image_handle target, + struct color color) __attribute__((nonnull(1, 2))); + + /// Present the back buffer to the target window. Ideally the backend should keep + /// track of the region of the back buffer that has been updated, and use relevant + /// mechanism (when possible) to present only the updated region. + bool (*present)(struct backend_base *backend_data) __attribute__((nonnull(1))); + + // ============ Resource management =========== + + /// Create a shader object from a shader source. + /// + /// Optional + void *(*create_shader)(backend_t *backend_data, const char *source) + __attribute__((nonnull(1, 2))); + + /// Free a shader object. + /// + /// Required if create_shader is present. + void (*destroy_shader)(backend_t *backend_data, void *shader) + __attribute__((nonnull(1, 2))); + + /// Create a new, uninitialized image with the given format and size. + /// + /// @param backend_data backend data + /// @param format the format of the image + /// @param size the size of the image + image_handle (*new_image)(struct backend_base *backend_data, + enum backend_image_format format, ivec2 size) + __attribute__((nonnull(1))); + + /// 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 + /// @return backend specific image handle for the pixmap. May be + /// NULL. + image_handle (*bind_pixmap)(struct backend_base *backend_data, xcb_pixmap_t pixmap, + struct xvisual_info fmt) __attribute__((nonnull(1))); + + /// Acquire the image handle of the back buffer. + /// + /// @param backend_data backend data + image_handle (*back_buffer)(struct backend_base *backend_data); + + /// Free resources associated with an image data structure. Releasing the image + /// returned by `back_buffer` should be a no-op. + /// + /// @param image the image to be released, cannot be NULL. + /// @return if this image is created by `bind_pixmap`, the X pixmap; 0 + /// otherwise. + xcb_pixmap_t (*release_image)(struct backend_base *backend_data, image_handle image) + __attribute__((nonnull(1, 2))); + + // =========== Query =========== + + /// Get backend quirks + /// @return a bitmask of `enum backend_quirk`. + uint32_t (*quirks)(struct backend_base *backend_data) __attribute__((nonnull(1))); + + /// Get the version of the backend + void (*version)(struct backend_base *backend_data, uint64_t *major, uint64_t *minor) + __attribute__((nonnull(1, 2, 3))); + + /// Check if an optional image format is supported by the backend. + bool (*is_format_supported)(struct backend_base *backend_data, + enum backend_image_format format) + __attribute__((nonnull(1))); + + /// Return the capabilities of an image. + uint32_t (*image_capabilities)(struct backend_base *backend_data, image_handle image) + __attribute__((nonnull(1, 2))); + + /// Get the attributes of a shader. + /// + /// Optional, Returns a bitmask of attributes, see `shader_attributes`. + uint64_t (*get_shader_attributes)(backend_t *backend_data, void *shader) + __attribute__((nonnull(1, 2))); + + /// Get the age of the buffer content we are currently rendering on top + /// of. The buffer that has just been `present`ed has a buffer age of 1. + /// Every time `present` is called, buffers get older. Return -1 if the + /// buffer is empty. + /// + /// Optional + int (*buffer_age)(backend_t *backend_data); + + /// Get the render time of the last frame. If the render is still in progress, + /// returns false. The time is returned in `ts`. Frames are delimited by the + /// present() calls. i.e. after a present() call, last_render_time() should start + /// reporting the time of the just presented frame. + /// + /// Optional, if not available, the most conservative estimation will be used. + bool (*last_render_time)(backend_t *backend_data, struct timespec *ts); + + /// The maximum number buffer_age might return. + int (*max_buffer_age)(backend_t *backend_data); + + // =========== Post-processing ============ + /// Create a blur context that can be used to call `blur` for images with a + /// specific format. + void *(*create_blur_context)(backend_t *base, enum blur_method, + enum backend_image_format format, void *args); + /// Destroy a blur context + void (*destroy_blur_context)(backend_t *base, void *ctx); + /// Get how many pixels outside of the blur area is needed for blur + void (*get_blur_size)(void *blur_context, int *width, int *height); + + // =========== Misc ============ + /// Return the driver that is been used by the backend + enum driver (*detect_driver)(backend_t *backend_data); + + void (*diagnostics)(backend_t *backend_data); + + enum device_status (*device_status)(backend_t *backend_data); +}; + +struct backend_base { + struct backend_operations ops; + struct x_connection *c; + struct ev_loop *loop; + + /// Whether the backend can accept new render request at the moment + bool busy; + // ... +}; + +/// Register a new backend, `major` and `minor` should be the version of the picom backend +/// interface. You should just pass `PICOM_BACKEND_MAJOR` and `PICOM_BACKEND_MINOR` here. +/// `name` is the name of the backend, `init` is the function to initialize the backend, +/// `can_present` should be true if the backend can present the back buffer to the screen, +/// false otherwise (e.g. if the backend does off screen rendering, etc.) +bool backend_register(uint64_t major, uint64_t minor, const char *name, + struct backend_base *(*init)(session_t *ps, xcb_window_t target), + bool can_present); + +/// Define a backend entry point. (Note constructor priority 202 is used here because 1xx +/// is reversed by test.h, and 201 is used for logging initialization.) +#define BACKEND_ENTRYPOINT(func) static void __attribute__((constructor(202))) func(void) diff --git a/include/picom/meson.build b/include/picom/meson.build new file mode 100644 index 0000000000..71727c41ad --- /dev/null +++ b/include/picom/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: MPL-2.0 +# Copyright (c) Yuxuan Shui + +api_headers = [ + 'api.h' + 'backend.h' +] +install_headers(api_headers, subdir: 'picom') diff --git a/src/types.h b/include/picom/types.h similarity index 94% rename from src/types.h rename to include/picom/types.h index 1566aa8ea8..8412fb169f 100644 --- a/src/types.h +++ b/include/picom/types.h @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -// Copyright (c) 2018 Yuxuan Shui +// Copyright (c) Yuxuan Shui #pragma once @@ -10,6 +10,15 @@ #include #include +enum blur_method { + BLUR_METHOD_NONE = 0, + BLUR_METHOD_KERNEL, + BLUR_METHOD_BOX, + BLUR_METHOD_GAUSSIAN, + BLUR_METHOD_DUAL_KAWASE, + BLUR_METHOD_INVALID, +}; + /// Enumeration type to represent switches. typedef enum { OFF = 0, // false diff --git a/man/picom.1.asciidoc b/man/picom.1.asciidoc index 5f995ff62b..2387ce222a 100644 --- a/man/picom.1.asciidoc +++ b/man/picom.1.asciidoc @@ -76,6 +76,9 @@ OPTIONS *--write-pid-path* 'PATH':: Write process ID to a file. it is recommended to use an absolute path. +*--plugins* 'PATH':: + Specify plugins to load. Plugins will first be searched in current working directory (unless specified in the config file, in which case this step is skipped), then in `$XDG_CONFIG_HOME/picom/plugins`, then in `$XDG_CONFIG_DIRS/picom/plugins`. If all of the above fail, the plugin name is passed directly to the dynamic loader. Can be specified multiple times to load more than one plugins. + *--shadow-color* 'STRING':: Color of shadow, as a hex string ('#000000') diff --git a/meson.build b/meson.build index 65c0780286..408900e2b7 100644 --- a/meson.build +++ b/meson.build @@ -84,6 +84,15 @@ install_data('bin/picom-trans', install_dir: get_option('bindir')) install_data('picom.desktop', install_dir: 'share/applications') install_data('picom.desktop', install_dir: get_option('sysconfdir') / 'xdg' / 'autostart') +pkgconf = import('pkgconfig') + +picom_pc = pkgconf.generate( + name: 'picom-api', + description: 'picom API headers', + requires: [ 'pixman-1', 'xcb' ], + subdirs: 'picom', +) + if get_option('compton') install_data('compton.desktop', install_dir: 'share/applications') install_data('media/icons/48x48/compton.png', diff --git a/src/api.c b/src/api.c new file mode 100644 index 0000000000..713fece81a --- /dev/null +++ b/src/api.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +#include + +#include +#include + +#include + +#include "compiler.h" +#include "list.h" +#include "log.h" +#include "utils.h" + +struct backend_plugins { + UT_hash_handle hh; + const char *backend_name; + struct list_node plugins; +} *backend_plugins; + +struct backend_plugin { + const char *backend_name; + picom_backend_plugin_entrypoint entrypoint; + void *user_data; + struct list_node siblings; +}; + +static bool add_backend_plugin(const char *backend_name, uint64_t major, uint64_t minor, + picom_backend_plugin_entrypoint entrypoint, void *user_data) { + if (major != PICOM_BACKEND_MAJOR || minor > PICOM_BACKEND_MINOR) { + log_error("Cannot add plugin for backend %s, because the requested " + "version %" PRIu64 ".%" PRIu64 " is incompatible with the our " + "%lu.%lu", + backend_name, major, minor, PICOM_BACKEND_MAJOR, + PICOM_BACKEND_MINOR); + return false; + } + + auto plugin = ccalloc(1, struct backend_plugin); + plugin->backend_name = backend_name; + plugin->entrypoint = entrypoint; + plugin->user_data = user_data; + + struct backend_plugins *plugins = NULL; + HASH_FIND_STR(backend_plugins, backend_name, plugins); + if (!plugins) { + plugins = ccalloc(1, struct backend_plugins); + plugins->backend_name = strdup(backend_name); + list_init_head(&plugins->plugins); + HASH_ADD_STR(backend_plugins, backend_name, plugins); + } + list_insert_after(&plugins->plugins, &plugin->siblings); + return true; +} + +void api_backend_plugins_invoke(const char *backend_name, struct backend_base *backend) { + struct backend_plugins *plugins = NULL; + HASH_FIND_STR(backend_plugins, backend_name, plugins); + if (!plugins) { + return; + } + + list_foreach(struct backend_plugin, plugin, &plugins->plugins, siblings) { + plugin->entrypoint(backend, plugin->user_data); + } +} + +static struct picom_api picom_api = { + .add_backend_plugin = add_backend_plugin, +}; + +PICOM_PUBLIC_API const struct picom_api * +picom_api_get_interfaces(uint64_t major, uint64_t minor, const char *context) { + if (major != PICOM_API_MAJOR || minor > PICOM_API_MINOR) { + log_error("Cannot provide API interfaces to %s, because the requested" + "version %" PRIu64 ".%" PRIu64 " is incompatible with our " + "%lu.%lu", + context, major, minor, PICOM_API_MAJOR, PICOM_API_MINOR); + return NULL; + } + return &picom_api; +} diff --git a/src/api_internal.h b/src/api_internal.h new file mode 100644 index 0000000000..de5fa77796 --- /dev/null +++ b/src/api_internal.h @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Yuxuan Shui + +struct backend_base; + +/// Invoke all backend plugins for the specified backend. +void api_backend_plugins_invoke(const char *backend_name, struct backend_base *backend); diff --git a/src/backend/backend.c b/src/backend/backend.c index 3580faa17b..d9fa1bd222 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -1,32 +1,84 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) Yuxuan Shui +#include #include #include +#include + #include "backend/backend.h" #include "common.h" #include "compiler.h" #include "config.h" #include "log.h" #include "region.h" -#include "types.h" #include "win.h" #include "x.h" -extern struct backend_operations xrender_ops, dummy_ops; -#ifdef CONFIG_OPENGL -extern struct backend_operations glx_ops; -extern struct backend_operations egl_ops; -#endif - -struct backend_operations *backend_list[NUM_BKEND] = { - [BKEND_XRENDER] = &xrender_ops, - [BKEND_DUMMY] = &dummy_ops, -#ifdef CONFIG_OPENGL - [BKEND_GLX] = &glx_ops, - [BKEND_EGL] = &egl_ops, -#endif -}; +static struct backend_info { + UT_hash_handle hh; + const char *name; + struct backend_base *(*init)(session_t *ps, xcb_window_t target); + bool can_present; +} *backend_registry = NULL; + +PICOM_PUBLIC_API bool +backend_register(uint64_t major, uint64_t minor, const char *name, + struct backend_base *(*init)(session_t *ps, xcb_window_t target), + bool can_present) { + if (major != PICOM_BACKEND_MAJOR) { + log_error("Backend %s has incompatible major version %" PRIu64 + ", expected %lu", + name, major, PICOM_BACKEND_MAJOR); + return false; + } + if (minor > PICOM_BACKEND_MINOR) { + log_error("Backend %s has incompatible minor version %" PRIu64 + ", expected %lu", + name, minor, PICOM_BACKEND_MINOR); + return false; + } + struct backend_info *info = NULL; + HASH_FIND_STR(backend_registry, name, info); + if (info) { + log_error("Backend %s is already registered", name); + return false; + } + + info = cmalloc(struct backend_info); + info->name = name; + info->init = init; + info->can_present = can_present; + HASH_ADD_KEYPTR(hh, backend_registry, info->name, strlen(info->name), info); + return true; +} + +struct backend_info *backend_find(const char *name) { + struct backend_info *info = NULL; + HASH_FIND_STR(backend_registry, name, info); + return info; +} + +struct backend_base * +backend_init(struct backend_info *info, session_t *ps, xcb_window_t target) { + return info->init(ps, target); +} + +struct backend_info *backend_iter(void) { + return backend_registry; +} + +struct backend_info *backend_iter_next(struct backend_info *info) { + return info->hh.next; +} + +const char *backend_name(struct backend_info *info) { + return info->name; +} + +bool backend_can_present(struct backend_info *info) { + return info->can_present; +} void handle_device_reset(session_t *ps) { log_error("Device reset detected"); @@ -66,22 +118,22 @@ bool backend_execute(struct backend_base *backend, image_handle target, unsigned continue; } succeeded = - backend->ops->blit(backend, cmd->origin, target, &cmd->blit); + backend->ops.blit(backend, cmd->origin, target, &cmd->blit); break; case BACKEND_COMMAND_COPY_AREA: if (!pixman_region32_not_empty(cmd->copy_area.region)) { continue; } - succeeded = backend->ops->copy_area(backend, cmd->origin, target, - cmd->copy_area.source_image, - cmd->copy_area.region); + succeeded = backend->ops.copy_area(backend, cmd->origin, target, + cmd->copy_area.source_image, + cmd->copy_area.region); break; case BACKEND_COMMAND_BLUR: if (!pixman_region32_not_empty(cmd->blur.target_mask)) { continue; } succeeded = - backend->ops->blur(backend, cmd->origin, target, &cmd->blur); + backend->ops.blur(backend, cmd->origin, target, &cmd->blur); break; case BACKEND_COMMAND_INVALID: default: assert(false); diff --git a/src/backend/backend.h b/src/backend/backend.h index 139a306766..32c037c9a1 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -5,457 +5,25 @@ #include -#include "compiler.h" -#include "config.h" -#include "driver.h" -#include "kernel.h" -#include "region.h" -#include "types.h" -#include "x.h" +#include -typedef struct session session_t; -struct managed_win; - -struct ev_loop; -struct backend_operations; - -typedef struct backend_base { - struct backend_operations *ops; - struct x_connection *c; - struct ev_loop *loop; - - /// Whether the backend can accept new render request at the moment - bool busy; - // ... -} backend_t; - -typedef void (*backend_ready_callback_t)(void *); - -// This mimics OpenGL's ARB_robustness extension, which enables detection of GPU context -// resets. -// See: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_robustness.txt, section -// 2.6 "Graphics Reset Recovery". -enum device_status { - DEVICE_STATUS_NORMAL, - DEVICE_STATUS_RESETTING, -}; - -enum shader_attributes { - // Whether the shader needs to be render regardless of whether the window is - // updated. - SHADER_ATTRIBUTE_ANIMATED = 1, -}; - -struct gaussian_blur_args { - int size; - double deviation; -}; - -struct box_blur_args { - int size; -}; - -struct kernel_blur_args { - struct conv **kernels; - int kernel_count; -}; - -struct dual_kawase_blur_args { - int size; - int strength; -}; - -typedef struct image_handle { - // Intentionally left blank -} *image_handle; - -/// A mask for various backend operations. -/// -/// The mask is composed of both a mask region and a mask image. The resulting mask -/// is the intersection of the two. The mask image can be modified by the `corner_radius` -/// and `inverted` properties. Note these properties have no effect on the mask region. -struct backend_mask_image { - /// Mask image, can be NULL. - /// - /// Mask image must be an image that was created with the - /// `BACKEND_IMAGE_FORMAT_MASK` format. Using an image with a wrong format as mask - /// is undefined behavior. - image_handle image; - /// Corner radius of the mask image, the corners of the mask image will be - /// rounded. - double corner_radius; - /// Origin of the mask image, in the source image's coordinate. - ivec2 origin; - /// Whether the mask image should be inverted. - bool inverted; -}; - -struct backend_blur_args { - /// The blur context - void *blur_context; - /// The source mask for the blur operation, may be NULL. Only parts of the source - /// image covered by the mask should participate in the blur operation. - const struct backend_mask_image *source_mask; - /// Region of the target image that will be covered by the blur operation, in the - /// source image's coordinate. - const region_t *target_mask; - /// Source image - image_handle source_image; - /// Opacity of the blurred image - double opacity; -}; - -struct backend_blit_args { - /// Source image, can be NULL. - image_handle source_image; - /// Mask for the source image. may be NULL. Only contents covered by the mask - /// should participate in the blit operation. This applies to the source image - /// before it's scaled. - const struct backend_mask_image *source_mask; - /// Mask for the target image. Only regions of the target image covered by this - /// mask should be modified. This is the target's coordinate system. - const region_t *target_mask; - /// Custom shader for this blit operation. - void *shader; - /// Opacity of the source image. - double opacity; - /// Dim level of the source image. - double dim; - /// Brightness limit of the source image. Source image - /// will be normalized so that the maximum brightness is - /// this value. - double max_brightness; - /// Scale factor for the horizontal and vertical direction (X for horizontal, - /// Y for vertical). - vec2 scale; - /// Corner radius of the source image BEFORE scaling. The corners of - /// the source image will be rounded. - double corner_radius; - /// Effective size of the source image BEFORE scaling, set where the corners - /// of the image are. - ivec2 effective_size; - /// Border width of the source image BEFORE scaling. This is used with - /// `corner_radius` to create a border for the rounded corners. - /// Setting this has no effect if `corner_radius` is 0. - int border_width; - /// Whether the source image should be inverted. - bool color_inverted; -}; - -enum backend_image_format { - /// A format that can be used for normal rendering, and binding - /// X pixmaps. - /// Images created with `bind_pixmap` have this format automatically. - BACKEND_IMAGE_FORMAT_PIXMAP, - /// Like `BACKEND_IMAGE_FORMAT_PIXMAP`, but the image has a higher - /// precision. Support is optional. - BACKEND_IMAGE_FORMAT_PIXMAP_HIGH, - /// A format that can be used for masks. - BACKEND_IMAGE_FORMAT_MASK, -}; - -enum backend_image_capability { - /// Image can be sampled from. This is required for `blit` and `blur` source - /// images. All images except the back buffer should have this capability. - /// Note that `copy_area` should work without this capability, this is so that - /// blurring the back buffer could be done. - BACKEND_IMAGE_CAP_SRC = 1 << 0, - /// Image can be rendered to. This is required for target images of any operation. - /// All images except bound X pixmaps should have this capability. - BACKEND_IMAGE_CAP_DST = 1 << 1, -}; - -enum backend_command_op { - BACKEND_COMMAND_INVALID = -1, - BACKEND_COMMAND_BLIT, - BACKEND_COMMAND_BLUR, - BACKEND_COMMAND_COPY_AREA, -}; - -/// Symbolic references used as render command source images. The actual `image_handle` -/// will later be filled in by the renderer using this symbolic reference. -enum backend_command_source { - BACKEND_COMMAND_SOURCE_WINDOW, - BACKEND_COMMAND_SOURCE_SHADOW, - BACKEND_COMMAND_SOURCE_BACKGROUND, -}; - -// TODO(yshui) might need better names - -struct backend_command { - enum backend_command_op op; - ivec2 origin; - enum backend_command_source source; - union { - struct { - struct backend_blit_args blit; - /// Region of the screen that will be covered by this blit - /// operations, in screen coordinates. - region_t opaque_region; - }; - struct { - image_handle source_image; - const region_t *region; - } copy_area; - struct backend_blur_args blur; - }; - /// Source mask for the operation. - /// If the `source_mask` of the operation's argument points to this, a mask image - /// will be created for the operation for the renderer. - struct backend_mask_image source_mask; - /// Target mask for the operation. - region_t target_mask; -}; - -enum backend_quirk { - /// Backend cannot do blur quickly. The compositor will avoid using blur to create - /// shadows on this backend - BACKEND_QUIRK_SLOW_BLUR = 1 << 0, -}; - -struct backend_operations { - // =========== Initialization =========== - - /// Initialize the backend, prepare for rendering to the target window. - backend_t *(*init)(session_t *, xcb_window_t)attr_nonnull(1); - void (*deinit)(backend_t *backend_data) attr_nonnull(1); - - /// Called when rendering will be stopped for an unknown amount of - /// time (e.g. when screen is unredirected). Free some resources. - /// - /// Optional, not yet used - void (*pause)(backend_t *backend_data, session_t *ps); - - /// Called before rendering is resumed - /// - /// Optional, not yet used - void (*resume)(backend_t *backend_data, session_t *ps); - - /// Called when root window size changed. All existing image data ever - /// returned by this backend should remain valid after this call - /// returns. - /// - /// Optional - void (*root_change)(backend_t *backend_data, session_t *ps); - - // =========== Rendering ============ - - /// Called before when a new frame starts. - /// - /// Optional - void (*prepare)(backend_t *backend_data, const region_t *reg_damage); - - /// Multiply the alpha channel of the target image by a given value. - /// - /// @param backend_data backend data - /// @param target an image handle, cannot be NULL. - /// @param alpha the alpha value to multiply - /// @param region the region to apply the alpha, in the target image's - /// coordinate. - bool (*apply_alpha)(struct backend_base *backend_data, image_handle target, - double alpha, const region_t *region) attr_nonnull(1, 2, 4); - - /// Copy pixels from a source image on to the target image. - /// - /// Some effects may be applied. If the region specified by the mask - /// contains parts that are outside the source image, the source image - /// will be repeated to fit. - /// - /// Source and target MUST NOT be the same image. - /// - /// @param backend_data backend data - /// @param origin the origin of the operation, in the target image's - /// coordinate. - /// @param target an image handle, cannot be NULL. - /// @param args arguments for blit - /// @return whether the operation is successful - bool (*blit)(struct backend_base *backend_data, ivec2 origin, image_handle target, - const struct backend_blit_args *args) attr_nonnull(1, 3, 4); - - /// Blur a given region of a source image and store the result in the - /// target image. - /// - /// The blur operation might access pixels outside the mask region, the - /// amount of pixels accessed can be queried with `get_blur_size`. If - /// pixels outside the source image are accessed, the result will be - /// clamped to the edge of the source image. - /// - /// Source and target may be the same image. - /// - /// @param backend_data backend data - /// @param origin the origin of the operation, in the target image's - /// coordinate. - /// @param target an image handle, cannot be NULL. - /// @param args argument for blur - /// @return whether the operation is successful - bool (*blur)(struct backend_base *backend_data, ivec2 origin, image_handle target, - const struct backend_blur_args *args) attr_nonnull(1, 3, 4); - - /// Direct copy of pixels from a source image on to the target image. - /// This is a simpler version of `blit`, without any effects. Note unlike `blit`, - /// if `region` tries to sample from outside the source image, instead of - /// repeating, the result will be clamped to the edge of the source image. - /// Blending should not be applied for the copy. - /// - /// Source and target MUST NOT be the same image. - /// - /// @param backend_data backend data - /// @param origin the origin of the operation, in the target image's - /// coordinate. - /// @param target an image handle, cannot be NULL. - /// @param source an image handle, cannot be NULL. - /// @param region the region to copy, in the target image's coordinate. - /// @return whether the operation is successful - bool (*copy_area)(struct backend_base *backend_data, ivec2 origin, - image_handle target, image_handle source, const region_t *region) - attr_nonnull(1, 3, 4, 5); - - /// Similar to `copy_area`, but is specialized for copying from a higher - /// precision format to a lower precision format. It has 2 major differences from - /// `copy_area`: - /// - /// 1. This function _may_ use dithering when copying from a higher precision - /// format to a lower precision format. But this is not required. - /// 2. This function only needs to support copying from an image with the SRC - /// capability. Unlike `copy_area`, which supports copying from any image. - /// - /// It's perfectly legal to have this pointing to the same function as - /// `copy_area`, if the backend doesn't support dithering. - /// - /// @param backend_data backend data - /// @param origin the origin of the operation, in the target image's - /// coordinate. - /// @param target an image handle, cannot be NULL. - /// @param source an image handle, cannot be NULL. - /// @param region the region to copy, in the target image's coordinate. - /// @return whether the operation is successful - bool (*copy_area_quantize)(struct backend_base *backend_data, ivec2 origin, - image_handle target, image_handle source, - const region_t *region) attr_nonnull(1, 3, 4, 5); - - /// Initialize an image with a given color value. If the image has a mask format, - /// only the alpha channel of the color is used. - /// - /// @param backend_data backend data - /// @param target an image handle, cannot be NULL. - /// @param color the color to fill the image with - /// @return whether the operation is successful - bool (*clear)(struct backend_base *backend_data, image_handle target, - struct color color) attr_nonnull(1, 2); - - /// Present the back buffer to the target window. Ideally the backend should keep - /// track of the region of the back buffer that has been updated, and use relevant - /// mechanism (when possible) to present only the updated region. - bool (*present)(struct backend_base *backend_data) attr_nonnull(1); - - // ============ Resource management =========== - - /// Create a shader object from a shader source. - /// - /// Optional - void *(*create_shader)(backend_t *backend_data, const char *source)attr_nonnull(1, 2); - - /// Free a shader object. - /// - /// Required if create_shader is present. - void (*destroy_shader)(backend_t *backend_data, void *shader) attr_nonnull(1, 2); - - /// Create a new, uninitialized image with the given format and size. - /// - /// @param backend_data backend data - /// @param format the format of the image - /// @param size the size of the image - image_handle (*new_image)(struct backend_base *backend_data, - enum backend_image_format format, ivec2 size) - attr_nonnull(1); - - /// 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 - /// @return backend specific image handle for the pixmap. May be - /// NULL. - image_handle (*bind_pixmap)(struct backend_base *backend_data, xcb_pixmap_t pixmap, - struct xvisual_info fmt) attr_nonnull(1); - - /// Acquire the image handle of the back buffer. - /// - /// @param backend_data backend data - image_handle (*back_buffer)(struct backend_base *backend_data); - - /// Free resources associated with an image data structure. Releasing the image - /// returned by `back_buffer` should be a no-op. - /// - /// @param image the image to be released, cannot be NULL. - /// @return if this image is created by `bind_pixmap`, the X pixmap; 0 - /// otherwise. - xcb_pixmap_t (*release_image)(struct backend_base *backend_data, image_handle image) - attr_nonnull(1, 2); - - // =========== Query =========== - - /// Get backend quirks - /// @return a bitmask of `enum backend_quirk`. - uint32_t (*quirks)(struct backend_base *backend_data) attr_nonnull(1); - - /// Check if an optional image format is supported by the backend. - bool (*is_format_supported)(struct backend_base *backend_data, - enum backend_image_format format) attr_nonnull(1); - - /// Return the capabilities of an image. - uint32_t (*image_capabilities)(struct backend_base *backend_data, image_handle image) - attr_nonnull(1, 2); - - /// Get the attributes of a shader. - /// - /// Optional, Returns a bitmask of attributes, see `shader_attributes`. - uint64_t (*get_shader_attributes)(backend_t *backend_data, void *shader) - attr_nonnull(1, 2); - - /// Get the age of the buffer content we are currently rendering on top - /// of. The buffer that has just been `present`ed has a buffer age of 1. - /// Every time `present` is called, buffers get older. Return -1 if the - /// buffer is empty. - /// - /// Optional - int (*buffer_age)(backend_t *backend_data); - - /// Get the render time of the last frame. If the render is still in progress, - /// returns false. The time is returned in `ts`. Frames are delimited by the - /// present() calls. i.e. after a present() call, last_render_time() should start - /// reporting the time of the just presented frame. - /// - /// Optional, if not available, the most conservative estimation will be used. - bool (*last_render_time)(backend_t *backend_data, struct timespec *ts); - - /// The maximum number buffer_age might return. - int max_buffer_age; - - // =========== Post-processing ============ - /// Create a blur context that can be used to call `blur` for images with a - /// specific format. - void *(*create_blur_context)(backend_t *base, enum blur_method, - enum backend_image_format format, void *args); - /// Destroy a blur context - void (*destroy_blur_context)(backend_t *base, void *ctx); - /// Get how many pixels outside of the blur area is needed for blur - void (*get_blur_size)(void *blur_context, int *width, int *height); - - // =========== Misc ============ - /// Return the driver that is been used by the backend - enum driver (*detect_driver)(backend_t *backend_data); - - void (*diagnostics)(backend_t *backend_data); - - enum device_status (*device_status)(backend_t *backend_data); -}; - -extern struct backend_operations *backend_list[]; +#include "log.h" bool backend_execute(struct backend_base *backend, image_handle target, unsigned ncmds, const struct backend_command cmds[ncmds]); + +struct backend_info *backend_find(const char *name); +struct backend_base * +backend_init(struct backend_info *info, session_t *ps, xcb_window_t target); +struct backend_info *backend_iter(void); +struct backend_info *backend_iter_next(struct backend_info *info); +const char *backend_name(struct backend_info *info); +bool backend_can_present(struct backend_info *info); void log_backend_command_(enum log_level level, const char *func, const struct backend_command *cmd); #define log_backend_command(level, cmd) \ log_backend_command_(LOG_LEVEL_##level, __func__, &(cmd)); + +/// Define a backend entry point. (Note constructor priority 202 is used here because 1xx +/// is reversed by test.h, and 201 is used for logging initialization.) +#define BACKEND_ENTRYPOINT(func) static void __attribute__((constructor(202))) func(void) diff --git a/src/backend/backend_common.c b/src/backend/backend_common.c index 7863dde69c..4554964ae0 100644 --- a/src/backend/backend_common.c +++ b/src/backend/backend_common.c @@ -6,7 +6,6 @@ #include #include -#include "backend/backend.h" #include "backend/backend_common.h" #include "common.h" #include "config.h" @@ -413,7 +412,7 @@ void init_backend_base(struct backend_base *base, session_t *ps) { base->c = &ps->c; base->loop = ps->loop; base->busy = false; - base->ops = NULL; + base->ops = (struct backend_operations){}; } uint32_t backend_no_quirks(struct backend_base *base attr_unused) { diff --git a/src/backend/backend_common.h b/src/backend/backend_common.h index e9091cbeb5..0bab860763 100644 --- a/src/backend/backend_common.h +++ b/src/backend/backend_common.h @@ -7,15 +7,14 @@ #include -#include "backend.h" #include "config.h" -#include "region.h" struct session; struct win; struct conv; struct backend_base; struct backend_operations; +struct x_connection; struct dual_kawase_params { /// Number of downsample passes diff --git a/src/backend/driver.c b/src/backend/driver.c index 99ea4d7a3a..53ef1f5138 100644 --- a/src/backend/driver.c +++ b/src/backend/driver.c @@ -83,8 +83,8 @@ enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_wind free(randr_version); // If the backend supports driver detection, use that as well - if (backend_data && backend_data->ops->detect_driver) { - ret |= backend_data->ops->detect_driver(backend_data); + if (backend_data && backend_data->ops.detect_driver) { + ret |= backend_data->ops.detect_driver(backend_data); } return ret; } diff --git a/src/backend/dummy/dummy.c b/src/backend/dummy/dummy.c index 84d7edcd33..04ec3fff20 100644 --- a/src/backend/dummy/dummy.c +++ b/src/backend/dummy/dummy.c @@ -1,6 +1,8 @@ #include #include +#include + #include "backend/backend.h" #include "backend/backend_common.h" #include "common.h" @@ -8,7 +10,6 @@ #include "config.h" #include "log.h" #include "region.h" -#include "types.h" #include "uthash_extra.h" #include "utils.h" #include "x.h" @@ -28,12 +29,12 @@ struct dummy_data { struct dummy_image back_buffer; }; -struct backend_operations dummy_ops; +const struct backend_operations dummy_ops; struct backend_base *dummy_init(session_t *ps attr_unused, xcb_window_t target attr_unused) { auto ret = ccalloc(1, struct dummy_data); init_backend_base(&ret->base, ps); - ret->base.ops = &dummy_ops; + ret->base.ops = dummy_ops; list_init_head(&ret->non_pixmap_images); return &ret->base; } @@ -203,7 +204,19 @@ bool dummy_is_format_supported(struct backend_base *base attr_unused, return true; } -struct backend_operations dummy_ops = { +static int dummy_max_buffer_age(struct backend_base *base attr_unused) { + return 5; +} + +#define PICOM_BACKEND_DUMMY_MAJOR (0UL) +#define PICOM_BACKEND_DUMMY_MINOR (1UL) + +static void dummy_version(struct backend_base * /*base*/, uint64_t *major, uint64_t *minor) { + *major = PICOM_BACKEND_DUMMY_MAJOR; + *minor = PICOM_BACKEND_DUMMY_MINOR; +} + +const struct backend_operations dummy_ops = { .apply_alpha = dummy_apply_alpha, .back_buffer = dummy_back_buffer, .blit = dummy_blit, @@ -216,14 +229,22 @@ struct backend_operations dummy_ops = { .new_image = dummy_new_image, .bind_pixmap = dummy_bind_pixmap, .quirks = backend_no_quirks, + .version = dummy_version, .release_image = dummy_release_image, .init = dummy_init, .deinit = dummy_deinit, .buffer_age = dummy_buffer_age, - .max_buffer_age = 5, + .max_buffer_age = dummy_max_buffer_age, .create_blur_context = dummy_create_blur_context, .destroy_blur_context = dummy_destroy_blur_context, .get_blur_size = dummy_get_blur_size, }; + +BACKEND_ENTRYPOINT(dummy_register) { + if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "dummy", + dummy_ops.init, false)) { + log_error("Failed to register dummy backend"); + } +} diff --git a/src/backend/gl/egl.c b/src/backend/gl/egl.c index 8ff0754648..9b5dbbb2e5 100644 --- a/src/backend/gl/egl.c +++ b/src/backend/gl/egl.c @@ -4,8 +4,6 @@ */ #include -#include -#include #include #include #include @@ -76,10 +74,9 @@ static void egl_release_image(backend_t *base, struct gl_texture *tex) { void egl_deinit(backend_t *base) { struct egl_data *gd = (void *)base; - gl_deinit(&gd->gl); - // Destroy EGL context if (gd->ctx != EGL_NO_CONTEXT) { + gl_deinit(&gd->gl); eglMakeCurrent(gd->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(gd->display, gd->ctx); gd->ctx = EGL_NO_CONTEXT; @@ -106,7 +103,7 @@ static bool egl_set_swap_interval(int interval, EGLDisplay dpy) { return eglSwapInterval(dpy, interval); } -struct backend_operations egl_ops; +const struct backend_operations egl_ops; /** * Initialize OpenGL. */ @@ -121,6 +118,8 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) { return NULL; } + log_warn("The egl backend is still experimental, use with care."); + gd = ccalloc(1, struct egl_data); gd->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, ps->c.dpy, (EGLint[]){ @@ -153,7 +152,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) { eglext_init(gd->display); init_backend_base(&gd->gl.base, ps); - gd->gl.base.ops = &egl_ops; + gd->gl.base.ops = egl_ops; if (!eglext.has_EGL_KHR_image_pixmap) { log_error("EGL_KHR_image_pixmap not available."); goto end; @@ -338,7 +337,23 @@ static void egl_diagnostics(backend_t *base) { } } -struct backend_operations egl_ops = { +static int egl_max_buffer_age(backend_t *base attr_unused) { + if (!eglext.has_EGL_EXT_buffer_age) { + return 0; + } + + return 5; // Why? +} + +#define PICOM_BACKEND_EGL_MAJOR (0UL) +#define PICOM_BACKEND_EGL_MINOR (1UL) + +static void egl_version(struct backend_base * /*base*/, uint64_t *major, uint64_t *minor) { + *major = PICOM_BACKEND_EGL_MAJOR; + *minor = PICOM_BACKEND_EGL_MINOR; +} + +const struct backend_operations egl_ops = { .apply_alpha = gl_apply_alpha, .back_buffer = gl_back_buffer, .blit = gl_blit, @@ -352,6 +367,7 @@ struct backend_operations egl_ops = { .new_image = gl_new_image, .present = egl_present, .quirks = backend_no_quirks, + .version = egl_version, .release_image = gl_release_image, .init = egl_init, @@ -368,7 +384,7 @@ struct backend_operations egl_ops = { .create_shader = gl_create_window_shader, .destroy_shader = gl_destroy_window_shader, .get_shader_attributes = gl_get_shader_attributes, - .max_buffer_age = 5, // Why? + .max_buffer_age = egl_max_buffer_age, }; struct eglext_info eglext = {0}; @@ -390,3 +406,10 @@ void eglext_init(EGLDisplay dpy) { #endif #undef check_ext } + +BACKEND_ENTRYPOINT(egl_register) { + if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "egl", + egl_ops.init, true)) { + log_error("Failed to register egl backend"); + } +} diff --git a/src/backend/gl/gl_common.c b/src/backend/gl/gl_common.c index e9ab9f3c66..328c28619b 100644 --- a/src/backend/gl/gl_common.c +++ b/src/backend/gl/gl_common.c @@ -9,13 +9,13 @@ #include #include // for xcb_render_fixed_t, XXX -#include "backend/backend.h" +#include + #include "common.h" #include "compiler.h" #include "config.h" #include "log.h" #include "region.h" -#include "types.h" #include "utils.h" #include "backend/backend_common.h" diff --git a/src/backend/gl/glx.c b/src/backend/gl/glx.c index feaccfcf6d..791f433bda 100644 --- a/src/backend/gl/glx.c +++ b/src/backend/gl/glx.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -31,7 +30,6 @@ #include "config.h" #include "log.h" #include "picom.h" -#include "region.h" #include "utils.h" #include "win.h" #include "x.h" @@ -225,7 +223,7 @@ static bool glx_set_swap_interval(int interval, Display *dpy, GLXDrawable drawab return vsync_enabled; } -struct backend_operations glx_ops; +const struct backend_operations glx_ops; /** * Initialize OpenGL. */ @@ -234,7 +232,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) { glxext_init(ps->c.dpy, ps->c.screen); auto gd = ccalloc(1, struct _glx_data); init_backend_base(&gd->gl.base, ps); - gd->gl.base.ops = &glx_ops; + gd->gl.base.ops = glx_ops; gd->target_win = target; @@ -517,7 +515,19 @@ static void glx_diagnostics(backend_t *base) { } } -struct backend_operations glx_ops = { +static int glx_max_buffer_age(struct backend_base *base attr_unused) { + return 5; // Why? +} + +#define PICOM_BACKEND_GLX_MAJOR (0UL) +#define PICOM_BACKEND_GLX_MINOR (1UL) + +static void glx_version(struct backend_base * /*base*/, uint64_t *major, uint64_t *minor) { + *major = PICOM_BACKEND_GLX_MAJOR; + *minor = PICOM_BACKEND_GLX_MINOR; +} + +const struct backend_operations glx_ops = { .apply_alpha = gl_apply_alpha, .back_buffer = gl_back_buffer, .bind_pixmap = glx_bind_pixmap, @@ -531,6 +541,7 @@ struct backend_operations glx_ops = { .new_image = gl_new_image, .present = glx_present, .quirks = backend_no_quirks, + .version = glx_version, .release_image = gl_release_image, .init = glx_init, @@ -547,7 +558,7 @@ struct backend_operations glx_ops = { .create_shader = gl_create_window_shader, .destroy_shader = gl_destroy_window_shader, .get_shader_attributes = gl_get_shader_attributes, - .max_buffer_age = 5, // Why? + .max_buffer_age = glx_max_buffer_age, }; struct glxext_info glxext = {0}; @@ -575,3 +586,10 @@ void glxext_init(Display *dpy, int screen) { #endif #undef check_ext } + +BACKEND_ENTRYPOINT(glx_register) { + if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "glx", + glx_ops.init, true)) { + log_error("Failed to register glx backend"); + } +} diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 4096923d20..e83c4bd146 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -12,6 +12,8 @@ #include #include +#include + #include "backend/backend.h" #include "backend/backend_common.h" #include "backend/driver.h" @@ -22,7 +24,6 @@ #include "log.h" #include "picom.h" #include "region.h" -#include "types.h" #include "utils.h" #include "win.h" #include "x.h" @@ -864,15 +865,20 @@ static void xrender_get_blur_size(void *blur_context, int *width, int *height) { *width = ctx->resize_width; *height = ctx->resize_height; } -struct backend_operations xrender_ops; +const struct backend_operations xrender_ops; static backend_t *xrender_init(session_t *ps, xcb_window_t target) { if (ps->o.dithered_present) { - log_warn("\"dithered-present\" is not supported by the xrender backend."); + log_warn("\"dithered-present\" is not supported by the xrender backend, " + "it will be ignored."); + } + if (ps->o.max_brightness < 1.0) { + log_warn("\"max-brightness\" is not supported by the xrender backend, it " + "will be ignored."); } auto xd = ccalloc(1, struct xrender_data); init_backend_base(&xd->base, ps); - xd->base.ops = &xrender_ops; + xd->base.ops = xrender_ops; for (int i = 0; i <= MAX_ALPHA; ++i) { double o = (double)i / (double)MAX_ALPHA; @@ -1014,7 +1020,19 @@ uint32_t xrender_quirks(struct backend_base *base) { return ((struct xrender_data *)base)->quirks; } -struct backend_operations xrender_ops = { +static int xrender_max_buffer_age(struct backend_base *base) { + return ((struct xrender_data *)base)->vsync ? 2 : 1; +} + +#define PICOM_BACKEND_XRENDER_MAJOR (0UL) +#define PICOM_BACKEND_XRENDER_MINOR (1UL) + +static void xrender_version(struct backend_base * /*base*/, uint64_t *major, uint64_t *minor) { + *major = PICOM_BACKEND_XRENDER_MAJOR; + *minor = PICOM_BACKEND_XRENDER_MINOR; +} + +const struct backend_operations xrender_ops = { .apply_alpha = xrender_apply_alpha, .back_buffer = xrender_back_buffer, .bind_pixmap = xrender_bind_pixmap, @@ -1028,6 +1046,7 @@ struct backend_operations xrender_ops = { .new_image = xrender_new_image, .present = xrender_present, .quirks = xrender_quirks, + .version = xrender_version, .release_image = xrender_release_image, .init = xrender_init, @@ -1036,11 +1055,18 @@ struct backend_operations xrender_ops = { // `render_shadow`, and `backend_compat_shadow_from_mask` for // `shadow_from_mask` .buffer_age = xrender_buffer_age, - .max_buffer_age = 2, + .max_buffer_age = xrender_max_buffer_age, .create_blur_context = xrender_create_blur_context, .destroy_blur_context = xrender_destroy_blur_context, .get_blur_size = xrender_get_blur_size // end }; +BACKEND_ENTRYPOINT(xrender_register) { + if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "xrender", + xrender_ops.init, true)) { + log_error("Failed to register xrender backend"); + } +} + // vim: set noet sw=8 ts=8: diff --git a/src/c2.c b/src/c2.c index 0b8996ab29..8030f92181 100644 --- a/src/c2.c +++ b/src/c2.c @@ -36,6 +36,7 @@ #include "log.h" #include "string_utils.h" #include "test.h" +#include "uthash_extra.h" #include "utils.h" #include "win.h" #include "x.h" diff --git a/src/common.h b/src/common.h index b7b220e930..13f7660857 100644 --- a/src/common.h +++ b/src/common.h @@ -40,7 +40,9 @@ #include #include -#include "uthash_extra.h" +#include +#include + #ifdef CONFIG_OPENGL #include "backend/gl/glx.h" #endif @@ -51,16 +53,11 @@ #endif // FIXME This list of includes should get shorter -#include "backend/backend.h" #include "backend/driver.h" -#include "compiler.h" #include "config.h" -#include "list.h" #include "region.h" #include "render.h" #include "statistics.h" -#include "types.h" -#include "utils.h" #include "win_defs.h" #include "x.h" @@ -462,7 +459,7 @@ static inline xcb_window_t get_tgt_window(session_t *ps) { * Check if current backend uses GLX. */ static inline bool bkend_use_glx(session_t *ps) { - return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; + return BKEND_GLX == ps->o.legacy_backend || BKEND_XR_GLX_HYBRID == ps->o.legacy_backend; } /** diff --git a/src/compiler.h b/src/compiler.h index 54ed1ea6bf..03674be8e3 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -126,6 +126,8 @@ #else # define thread_local _Pragma("GCC error \"No thread local storage support\"") __error__ #endif + +#define PICOM_PUBLIC_API __attribute__((visibility("default"))) // clang-format on typedef unsigned long ulong; diff --git a/src/config.c b/src/config.c index 6bc511ed70..bd6ac186f2 100644 --- a/src/config.c +++ b/src/config.c @@ -3,13 +3,11 @@ // Copyright (c) 2013 Richard Grenville #include -#include +#include #include #include -#include #include #include -#include #include #include #include @@ -18,18 +16,13 @@ #include #include // for xcb_render_fixed_t, XXX +#include #include -#include "c2.h" #include "common.h" -#include "compiler.h" #include "kernel.h" #include "log.h" -#include "region.h" #include "string_utils.h" -#include "types.h" -#include "utils.h" -#include "win.h" #include "config.h" @@ -632,11 +625,23 @@ void *parse_window_shader_prefix_with_cwd(const char *src, const char **end, voi return parse_window_shader_prefix(src, end, cwd); } +bool load_plugin(const char *name, const char *include_dir) { + scoped_charp path = locate_auxiliary_file("plugins", optarg, include_dir); + void *handle = NULL; + if (!path) { + handle = dlopen(name, RTLD_LAZY); + } else { + log_debug("Plugin %s resolved to %s", name, path); + handle = dlopen(path, RTLD_LAZY); + } + return handle != NULL; +} + bool parse_config(options_t *opt, const char *config_file) { // clang-format off *opt = (struct options){ - .backend = BKEND_XRENDER, - .legacy_backends = false, + .legacy_backend = BKEND_XRENDER, + .use_legacy_backends = false, .glx_no_stencil = false, .mark_wmwin_focused = false, .mark_ovredir_focused = false, diff --git a/src/config.h b/src/config.h index a1561a19dc..9c0723c8c5 100644 --- a/src/config.h +++ b/src/config.h @@ -16,12 +16,12 @@ #include #include +#include #include "compiler.h" #include "kernel.h" #include "list.h" #include "log.h" -#include "types.h" #include "win_defs.h" typedef struct session session_t; @@ -58,15 +58,6 @@ typedef struct win_option { bool clip_shadow_above; } win_option_t; -enum blur_method { - BLUR_METHOD_NONE = 0, - BLUR_METHOD_KERNEL, - BLUR_METHOD_BOX, - BLUR_METHOD_GAUSSIAN, - BLUR_METHOD_DUAL_KAWASE, - BLUR_METHOD_INVALID, -}; - typedef struct _c2_lptr c2_lptr_t; enum vblank_scheduler_type { @@ -158,11 +149,13 @@ typedef struct options { bool debug_mode; // === General === /// Use the legacy backends? - bool legacy_backends; + bool use_legacy_backends; /// Path to write PID to. char *write_pid_path; - /// The backend in use. - int backend; + /// Name of the backend + struct backend_info *backend; + /// The backend in use (for legacy backends). + int legacy_backend; /// Log level. int log_level; /// Whether to sync X drawing with X Sync fence to avoid certain delay @@ -352,6 +345,8 @@ typedef struct options { extern const char *const BACKEND_STRS[NUM_BKEND + 1]; +bool load_plugin(const char *name, const char *include_dir); + bool must_use parse_long(const char *, long *); bool must_use parse_int(const char *, int *); struct conv **must_use parse_blur_kern_lst(const char *, int *count); @@ -407,7 +402,6 @@ static inline attr_pure int parse_backend(const char *str) { "version will be removed soon."); return BKEND_XR_GLX_HYBRID; } - log_error("Invalid backend argument: %s", str); return NUM_BKEND; } diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 8582397c97..643eda313c 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -10,6 +10,7 @@ #include #include +#include "backend/backend.h" #include "c2.h" #include "common.h" #include "config.h" @@ -628,6 +629,37 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { // Get options from the configuration file. We don't do range checking // right now. It will be done later + // Load user specified plugins at the very beginning, because list of backends + // depends on the plugins loaded. + auto plugins = config_lookup(&cfg, "plugins"); + if (plugins != NULL) { + sval = config_setting_get_string(plugins); + if (sval) { + if (!load_plugin(sval, NULL)) { + log_fatal("Failed to load plugin \"%s\".", sval); + goto out; + } + } else if (config_setting_is_array(plugins) || config_setting_is_list(plugins)) { + for (int i = 0; i < config_setting_length(plugins); i++) { + sval = config_setting_get_string_elem(plugins, i); + if (!sval) { + log_fatal("Invalid value for \"plugins\" at line " + "%d.", + config_setting_source_line(plugins)); + goto out; + } + if (!load_plugin(sval, NULL)) { + log_fatal("Failed to load plugin \"%s\".", sval); + goto out; + } + } + } else { + log_fatal("Invalid value for \"plugins\" at line %d.", + config_setting_source_line(plugins)); + goto out; + } + } + // --dbus lcfg_lookup_bool(&cfg, "dbus", &opt->dbus); @@ -743,9 +775,10 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { lcfg_lookup_bool(&cfg, "vsync", &opt->vsync); // --backend if (config_lookup_string(&cfg, "backend", &sval)) { - opt->backend = parse_backend(sval); - if (opt->backend >= NUM_BKEND) { - log_fatal("Cannot parse backend"); + opt->legacy_backend = parse_backend(sval); + opt->backend = backend_find(sval); + if (opt->legacy_backend >= NUM_BKEND && opt->backend == NULL) { + log_fatal("Invalid backend: %s", sval); goto out; } } diff --git a/src/dbus.c b/src/dbus.c index e60aceb282..f09080fc6d 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -20,15 +20,15 @@ #include #include +#include + +#include "backend/backend.h" #include "common.h" #include "compiler.h" #include "config.h" #include "list.h" #include "log.h" #include "string_utils.h" -#include "transition.h" -#include "types.h" -#include "uthash_extra.h" #include "utils.h" #include "win.h" #include "win_defs.h" @@ -853,7 +853,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus dbus_set_error_const(err, DBUS_ERROR_INVALID_ARGS, NULL); return DBUS_HANDLER_RESULT_HANDLED; } - assert((size_t)ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); #define append(tgt, type, ret) \ if (!strcmp(#tgt, target)) { \ @@ -864,6 +863,18 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus } #define append_session_option(tgt, type) append(tgt, type, ps->o.tgt) + if (!strcmp("backend", target)) { + assert(!ps->o.use_legacy_backends || + (size_t)ps->o.legacy_backend < ARR_SIZE(BACKEND_STRS)); + const char *name = ps->o.use_legacy_backends + ? BACKEND_STRS[ps->o.legacy_backend] + : backend_name(ps->o.backend); + if (reply != NULL && !cdbus_append_string(reply, name)) { + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + return DBUS_HANDLER_RESULT_HANDLED; + } + append(version, string, PICOM_VERSION); append(pid, int32, getpid()); append(display, string, DisplayString(ps->c.dpy)); @@ -875,7 +886,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus append(unredir_if_possible_delay, int32, (int32_t)ps->o.unredir_if_possible_delay); append(refresh_rate, int32, 0); append(sw_opti, boolean, false); - append(backend, string, BACKEND_STRS[ps->o.backend]); + append(backend, string, BACKEND_STRS[ps->o.legacy_backend]); append_session_option(unredir_if_possible, boolean); append_session_option(write_pid_path, string); diff --git a/src/diagnostic.c b/src/diagnostic.c index 9278a57b4c..7cfac5cbde 100644 --- a/src/diagnostic.c +++ b/src/diagnostic.c @@ -5,6 +5,7 @@ #include #include +#include "backend/backend.h" #include "backend/driver.h" #include "common.h" #include "config.h" @@ -44,17 +45,17 @@ void print_diagnostics(session_t *ps, const char *config_file, bool compositor_r printf("\n### Drivers (inaccurate):\n\n"); print_drivers(ps->drivers); - for (int i = 0; i < NUM_BKEND; i++) { - if (backend_list[i] && backend_list[i]->diagnostics) { - printf("\n### Backend: %s\n\n", BACKEND_STRS[i]); - auto data = backend_list[i]->init(ps, session_get_target_window(ps)); - if (!data) { - printf(" Cannot initialize this backend\n"); - } else { - backend_list[i]->diagnostics(data); - backend_list[i]->deinit(data); - } + for (auto i = backend_iter(); i; i = backend_iter_next(i)) { + auto backend_data = backend_init(i, ps, session_get_target_window(ps)); + if (!backend_data) { + printf(" Cannot initialize backend %s\n", backend_name(i)); + continue; } + if (backend_data->ops.diagnostics) { + printf("\n### Backend: %s\n\n", backend_name(i)); + backend_data->ops.diagnostics(backend_data); + } + backend_data->ops.deinit(backend_data); } } diff --git a/src/event.c b/src/event.c index d7b193bc5b..d461e3fe43 100644 --- a/src/event.c +++ b/src/event.c @@ -11,6 +11,8 @@ #include #include +#include + #include "atom.h" #include "c2.h" #include "common.h" @@ -20,7 +22,6 @@ #include "log.h" #include "picom.h" #include "region.h" -#include "types.h" #include "utils.h" #include "win.h" #include "win_defs.h" diff --git a/src/log.h b/src/log.h index e7c6c06c43..3e20f61129 100644 --- a/src/log.h +++ b/src/log.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include "compiler.h" diff --git a/src/meson.build b/src/meson.build index 52baf22056..189829391a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,8 +11,8 @@ srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils. 'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c', 'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c', 'vblank.c', 'transition.c', 'wm.c', 'renderer/layout.c', 'renderer/command_builder.c', - 'renderer/renderer.c', 'renderer/damage.c', 'config_libconfig.c', 'inspect.c', 'script.c') ] -picom_inc = include_directories('.') + 'renderer/renderer.c', 'renderer/damage.c', 'config_libconfig.c', 'inspect.c', 'script.c', 'api.c') ] +picom_inc = include_directories(['.', '../include']) cflags = [] @@ -86,7 +86,8 @@ subdir('backend') picom = executable('picom', srcs, c_args: cflags, dependencies: [ base_deps, deps, test_h_dep ], - install: true, include_directories: picom_inc) + install: true, include_directories: picom_inc, + export_dynamic: true, gnu_symbol_visibility: 'hidden') if get_option('unittest') test('picom unittest', picom, args: [ '--unittest' ]) diff --git a/src/opengl.c b/src/opengl.c index ea09e1cc07..704461fc3f 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -642,7 +642,7 @@ static inline GLuint glx_gen_texture(GLenum tex_tgt, int width, int height) { */ bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, int y, int width, int height) { - if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) { + if (ps->o.legacy_backend != BKEND_GLX && ps->o.legacy_backend != BKEND_XR_GLX_HYBRID) { return true; } @@ -698,7 +698,7 @@ bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, i */ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, int width, int height, bool repeat, const struct glx_fbconfig_info *fbcfg) { - if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) { + if (ps->o.legacy_backend != BKEND_GLX && ps->o.legacy_backend != BKEND_XR_GLX_HYBRID) { return true; } diff --git a/src/options.c b/src/options.c index eb7f3ea8a2..2576bd72ee 100644 --- a/src/options.c +++ b/src/options.c @@ -298,6 +298,18 @@ store_benchmark_wid(const struct picom_option * /*opt*/, const struct picom_arg return true; } +static bool store_backend(const struct picom_option * /*opt*/, const struct picom_arg * /*arg*/, + const char *arg_str, void *output) { + struct options *opt = (struct options *)output; + opt->legacy_backend = parse_backend(arg_str); + opt->backend = backend_find(arg_str); + if (opt->legacy_backend == NUM_BKEND && opt->backend == NULL) { + log_error("Invalid backend: %s", arg_str); + return false; + } + return true; +} + #define WINDOW_SHADER_RULE \ { .parse_prefix = parse_window_shader_prefix_with_cwd, .free_value = free, } @@ -320,6 +332,7 @@ static const struct picom_option picom_options[] = { [314] = {"show-all-xerrors", IGNORE(no_argument)}, ['b'] = {"daemon" , IGNORE(no_argument) , "Daemonize process."}, [256] = {"config" , IGNORE(required_argument), "Path to the configuration file."}, + [307] = {"plugins" , IGNORE(required_argument), "Plugins to load. Can be specified multiple times, each time with a single plugin."}, // Simple flags ['c'] = {"shadow" , ENABLE(shadow_enable) , "Enabled client-side shadows on windows."}, @@ -380,7 +393,7 @@ static const struct picom_option picom_options[] = { "rendered screen. Reduces banding artifacts, but might cause performance " "degradation. Only works with OpenGL."}, [341] = {"no-frame-pacing" , DISABLE(frame_pacing) , "Disable frame pacing. This might increase the latency."}, - [733] = {"legacy-backends" , ENABLE(legacy_backends) , "Use deprecated version of the backends."}, + [733] = {"legacy-backends" , ENABLE(use_legacy_backends) , "Use deprecated version of the backends."}, [800] = {"monitor-repaint" , ENABLE(monitor_repaint) , "Highlight the updated area of the screen. For debugging."}, [801] = {"diagnostics" , ENABLE(print_diagnostics) , "Print diagnostic information"}, [802] = {"debug-mode" , ENABLE(debug_mode) , "Render into a separate window, and don't take over the screen. Useful when " @@ -405,7 +418,7 @@ static const struct picom_option picom_options[] = { [259] = {"shadow-blue" , FLOAT(shadow_blue, 0, 1) , "Blue color value of shadow (0.0 - 1.0, defaults to 0)."}, [261] = {"inactive-dim" , FLOAT(inactive_dim, 0, 1) , "Dim inactive windows. (0.0 - 1.0, defaults to 0)"}, [283] = {"blur-background" , FIXED(blur_method, BLUR_METHOD_KERNEL) , "Blur background of semi-transparent / ARGB windows. May impact performance"}, - [290] = {"backend" , PARSE_WITH(parse_backend, NUM_BKEND, backend) , "Backend. Possible values are: " BACKENDS}, + [290] = {"backend" , DO(store_backend) , "Backend. Possible values are: " BACKENDS}, [293] = {"benchmark" , INTEGER(benchmark, 0, INT_MAX) , "Benchmark mode. Repeatedly paint until reaching the specified cycles."}, [297] = {"active-opacity" , FLOAT(active_opacity, 0, 1) , "Default opacity for active windows. (0.0 - 1.0)"}, [302] = {"resize-damage" , INTEGER(resize_damage, INT_MIN, INT_MAX)}, // only used by legacy backends @@ -483,7 +496,7 @@ static const struct picom_option picom_options[] = { ['z'] = {"clear-shadow" , SAY_DEPRECATED(false, CLEAR_SHADOW_DEPRECATION , IGNORE(no_argument))}, [272] = {"xinerama-shadow-crop", SAY_DEPRECATED(false, "Use --crop-shadow-to-monitor instead.", ENABLE(crop_shadow_to_monitor))}, [287] = {"logpath" , SAY_DEPRECATED(false, "Use --log-file instead." , STRING(logpath))}, - [289] = {"opengl" , SAY_DEPRECATED(false, "Use --backend=glx instead." , FIXED(backend, BKEND_GLX))}, + [289] = {"opengl" , SAY_DEPRECATED(false, "Use --backend=glx instead." , FIXED(legacy_backend, BKEND_GLX))}, [305] = {"shadow-exclude-reg" , SAY_DEPRECATED(true, "Use --clip-shadow-above instead." , REJECT(required_argument))}, #undef CLEAR_SHADOW_DEPRECATION @@ -679,6 +692,7 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all bool *fork, int *exit_code) { setup_longopts(); + scoped_charp current_working_dir = getcwd(NULL, 0); int o = 0, longopt_idx = -1; // Pre-parse the command line arguments to check for --config and invalid @@ -694,7 +708,6 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all } else if (o == 'h') { usage(argv[0], 0); return true; - } else if (o == 'b') { *fork = true; } else if (o == 314) { @@ -702,10 +715,17 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all } else if (o == 318) { printf("%s\n", PICOM_VERSION); return true; + } else if (o == 307) { + // --plugin + if (!load_plugin(optarg, current_working_dir)) { + log_error("Failed to load plugin %s", optarg); + goto err; + } } else if (o == '?' || o == ':') { usage(argv[0], 1); goto err; } + // TODO(yshui) maybe log-level should be handled here. } // Check for abundant positional arguments @@ -721,6 +741,143 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all return true; } +static bool sanitize_options(struct options *opt) { + if (opt->use_legacy_backends) { + if (opt->legacy_backend == BKEND_EGL) { + log_error("The egl backend is not supported with " + "--legacy-backends"); + return false; + } + + if (opt->monitor_repaint && opt->legacy_backend != BKEND_XRENDER) { + log_warn("For legacy backends, --monitor-repaint is only " + "implemented for " + "xrender."); + } + + if (opt->debug_mode) { + log_error("Debug mode does not work with the legacy backends."); + return false; + } + + if (opt->transparent_clipping) { + log_error("Transparent clipping does not work with the legacy " + "backends"); + return false; + } + + if (opt->max_brightness < 1.0) { + log_warn("--max-brightness is not supported by the legacy " + "backends. Falling back to 1.0."); + opt->max_brightness = 1.0; + } + + if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) { + log_warn("Dual-kawase blur is not implemented by the legacy " + "backends."); + opt->blur_method = BLUR_METHOD_NONE; + } + + if (opt->number_of_scripts > 0) { + log_warn("Custom animations are not supported by the legacy " + "backends. Disabling animations."); + for (size_t i = 0; i < ARR_SIZE(opt->animations); i++) { + opt->animations[i].script = NULL; + } + for (int i = 0; i < opt->number_of_scripts; i++) { + script_free(opt->all_scripts[i]); + } + free(opt->all_scripts); + opt->all_scripts = NULL; + opt->number_of_scripts = 0; + } + + if (opt->window_shader_fg || opt->window_shader_fg_rules) { + log_warn("The new shader interface is not supported by the " + "legacy glx backend. You may want to use " + "--glx-fshader-win instead."); + opt->window_shader_fg = NULL; + c2_list_free(&opt->window_shader_fg_rules, free); + } + + if (opt->legacy_backend == BKEND_XRENDER) { + bool has_neg = false; + for (int i = 0; i < opt->blur_kernel_count; i++) { + auto kernel = opt->blur_kerns[i]; + for (int j = 0; j < kernel->h * kernel->w; j++) { + if (kernel->data[j] < 0) { + has_neg = true; + break; + } + } + if (has_neg) { + log_warn("A convolution kernel with negative " + "values may not work properly under X " + "Render backend."); + break; + } + } + } + } else { + if (opt->backend == NULL) { + auto valid_backend_name = + backend_find(BACKEND_STRS[opt->legacy_backend]) != NULL; + if (!valid_backend_name) { + log_error("Backend \"%s\" is only available as part of " + "the legacy backends.", + BACKEND_STRS[opt->legacy_backend]); + } else { + // If the backend name is a valid new backend, then + // it must not have been specified by the user, because + // otherwise opt->backend wouldn't be NULL. + log_error("Backend not specified. You must choose one " + "explicitly. Valid ones are: "); + for (auto i = backend_iter(); i; i = backend_iter_next(i)) { + log_error("\t%s", backend_name(i)); + } + } + return false; + } + + if (opt->glx_fshader_win_str) { + log_warn("--glx-fshader-win has been replaced by " + "\"--window-shader-fg\" for the new backends."); + } + + if (opt->max_brightness < 1.0 && opt->use_damage) { + log_warn("--max-brightness requires --no-use-damage. " + "Falling back to 1.0."); + opt->max_brightness = 1.0; + } + } + + if (opt->write_pid_path && *opt->write_pid_path != '/') { + log_warn("--write-pid-path is not an absolute path"); + } + + // Sanitize parameters for dual-filter kawase blur + if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) { + if (opt->blur_strength <= 0 && opt->blur_radius > 500) { + log_warn("Blur radius >500 not supported by dual_kawase " + "method, " + "capping to 500."); + opt->blur_radius = 500; + } + if (opt->blur_strength > 20) { + log_warn("Blur strength >20 not supported by dual_kawase " + "method, " + "capping to 20."); + opt->blur_strength = 20; + } + } + + if (opt->resize_damage < 0) { + log_warn("Negative --resize-damage will not work correctly."); + } + + return true; +} + /** * Process arguments and configuration files. */ @@ -748,83 +905,22 @@ bool get_cfg(options_t *opt, int argc, char *const *argv) { } log_set_level_tls(opt->log_level); - if (opt->monitor_repaint && opt->backend != BKEND_XRENDER && opt->legacy_backends) { - log_warn("--monitor-repaint has no effect when backend is not xrender"); - } - if (opt->window_shader_fg) { scoped_charp cwd = getcwd(NULL, 0); scoped_charp tmp = opt->window_shader_fg; opt->window_shader_fg = locate_auxiliary_file("shaders", tmp, cwd); if (!opt->window_shader_fg) { - log_error("Couldn't find the specified window shader file \"%s\"", tmp); + log_error("Couldn't find the specified window shader " + "file \"%s\"", + tmp); return false; } } - if (opt->write_pid_path && *opt->write_pid_path != '/') { - log_warn("--write-pid-path is not an absolute path"); - } - - if (opt->backend == BKEND_EGL) { - if (opt->legacy_backends) { - log_error("The egl backend is not supported with " - "--legacy-backends"); - return false; - } - log_warn("The egl backend is still experimental, use with care."); - } - - if (!opt->legacy_backends && !backend_list[opt->backend]) { - log_error("Backend \"%s\" is only available as part of the legacy " - "backends.", - BACKEND_STRS[opt->backend]); - return false; - } - - if (opt->debug_mode && opt->legacy_backends) { - log_error("Debug mode does not work with the legacy backends."); + if (!sanitize_options(opt)) { return false; } - if (opt->transparent_clipping && opt->legacy_backends) { - log_error("Transparent clipping does not work with the legacy " - "backends"); - return false; - } - - if (opt->glx_fshader_win_str && !opt->legacy_backends) { - log_warn("--glx-fshader-win has been replaced by " - "\"--window-shader-fg\" for the new backends."); - } - - if (opt->window_shader_fg || opt->window_shader_fg_rules) { - if (opt->backend == BKEND_XRENDER || opt->legacy_backends) { - log_warn(opt->backend == BKEND_XRENDER - ? "Shader interface is not supported by the xrender " - "backend." - : "The new shader interface is not supported by the " - "legacy glx backend. You may want to use " - "--glx-fshader-win instead."); - opt->window_shader_fg = NULL; - c2_list_free(&opt->window_shader_fg_rules, free); - } - } - - // Range checking and option assignments - if (opt->max_brightness < 1.0) { - if (opt->backend == BKEND_XRENDER || opt->legacy_backends) { - log_warn("--max-brightness is not supported by the %s backend. " - "Falling back to 1.0.", - opt->backend == BKEND_XRENDER ? "xrender" : "legacy glx"); - opt->max_brightness = 1.0; - } else if (opt->use_damage) { - log_warn("--max-brightness requires --no-use-damage. Falling " - "back to 1.0."); - opt->max_brightness = 1.0; - } - } - // --blur-background-frame implies --blur-background if (opt->blur_background_frame && opt->blur_method == BLUR_METHOD_NONE) { opt->blur_method = BLUR_METHOD_KERNEL; @@ -846,57 +942,6 @@ bool get_cfg(options_t *opt, int argc, char *const *argv) { CHECK(opt->blur_kernel_count); } - // Sanitize parameters for dual-filter kawase blur - if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE) { - if (opt->blur_strength <= 0 && opt->blur_radius > 500) { - log_warn("Blur radius >500 not supported by dual_kawase method, " - "capping to 500."); - opt->blur_radius = 500; - } - if (opt->blur_strength > 20) { - log_warn("Blur strength >20 not supported by dual_kawase method, " - "capping to 20."); - opt->blur_strength = 20; - } - if (opt->legacy_backends) { - log_warn("Dual-kawase blur is not implemented by the legacy " - "backends."); - } - } - - if (opt->resize_damage < 0) { - log_warn("Negative --resize-damage will not work correctly."); - } - - if (opt->backend == BKEND_XRENDER) { - for (int i = 0; i < opt->blur_kernel_count; i++) { - auto kernel = opt->blur_kerns[i]; - for (int j = 0; j < kernel->h * kernel->w; j++) { - if (kernel->data[j] < 0) { - log_warn("A convolution kernel with negative " - "values may not work properly under X " - "Render backend."); - goto check_end; - } - } - } - check_end:; - } - - if (opt->legacy_backends && opt->number_of_scripts > 0) { - log_warn("Custom animations are not supported by the legacy " - "backends. Disabling animations."); - for (size_t i = 0; i < ARR_SIZE(opt->animations); i++) { - opt->animations[i].script = NULL; - } - for (int i = 0; i < opt->number_of_scripts; i++) { - script_free(opt->all_scripts[i]); - } - free(opt->all_scripts); - opt->all_scripts = NULL; - opt->number_of_scripts = 0; - } - if (opt->fading_enable) { generate_fading_config(opt); } @@ -918,8 +963,8 @@ void options_postprocess_c2_lists(struct c2_state *state, struct x_connection *c c2_list_postprocess(state, c->c, option->corner_radius_rules) && c2_list_postprocess(state, c->c, option->focus_blacklist) && c2_list_postprocess(state, c->c, option->transparent_clipping_blacklist))) { - log_error("Post-processing of conditionals failed, some of your rules " - "might not work"); + log_error("Post-processing of conditionals failed, some of your " + "rules might not work"); } } diff --git a/src/options.h b/src/options.h index d3c43759c5..91b7c0ca35 100644 --- a/src/options.h +++ b/src/options.h @@ -7,10 +7,10 @@ #include #include // for xcb_render_fixed_t +#include + #include "compiler.h" #include "config.h" -#include "types.h" -#include "win.h" // for wintype_t typedef struct session session_t; diff --git a/src/picom.c b/src/picom.c index b5e15b5da3..35212e9dac 100644 --- a/src/picom.c +++ b/src/picom.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -38,17 +38,16 @@ #include #include -#include +#include #include +#include "api_internal.h" #include "common.h" #include "compiler.h" #include "config.h" -#include "err.h" #include "inspect.h" #include "kernel.h" #include "picom.h" -#include "transition.h" #include "win_defs.h" #include "wm.h" #ifdef CONFIG_OPENGL @@ -70,7 +69,6 @@ #include "renderer/layout.h" #include "renderer/renderer.h" #include "statistics.h" -#include "types.h" #include "uthash_extra.h" #include "utils.h" #include "vblank.h" @@ -154,7 +152,7 @@ enum vblank_callback_action check_render_finish(struct vblank_event *e attr_unus struct timespec render_time; bool completed = - ps->backend_data->ops->last_render_time(ps->backend_data, &render_time); + ps->backend_data->ops.last_render_time(ps->backend_data, &render_time); if (!completed) { // Render hasn't completed yet, we can't start another render. // Check again at the next vblank. @@ -440,7 +438,7 @@ void add_damage(session_t *ps, const region_t *damage) { return; } - if (!damage) { + if (!damage || ps->damage_ring.count <= 0) { return; } log_trace("Adding damage: "); @@ -577,14 +575,14 @@ static void destroy_backend(session_t *ps) { HASH_ITER2(ps->shaders, shader) { if (shader->backend_shader != NULL) { - ps->backend_data->ops->destroy_shader(ps->backend_data, - shader->backend_shader); + ps->backend_data->ops.destroy_shader(ps->backend_data, + shader->backend_shader); shader->backend_shader = NULL; } } if (ps->backend_data && ps->root_image) { - ps->backend_data->ops->release_image(ps->backend_data, ps->root_image); + ps->backend_data->ops.release_image(ps->backend_data, ps->root_image); ps->root_image = NULL; } @@ -595,11 +593,11 @@ static void destroy_backend(session_t *ps) { } // deinit backend if (ps->backend_blur_context) { - ps->backend_data->ops->destroy_blur_context( + ps->backend_data->ops.destroy_blur_context( ps->backend_data, ps->backend_blur_context); ps->backend_blur_context = NULL; } - ps->backend_data->ops->deinit(ps->backend_data); + ps->backend_data->ops.deinit(ps->backend_data); ps->backend_data = NULL; } } @@ -637,7 +635,7 @@ static bool initialize_blur(session_t *ps) { enum backend_image_format format = ps->o.dithered_present ? BACKEND_IMAGE_FORMAT_PIXMAP_HIGH : BACKEND_IMAGE_FORMAT_PIXMAP; - ps->backend_blur_context = ps->backend_data->ops->create_blur_context( + ps->backend_blur_context = ps->backend_data->ops.create_blur_context( ps->backend_data, ps->o.blur_method, format, args); return ps->backend_blur_context != NULL; } @@ -658,18 +656,17 @@ static int mark_pixmap_stale(struct win *w, void *data) { /// Init the backend and bind all the window pixmap to backend images static bool initialize_backend(session_t *ps) { - if (!ps->o.legacy_backends) { + if (!ps->o.use_legacy_backends) { assert(!ps->backend_data); // Reinitialize win_data - assert(backend_list[ps->o.backend]); ps->backend_data = - backend_list[ps->o.backend]->init(ps, session_get_target_window(ps)); + backend_init(ps->o.backend, ps, session_get_target_window(ps)); + api_backend_plugins_invoke(backend_name(ps->o.backend), ps->backend_data); if (!ps->backend_data) { log_fatal("Failed to initialize backend, aborting..."); quit(ps); return false; } - ps->backend_data->ops = backend_list[ps->o.backend]; if (!initialize_blur(ps)) { log_fatal("Failed to prepare for background blur, aborting..."); @@ -677,24 +674,30 @@ static bool initialize_backend(session_t *ps) { } // Create shaders - HASH_ITER2(ps->shaders, shader) { - assert(shader->backend_shader == NULL); - shader->backend_shader = ps->backend_data->ops->create_shader( - ps->backend_data, shader->source); - if (shader->backend_shader == NULL) { - log_warn("Failed to create shader for shader file %s, " - "this shader will not be used", - shader->key); - } else { - if (ps->backend_data->ops->get_shader_attributes) { - shader->attributes = - ps->backend_data->ops->get_shader_attributes( - ps->backend_data, shader->backend_shader); + if (!ps->backend_data->ops.create_shader && ps->shaders) { + log_warn("Shaders are not supported by selected backend %s, " + "they will be ignored", + backend_name(ps->o.backend)); + } else { + HASH_ITER2(ps->shaders, shader) { + assert(shader->backend_shader == NULL); + shader->backend_shader = ps->backend_data->ops.create_shader( + ps->backend_data, shader->source); + if (shader->backend_shader == NULL) { + log_warn("Failed to create shader for shader " + "file %s, this shader will not be used", + shader->key); } else { shader->attributes = 0; + if (ps->backend_data->ops.get_shader_attributes) { + shader->attributes = + ps->backend_data->ops.get_shader_attributes( + ps->backend_data, + shader->backend_shader); + } + log_debug("Shader %s has attributes %" PRIu64, + shader->key, shader->attributes); } - log_debug("Shader %s has attributes %" PRIu64, - shader->key, shader->attributes); } } @@ -716,7 +719,7 @@ static bool initialize_backend(session_t *ps) { // The old backends binds pixmap lazily, nothing to do here return true; err: - ps->backend_data->ops->deinit(ps->backend_data); + ps->backend_data->ops.deinit(ps->backend_data); ps->backend_data = NULL; quit(ps); return false; @@ -736,9 +739,9 @@ static void configure_root(session_t *ps) { bool has_root_change = false; if (ps->redirected) { // On root window changes - if (!ps->o.legacy_backends) { + if (!ps->o.use_legacy_backends) { assert(ps->backend_data); - has_root_change = ps->backend_data->ops->root_change != NULL; + has_root_change = ps->backend_data->ops.root_change != NULL; } else { // Old backend can handle root change has_root_change = true; @@ -779,13 +782,13 @@ static void configure_root(session_t *ps) { ps->damage_ring.cursor = ps->damage_ring.count - 1; #ifdef CONFIG_OPENGL // GLX root change callback - if (BKEND_GLX == ps->o.backend && ps->o.legacy_backends) { + if (BKEND_GLX == ps->o.legacy_backend && ps->o.use_legacy_backends) { glx_on_root_change(ps); } #endif if (has_root_change) { if (ps->backend_data != NULL) { - ps->backend_data->ops->root_change(ps->backend_data, ps); + ps->backend_data->ops.root_change(ps->backend_data, ps); } // Old backend's root_change is not a specific function } else { @@ -1079,7 +1082,7 @@ void root_damaged(session_t *ps) { if (ps->backend_data) { if (ps->root_image) { - ps->backend_data->ops->release_image(ps->backend_data, ps->root_image); + ps->backend_data->ops.release_image(ps->backend_data, ps->root_image); ps->root_image = NULL; } auto pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms); @@ -1113,7 +1116,7 @@ void root_damaged(session_t *ps) { : x_get_visual_for_depth(ps->c.screen_info, r->depth); free(r); - ps->root_image = ps->backend_data->ops->bind_pixmap( + ps->root_image = ps->backend_data->ops.bind_pixmap( ps->backend_data, pixmap, x_get_visual_info(&ps->c, visual)); ps->root_image_generation += 1; if (!ps->root_image) { @@ -1375,10 +1378,10 @@ uint8_t session_redirection_mode(session_t *ps) { if (ps->o.debug_mode) { // If the backend is not rendering to the screen, we don't need to // take over the screen. - assert(!ps->o.legacy_backends); + assert(!ps->o.use_legacy_backends); return XCB_COMPOSITE_REDIRECT_AUTOMATIC; } - if (!ps->o.legacy_backends && !backend_list[ps->o.backend]->present) { + if (!ps->o.use_legacy_backends && !backend_can_present(ps->o.backend)) { // if the backend doesn't render anything, we don't need to take over the // screen. return XCB_COMPOSITE_REDIRECT_AUTOMATIC; @@ -1415,11 +1418,11 @@ static bool redirect_start(session_t *ps) { return false; } - if (!ps->o.legacy_backends) { + if (!ps->o.use_legacy_backends) { assert(ps->backend_data); - ps->damage_ring.count = ps->backend_data->ops->max_buffer_age; - ps->layout_manager = - layout_manager_new((unsigned)ps->backend_data->ops->max_buffer_age); + ps->damage_ring.count = + ps->backend_data->ops.max_buffer_age(ps->backend_data); + ps->layout_manager = layout_manager_new((unsigned)ps->damage_ring.count); } else { ps->damage_ring.count = maximum_buffer_age(ps); } @@ -1431,7 +1434,8 @@ static bool redirect_start(session_t *ps) { } ps->frame_pacing = ps->o.frame_pacing && ps->o.vsync; - if ((ps->o.legacy_backends || ps->o.benchmark || !ps->backend_data->ops->last_render_time) && + if ((ps->o.use_legacy_backends || ps->o.benchmark || + !ps->backend_data->ops.last_render_time) && ps->frame_pacing) { // Disable frame pacing if we are using a legacy backend or if we are in // benchmark mode, or if the backend doesn't report render time @@ -1777,7 +1781,7 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) { static int paint = 0; log_verbose("Render start, frame %d", paint); - if (!ps->o.legacy_backends) { + if (!ps->o.use_legacy_backends) { uint64_t after_damage_us = 0; now = get_time_timespec(); auto render_start_us = @@ -2211,7 +2215,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, } } - if (ps->o.legacy_backends) { + if (ps->o.use_legacy_backends) { ps->shadow_context = (void *)gaussian_kernel_autodetect_deviation(ps->o.shadow_radius); sum_kernel_preprocess((conv *)ps->shadow_context); @@ -2332,9 +2336,9 @@ static session_t *session_init(int argc, char **argv, Display *dpy, // The old backends doesn't have a automatic redirection mode log_info("The compositor is started in automatic redirection mode."); - assert(!ps->o.legacy_backends); + assert(!ps->o.use_legacy_backends); - if (backend_list[ps->o.backend]->present) { + if (backend_can_present(ps->o.backend)) { // If the backend has `present`, we couldn't be in automatic // redirection mode unless we are in debug mode. assert(ps->o.debug_mode); @@ -2348,7 +2352,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, apply_driver_workarounds(ps, ps->drivers); // Initialize filters, must be preceded by OpenGL context creation - if (ps->o.legacy_backends && !init_render(ps)) { + if (ps->o.use_legacy_backends && !init_render(ps)) { log_fatal("Failed to initialize the backend"); exit(1); } @@ -2371,7 +2375,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, } } - if (bkend_use_glx(ps) && ps->o.legacy_backends) { + if (bkend_use_glx(ps) && ps->o.use_legacy_backends) { auto gl_logger = gl_string_marker_logger_new(); if (gl_logger) { log_info("Enabling gl string marker"); @@ -2618,7 +2622,7 @@ static void session_destroy(session_t *ps) { ps->damage_ring.x_region = XCB_NONE; } - if (!ps->o.legacy_backends) { + if (!ps->o.use_legacy_backends) { // backend is deinitialized in unredirect() assert(ps->backend_data == NULL); } else { @@ -2635,7 +2639,7 @@ static void session_destroy(session_t *ps) { // Flush all events xcb_aux_sync(ps->c.c); ev_io_stop(ps->loop, &ps->xiow); - if (ps->o.legacy_backends) { + if (ps->o.use_legacy_backends) { free_conv((conv *)ps->shadow_context); } destroy_atoms(ps->atoms); @@ -2684,6 +2688,18 @@ static void session_run(session_t *ps) { #define PICOM_MAIN(...) main(__VA_ARGS__) #endif +/// Early initialization of logging system. To catch early logs, especially +/// from backend entrypoint functions. +static void __attribute__((constructor(201))) init_early_logging(void) { + log_init_tls(); + log_set_level_tls(LOG_LEVEL_WARN); + + auto stderr_logger = stderr_logger_new(); + if (stderr_logger != NULL) { + log_add_target_tls(stderr_logger); + } +} + /** * The function that everybody knows. */ @@ -2692,16 +2708,6 @@ int PICOM_MAIN(int argc, char **argv) { // correctly setlocale(LC_ALL, ""); - // Initialize logging system for early logging - log_init_tls(); - - { - auto stderr_logger = stderr_logger_new(); - if (stderr_logger) { - log_add_target_tls(stderr_logger); - } - } - parse_debug_options(&global_debug_options); int exit_code; diff --git a/src/picom.h b/src/picom.h index e83cf4d1ef..269b9ce47b 100644 --- a/src/picom.h +++ b/src/picom.h @@ -6,22 +6,20 @@ // === Includes === +#include #include #include #include #include -#include -#include "backend/backend.h" +#include + #include "c2.h" #include "common.h" -#include "compiler.h" #include "config.h" #include "log.h" // XXX clean up #include "region.h" #include "render.h" -#include "types.h" -#include "utils.h" #include "win.h" #include "x.h" diff --git a/src/region.h b/src/region.h index c0d072154c..838c71f0c4 100644 --- a/src/region.h +++ b/src/region.h @@ -2,12 +2,12 @@ // Copyright (c) 2018 Yuxuan Shui #pragma once #include -#include #include #include +#include + #include "log.h" -#include "types.h" #include "utils.h" typedef struct pixman_region32 pixman_region32_t; diff --git a/src/render.c b/src/render.c index 77a85a2e1f..10a79a1da0 100644 --- a/src/render.c +++ b/src/render.c @@ -10,9 +10,10 @@ #include #include +#include + #include "common.h" #include "options.h" -#include "transition.h" #ifdef CONFIG_OPENGL #include "backend/gl/glx.h" @@ -29,13 +30,11 @@ #include "kernel.h" #include "log.h" #include "region.h" -#include "types.h" #include "utils.h" #include "vsync.h" #include "win.h" #include "x.h" -#include "backend/backend.h" #include "backend/backend_common.h" #include "render.h" @@ -113,7 +112,8 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h * Check if current backend uses XRender for rendering. */ static inline bool bkend_use_xrender(session_t *ps) { - return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; + return BKEND_XRENDER == ps->o.legacy_backend || + BKEND_XR_GLX_HYBRID == ps->o.legacy_backend; } int maximum_buffer_age(session_t *ps) { @@ -154,7 +154,7 @@ static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) { /// Set the input/output clip region of the target buffer (not the actual target!) static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg) { - switch (ps->o.backend) { + switch (ps->o.legacy_backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: x_set_picture_clip_region(&ps->c, ps->tgt_buffer.pict, 0, 0, reg); @@ -243,7 +243,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f int fullhei, double opacity, bool argb, bool neg, int cr, xcb_render_picture_t pict, glx_texture_t *ptex, const region_t *reg_paint, const glx_prog_main_t *pprogram, clip_t *clip) { - switch (ps->o.backend) { + switch (ps->o.legacy_backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { auto alpha_step = (int)(opacity * MAX_ALPHA); @@ -381,7 +381,7 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) { } #ifdef CONFIG_OPENGL - if (BKEND_GLX == ps->o.backend && !glx_tex_bound(ppaint->ptex, XCB_NONE)) { + if (BKEND_GLX == ps->o.legacy_backend && !glx_tex_bound(ppaint->ptex, XCB_NONE)) { return false; } #endif @@ -562,7 +562,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) dim_opacity *= window_opacity; } - switch (ps->o.backend) { + switch (ps->o.legacy_backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { auto cval = (uint16_t)(0xffff * dim_opacity); @@ -656,7 +656,7 @@ static bool get_root_tile(session_t *ps) { ps->root_tile_fill = fill; ps->root_tile_paint.pixmap = pixmap; #ifdef CONFIG_OPENGL - if (BKEND_GLX == ps->o.backend) { + if (BKEND_GLX == ps->o.legacy_backend) { return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, visual, false); } #endif @@ -775,7 +775,8 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) { bool should_clip = (w->corner_radius > 0) && (!ps->o.wintype_option[w->window_type].full_shadow); if (should_clip) { - if (ps->o.backend == BKEND_XRENDER || ps->o.backend == BKEND_XR_GLX_HYBRID) { + if (ps->o.legacy_backend == BKEND_XRENDER || + ps->o.legacy_backend == BKEND_XR_GLX_HYBRID) { uint32_t max_ntraps = to_u32_checked(w->corner_radius); xcb_render_trapezoid_t traps[4 * max_ntraps + 3]; uint32_t n = make_rounded_window_shape( @@ -913,7 +914,7 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t factor_center = pct * 8.0 / (1.1 - pct); } - switch (ps->o.backend) { + switch (ps->o.legacy_backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: { // Normalize blur kernels @@ -1043,14 +1044,14 @@ void paint_all(session_t *ps, struct managed_win *t) { } } - if (BKEND_GLX != ps->o.backend) { + if (BKEND_GLX != ps->o.legacy_backend) { ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap( &ps->c, ps->c.screen_info->root_visual, ps->tgt_buffer.pixmap, 0, 0); } } - if (BKEND_XRENDER == ps->o.backend) { + if (BKEND_XRENDER == ps->o.legacy_backend) { x_set_picture_clip_region(&ps->c, ps->tgt_picture, 0, 0, ®ion); } @@ -1170,7 +1171,7 @@ void paint_all(session_t *ps, struct managed_win *t) { #ifdef CONFIG_OPENGL // If rounded corners backup the region first - if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) { + if (w->corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) { const int16_t x = w->g.x; const int16_t y = w->g.y; auto const wid = to_u16_checked(w->widthb); @@ -1193,7 +1194,7 @@ void paint_all(session_t *ps, struct managed_win *t) { #ifdef CONFIG_OPENGL // Rounded corners for XRender is implemented inside render() // Round window corners - if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) { + if (w->corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) { auto const wid = to_u16_checked(w->widthb); auto const hei = to_u16_checked(w->heightb); glx_round_corners_dst(ps, w, w->glx_texture_bg, w->g.x, @@ -1237,7 +1238,7 @@ void paint_all(session_t *ps, struct managed_win *t) { auto rwidth = to_u16_checked(ps->root_width); auto rheight = to_u16_checked(ps->root_height); - switch (ps->o.backend) { + switch (ps->o.legacy_backend) { case BKEND_XRENDER: if (ps->o.monitor_repaint) { // Copy the screen content to a new picture, and highlight the @@ -1375,7 +1376,7 @@ static bool init_alpha_picts(session_t *ps) { } bool init_render(session_t *ps) { - if (ps->o.backend == BKEND_DUMMY) { + if (ps->o.legacy_backend == BKEND_DUMMY) { return false; } @@ -1400,7 +1401,7 @@ bool init_render(session_t *ps) { } // Initialize window GL shader - if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) { + if (BKEND_GLX == ps->o.legacy_backend && ps->o.glx_fshader_win_str) { #ifdef CONFIG_OPENGL if (!glx_load_prog_main(NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win)) { return false; @@ -1429,7 +1430,7 @@ bool init_render(session_t *ps) { ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *); bool ret = false; - if (ps->o.backend == BKEND_GLX) { + if (ps->o.legacy_backend == BKEND_GLX) { #ifdef CONFIG_OPENGL ret = glx_init_blur(ps); #else @@ -1465,7 +1466,7 @@ bool init_render(session_t *ps) { } // Initialize our rounded corners fragment shader - if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) { + if (ps->o.corner_radius > 0 && ps->o.legacy_backend == BKEND_GLX) { #ifdef CONFIG_OPENGL if (!glx_init_rounded_corners(ps)) { log_error("Failed to init rounded corners shader."); diff --git a/src/renderer/command_builder.h b/src/renderer/command_builder.h index 4672f315f3..a33bb985b8 100644 --- a/src/renderer/command_builder.h +++ b/src/renderer/command_builder.h @@ -2,11 +2,13 @@ // Copyright (c) Yuxuan Shui #pragma once -#include "backend/backend.h" -#include "types.h" +#include struct command_builder; +struct backend_command; struct layout; +struct x_monitors; +struct win_option; struct command_builder *command_builder_new(void); void command_builder_free(struct command_builder *); diff --git a/src/renderer/damage.h b/src/renderer/damage.h index 6be5d07671..5111cbccb3 100644 --- a/src/renderer/damage.h +++ b/src/renderer/damage.h @@ -1,6 +1,6 @@ #pragma once -#include "types.h" +#include typedef struct pixman_region32 region_t; struct layout; diff --git a/src/renderer/layout.c b/src/renderer/layout.c index fed6e8560e..1c74299efe 100644 --- a/src/renderer/layout.c +++ b/src/renderer/layout.c @@ -3,11 +3,12 @@ #include #include +#include + #include "command_builder.h" #include "common.h" #include "list.h" #include "region.h" -#include "types.h" #include "utils.h" #include "win.h" #include "wm.h" diff --git a/src/renderer/layout.h b/src/renderer/layout.h index 00fdaa15f9..c50a975e97 100644 --- a/src/renderer/layout.h +++ b/src/renderer/layout.h @@ -4,9 +4,10 @@ #include #include #include -#include "backend/backend.h" + +#include + #include "region.h" -#include "types.h" struct layer_key { /// Window generation, (see `struct wm::generation` for explanation of what a diff --git a/src/renderer/renderer.c b/src/renderer/renderer.c index 679a336f64..d8a4068046 100644 --- a/src/renderer/renderer.c +++ b/src/renderer/renderer.c @@ -69,19 +69,19 @@ static void renderer_reallocate_culled_masks(struct renderer *r, size_t capacity void renderer_free(struct backend_base *backend, struct renderer *r) { if (r->white_image) { - backend->ops->release_image(backend, r->white_image); + backend->ops.release_image(backend, r->white_image); } if (r->black_image) { - backend->ops->release_image(backend, r->black_image); + backend->ops.release_image(backend, r->black_image); } if (r->back_image) { - backend->ops->release_image(backend, r->back_image); + backend->ops.release_image(backend, r->back_image); } if (r->monitor_repaint_pixel) { - backend->ops->release_image(backend, r->monitor_repaint_pixel); + backend->ops.release_image(backend, r->monitor_repaint_pixel); } if (r->shadow_blur_context) { - backend->ops->destroy_blur_context(backend, r->shadow_blur_context); + backend->ops.destroy_blur_context(backend, r->shadow_blur_context); } if (r->shadow_kernel) { free_conv(r->shadow_kernel); @@ -97,7 +97,7 @@ void renderer_free(struct backend_base *backend, struct renderer *r) { } if (r->monitor_repaint_copy) { for (int i = 0; i < r->max_buffer_age; i++) { - backend->ops->release_image(backend, r->monitor_repaint_copy[i]); + backend->ops.release_image(backend, r->monitor_repaint_copy[i]); } free(r->monitor_repaint_copy); } @@ -109,21 +109,21 @@ static bool renderer_init(struct renderer *renderer, struct backend_base *backend, double shadow_radius, struct color shadow_color, bool dithered_present) { auto has_high_precision = - backend->ops->is_format_supported(backend, BACKEND_IMAGE_FORMAT_PIXMAP_HIGH); + backend->ops.is_format_supported(backend, BACKEND_IMAGE_FORMAT_PIXMAP_HIGH); renderer->format = has_high_precision && dithered_present ? BACKEND_IMAGE_FORMAT_PIXMAP_HIGH : BACKEND_IMAGE_FORMAT_PIXMAP; renderer->back_image = NULL; renderer->white_image = - backend->ops->new_image(backend, renderer->format, (ivec2){1, 1}); - if (!renderer->white_image || !backend->ops->clear(backend, renderer->white_image, - (struct color){1, 1, 1, 1})) { + backend->ops.new_image(backend, renderer->format, (ivec2){1, 1}); + if (!renderer->white_image || !backend->ops.clear(backend, renderer->white_image, + (struct color){1, 1, 1, 1})) { return false; } renderer->black_image = - backend->ops->new_image(backend, renderer->format, (ivec2){1, 1}); - if (!renderer->black_image || !backend->ops->clear(backend, renderer->black_image, - (struct color){0, 0, 0, 1})) { + backend->ops.new_image(backend, renderer->format, (ivec2){1, 1}); + if (!renderer->black_image || !backend->ops.clear(backend, renderer->black_image, + (struct color){0, 0, 0, 1})) { return false; } renderer->canvas_size = (ivec2){0, 0}; @@ -132,7 +132,7 @@ renderer_init(struct renderer *renderer, struct backend_base *backend, .size = (int)shadow_radius, .deviation = gaussian_kernel_std_for_size(shadow_radius, 0.5 / 256.0), }; - renderer->shadow_blur_context = backend->ops->create_blur_context( + renderer->shadow_blur_context = backend->ops.create_blur_context( backend, BLUR_METHOD_GAUSSIAN, BACKEND_IMAGE_FORMAT_MASK, &args); if (!renderer->shadow_blur_context) { log_error("Failed to create shadow blur context"); @@ -154,7 +154,7 @@ renderer_init(struct renderer *renderer, struct backend_base *backend, } sum_kernel_preprocess(renderer->shadow_kernel); } - renderer->max_buffer_age = backend->ops->max_buffer_age + 1; + renderer->max_buffer_age = backend->ops.max_buffer_age(backend) + 1; return true; } @@ -176,23 +176,23 @@ renderer_set_root_size(struct renderer *r, struct backend_base *backend, ivec2 r return true; } if (r->back_image) { - backend->ops->release_image(backend, r->back_image); + backend->ops.release_image(backend, r->back_image); } if (r->back_buffer_copy) { for (int i = 0; i < r->max_buffer_age; i++) { - backend->ops->release_image(backend, r->back_buffer_copy[i]); + backend->ops.release_image(backend, r->back_buffer_copy[i]); } free(r->back_buffer_copy); r->back_buffer_copy = NULL; } if (r->monitor_repaint_copy) { for (int i = 0; i < r->max_buffer_age; i++) { - backend->ops->release_image(backend, r->monitor_repaint_copy[i]); + backend->ops.release_image(backend, r->monitor_repaint_copy[i]); } free(r->monitor_repaint_copy); r->monitor_repaint_copy = NULL; } - r->back_image = backend->ops->new_image(backend, r->format, root_size); + r->back_image = backend->ops.new_image(backend, r->format, root_size); if (r->back_image != NULL) { r->canvas_size = root_size; return true; @@ -205,16 +205,16 @@ static bool renderer_bind_mask(struct renderer *r, struct backend_base *backend, struct managed_win *w) { ivec2 size = {.width = w->widthb, .height = w->heightb}; bool succeeded = false; - auto image = backend->ops->new_image(backend, BACKEND_IMAGE_FORMAT_MASK, size); - if (!image || !backend->ops->clear(backend, image, (struct color){0, 0, 0, 0})) { + auto image = backend->ops.new_image(backend, BACKEND_IMAGE_FORMAT_MASK, size); + if (!image || !backend->ops.clear(backend, image, (struct color){0, 0, 0, 0})) { log_error("Failed to create mask image"); goto err; } auto bound_region_local = win_get_bounding_shape_global_by_val(w); pixman_region32_translate(&bound_region_local, -w->g.x, -w->g.y); - succeeded = backend->ops->copy_area(backend, (ivec2){0, 0}, (image_handle)image, - r->white_image, &bound_region_local); + succeeded = backend->ops.copy_area(backend, (ivec2){0, 0}, (image_handle)image, + r->white_image, &bound_region_local); pixman_region32_fini(&bound_region_local); if (!succeeded) { log_error("Failed to fill the mask"); @@ -225,7 +225,7 @@ renderer_bind_mask(struct renderer *r, struct backend_base *backend, struct mana err: if (image != NULL) { - backend->ops->release_image(backend, image); + backend->ops.release_image(backend, image); } return succeeded; } @@ -243,11 +243,11 @@ image_handle renderer_shadow_from_mask(struct renderer *r, struct backend_base * // Apply the properties on the mask image and blit the result into a larger // image, each side larger by `2 * radius` so there is space for blurring. - normalized_mask_image = backend->ops->new_image( + normalized_mask_image = backend->ops.new_image( backend, BACKEND_IMAGE_FORMAT_MASK, (ivec2){mask_size.width + 2 * radius, mask_size.height + 2 * radius}); - if (!normalized_mask_image || !backend->ops->clear(backend, normalized_mask_image, - (struct color){0, 0, 0, 0})) { + if (!normalized_mask_image || !backend->ops.clear(backend, normalized_mask_image, + (struct color){0, 0, 0, 0})) { log_error("Failed to create mask image"); goto out; } @@ -276,8 +276,8 @@ image_handle renderer_shadow_from_mask(struct renderer *r, struct backend_base * pixman_region32_init_rect(&target_mask, radius, radius, (unsigned)mask_size.width, (unsigned)mask_size.height); - succeeded = backend->ops->blit(backend, (ivec2){radius, radius}, - normalized_mask_image, &args); + succeeded = backend->ops.blit(backend, (ivec2){radius, radius}, + normalized_mask_image, &args); pixman_region32_fini(&target_mask); if (!succeeded) { log_error("Failed to blit for shadow generation"); @@ -297,7 +297,7 @@ image_handle renderer_shadow_from_mask(struct renderer *r, struct backend_base * (unsigned)(mask_size.width + 2 * radius), (unsigned)(mask_size.height + 2 * radius)); succeeded = - backend->ops->blur(backend, (ivec2){0, 0}, normalized_mask_image, &args); + backend->ops.blur(backend, (ivec2){0, 0}, normalized_mask_image, &args); pixman_region32_fini(&target_mask); if (!succeeded) { log_error("Failed to blur for shadow generation"); @@ -306,19 +306,19 @@ image_handle renderer_shadow_from_mask(struct renderer *r, struct backend_base * } // Finally, we blit with this mask to colorize the shadow succeeded = false; - shadow_image = backend->ops->new_image( + shadow_image = backend->ops.new_image( backend, BACKEND_IMAGE_FORMAT_PIXMAP, (ivec2){mask_size.width + 2 * radius, mask_size.height + 2 * radius}); if (!shadow_image || - !backend->ops->clear(backend, shadow_image, (struct color){0, 0, 0, 0})) { + !backend->ops.clear(backend, shadow_image, (struct color){0, 0, 0, 0})) { log_error("Failed to allocate shadow image"); goto out; } shadow_color_pixel = - backend->ops->new_image(backend, BACKEND_IMAGE_FORMAT_PIXMAP, (ivec2){1, 1}); + backend->ops.new_image(backend, BACKEND_IMAGE_FORMAT_PIXMAP, (ivec2){1, 1}); if (!shadow_color_pixel || - !backend->ops->clear(backend, shadow_color_pixel, r->shadow_color)) { + !backend->ops.clear(backend, shadow_color_pixel, r->shadow_color)) { log_error("Failed to create shadow color image"); goto out; } @@ -348,19 +348,19 @@ image_handle renderer_shadow_from_mask(struct renderer *r, struct backend_base * }; pixman_region32_init_rect(&target_mask, 0, 0, (unsigned)shadow_size.width, (unsigned)shadow_size.height); - succeeded = backend->ops->blit(backend, (ivec2){0, 0}, shadow_image, &args); + succeeded = backend->ops.blit(backend, (ivec2){0, 0}, shadow_image, &args); pixman_region32_fini(&target_mask); out: if (normalized_mask_image) { - backend->ops->release_image(backend, normalized_mask_image); + backend->ops.release_image(backend, normalized_mask_image); } if (shadow_color_pixel) { - backend->ops->release_image(backend, shadow_color_pixel); + backend->ops.release_image(backend, shadow_color_pixel); } if (!succeeded && shadow_image) { log_error("Failed to draw shadow image"); - backend->ops->release_image(backend, shadow_image); + backend->ops.release_image(backend, shadow_image); shadow_image = NULL; } return shadow_image; @@ -368,7 +368,7 @@ image_handle renderer_shadow_from_mask(struct renderer *r, struct backend_base * static bool renderer_bind_shadow(struct renderer *r, struct backend_base *backend, struct managed_win *w) { - if (backend->ops->quirks(backend) & BACKEND_QUIRK_SLOW_BLUR) { + if (backend->ops.quirks(backend) & BACKEND_QUIRK_SLOW_BLUR) { xcb_pixmap_t shadow = XCB_NONE; xcb_render_picture_t pict = XCB_NONE; @@ -379,7 +379,7 @@ static bool renderer_bind_shadow(struct renderer *r, struct backend_base *backen auto visual = x_get_visual_for_standard(backend->c, XCB_PICT_STANDARD_ARGB_32); - w->shadow_image = backend->ops->bind_pixmap( + w->shadow_image = backend->ops.bind_pixmap( backend, shadow, x_get_visual_info(backend->c, visual)); } else { if (!w->mask_image && !renderer_bind_mask(r, backend, w)) { @@ -467,16 +467,16 @@ void renderer_ensure_images_ready(struct renderer *r, struct backend_base *backe bool monitor_repaint) { if (monitor_repaint) { if (!r->monitor_repaint_pixel) { - r->monitor_repaint_pixel = backend->ops->new_image( + r->monitor_repaint_pixel = backend->ops.new_image( backend, BACKEND_IMAGE_FORMAT_PIXMAP, (ivec2){1, 1}); BUG_ON(!r->monitor_repaint_pixel); - backend->ops->clear(backend, r->monitor_repaint_pixel, - (struct color){.alpha = 0.5, .red = 0.5}); + backend->ops.clear(backend, r->monitor_repaint_pixel, + (struct color){.alpha = 0.5, .red = 0.5}); } if (!r->monitor_repaint_copy) { r->monitor_repaint_copy = ccalloc(r->max_buffer_age, image_handle); for (int i = 0; i < r->max_buffer_age; i++) { - r->monitor_repaint_copy[i] = backend->ops->new_image( + r->monitor_repaint_copy[i] = backend->ops.new_image( backend, BACKEND_IMAGE_FORMAT_PIXMAP, (ivec2){.width = r->canvas_size.width, .height = r->canvas_size.height}); @@ -493,10 +493,10 @@ void renderer_ensure_images_ready(struct renderer *r, struct backend_base *backe if (global_debug_options.consistent_buffer_age && !r->back_buffer_copy) { r->back_buffer_copy = ccalloc(r->max_buffer_age, image_handle); for (int i = 0; i < r->max_buffer_age; i++) { - r->back_buffer_copy[i] = backend->ops->new_image( - backend, BACKEND_IMAGE_FORMAT_PIXMAP, - (ivec2){.width = r->canvas_size.width, - .height = r->canvas_size.height}); + r->back_buffer_copy[i] = + backend->ops.new_image(backend, BACKEND_IMAGE_FORMAT_PIXMAP, + (ivec2){.width = r->canvas_size.width, + .height = r->canvas_size.height}); BUG_ON(!r->back_buffer_copy[i]); } } @@ -554,19 +554,19 @@ bool renderer_render(struct renderer *r, struct backend_base *backend, pixman_region32_init(&damage_region); pixman_region32_copy(&damage_region, &screen_region); ivec2 blur_size = {}; - if (backend->ops->get_blur_size && blur_context) { - backend->ops->get_blur_size(blur_context, &blur_size.width, &blur_size.height); + if (backend->ops.get_blur_size && blur_context) { + backend->ops.get_blur_size(blur_context, &blur_size.width, &blur_size.height); } auto buffer_age = - (use_damage || monitor_repaint) ? backend->ops->buffer_age(backend) : 0; + (use_damage || monitor_repaint) ? backend->ops.buffer_age(backend) : 0; if (buffer_age > 0 && global_debug_options.consistent_buffer_age) { int past_frame = (r->frame_index + r->max_buffer_age - buffer_age) % r->max_buffer_age; region_t region; pixman_region32_init_rect(®ion, 0, 0, (unsigned)r->canvas_size.width, (unsigned)r->canvas_size.height); - backend->ops->copy_area(backend, (ivec2){}, backend->ops->back_buffer(backend), - r->back_buffer_copy[past_frame], ®ion); + backend->ops.copy_area(backend, (ivec2){}, backend->ops.back_buffer(backend), + r->back_buffer_copy[past_frame], ®ion); pixman_region32_fini(®ion); } if (buffer_age > 0 && (unsigned)buffer_age <= layout_manager_max_buffer_age(lm)) { @@ -595,17 +595,17 @@ bool renderer_render(struct renderer *r, struct backend_base *backend, xcb_sync_reset_fence(backend->c->c, xsync_fence)); } - if (backend->ops->prepare) { - backend->ops->prepare(backend, &layout->commands[0].target_mask); + if (backend->ops.prepare) { + backend->ops.prepare(backend, &layout->commands[0].target_mask); } if (monitor_repaint && buffer_age <= r->max_buffer_age) { // Restore the area of back buffer that was tainted by monitor repaint int past_frame = (r->frame_index + r->max_buffer_age - buffer_age) % r->max_buffer_age; - backend->ops->copy_area(backend, (ivec2){}, backend->ops->back_buffer(backend), - r->monitor_repaint_copy[past_frame], - &r->monitor_repaint_region[past_frame]); + backend->ops.copy_area(backend, (ivec2){}, backend->ops.back_buffer(backend), + r->monitor_repaint_copy[past_frame], + &r->monitor_repaint_region[past_frame]); } if (!backend_execute(backend, r->back_image, layout->number_of_commands, @@ -616,9 +616,9 @@ bool renderer_render(struct renderer *r, struct backend_base *backend, if (monitor_repaint) { // Keep a copy of un-tainted back image - backend->ops->copy_area(backend, (ivec2){}, - r->monitor_repaint_copy[r->frame_index], - r->back_image, &damage_region); + backend->ops.copy_area(backend, (ivec2){}, + r->monitor_repaint_copy[r->frame_index], + r->back_image, &damage_region); pixman_region32_copy(&r->monitor_repaint_region[r->frame_index], &damage_region); struct backend_blit_args blit = { @@ -631,25 +631,23 @@ bool renderer_render(struct renderer *r, struct backend_base *backend, .scale = SCALE_IDENTITY, }; log_trace("Blit for monitor repaint"); - backend->ops->blit(backend, (ivec2){}, r->back_image, &blit); + backend->ops.blit(backend, (ivec2){}, r->back_image, &blit); } - backend->ops->copy_area_quantize(backend, (ivec2){}, - backend->ops->back_buffer(backend), - r->back_image, &damage_region); + backend->ops.copy_area_quantize(backend, (ivec2){}, backend->ops.back_buffer(backend), + r->back_image, &damage_region); if (global_debug_options.consistent_buffer_age) { region_t region; pixman_region32_init_rect(®ion, 0, 0, (unsigned)r->canvas_size.width, (unsigned)r->canvas_size.height); - backend->ops->copy_area(backend, (ivec2){}, - r->back_buffer_copy[r->frame_index], - backend->ops->back_buffer(backend), ®ion); + backend->ops.copy_area(backend, (ivec2){}, r->back_buffer_copy[r->frame_index], + backend->ops.back_buffer(backend), ®ion); pixman_region32_fini(®ion); } - if (backend->ops->present) { - backend->ops->present(backend); + if (backend->ops.present && !backend->ops.present(backend)) { + log_warn("Failed to present the frame"); } // "Un-cull" the render commands, so later damage calculation using those commands diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index 7354b99873..3a4b655675 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -4,7 +4,8 @@ #pragma once #include #include -#include "types.h" + +#include struct renderer; struct layout_manager; diff --git a/src/utils.h b/src/utils.h index 280f4c7977..115ab35398 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2,9 +2,7 @@ // Copyright (c) 2018 Yuxuan Shui #pragma once #include -#include #include -#include #include #include #include @@ -12,13 +10,13 @@ #include #include +#include #include #include #include "compiler.h" #include "log.h" -#include "types.h" #define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) diff --git a/src/win.c b/src/win.c index 69b27937f6..a44880d9be 100644 --- a/src/win.c +++ b/src/win.c @@ -15,8 +15,9 @@ #include #include +#include + #include "atom.h" -#include "backend/backend.h" #include "c2.h" #include "common.h" #include "compiler.h" @@ -27,10 +28,6 @@ #include "picom.h" #include "region.h" #include "render.h" -#include "string_utils.h" -#include "transition.h" -#include "types.h" -#include "uthash_extra.h" #include "utils.h" #include "win_defs.h" #include "wm.h" @@ -334,7 +331,7 @@ static inline void win_release_pixmap(backend_t *base, struct managed_win *w) { assert(w->win_image); if (w->win_image) { xcb_pixmap_t pixmap = XCB_NONE; - pixmap = base->ops->release_image(base, w->win_image); + pixmap = base->ops.release_image(base, w->win_image); w->win_image = NULL; // Bypassing win_set_flags, because `w` might have been destroyed w->flags |= WIN_FLAGS_PIXMAP_NONE; @@ -348,7 +345,7 @@ static inline void win_release_shadow(backend_t *base, struct managed_win *w) { if (w->shadow_image) { assert(w->shadow); xcb_pixmap_t pixmap = XCB_NONE; - pixmap = base->ops->release_image(base, w->shadow_image); + pixmap = base->ops.release_image(base, w->shadow_image); w->shadow_image = NULL; if (pixmap != XCB_NONE) { xcb_free_pixmap(base->c->c, pixmap); @@ -359,7 +356,7 @@ static inline void win_release_shadow(backend_t *base, struct managed_win *w) { static inline void win_release_mask(backend_t *base, struct managed_win *w) { if (w->mask_image) { xcb_pixmap_t pixmap = XCB_NONE; - pixmap = base->ops->release_image(base, w->mask_image); + pixmap = base->ops.release_image(base, w->mask_image); w->mask_image = NULL; if (pixmap != XCB_NONE) { xcb_free_pixmap(base->c->c, pixmap); @@ -379,7 +376,7 @@ static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w return false; } log_debug("New named pixmap for %#010x (%s) : %#010x", w->base.id, w->name, pixmap); - w->win_image = b->ops->bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual)); + w->win_image = b->ops.bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual)); if (!w->win_image) { log_error("Failed to bind pixmap"); xcb_free_pixmap(b->c->c, pixmap); diff --git a/src/win.h b/src/win.h index 1f2fe88ab8..19ee1c867d 100644 --- a/src/win.h +++ b/src/win.h @@ -7,9 +7,8 @@ #include #include -#include - -#include "uthash_extra.h" +#include +#include #include "c2.h" #include "compiler.h" @@ -17,8 +16,6 @@ #include "region.h" #include "render.h" #include "script.h" -#include "transition.h" -#include "types.h" #include "utils.h" #include "win_defs.h" #include "x.h" diff --git a/src/x.c b/src/x.c index 78d9c68b57..897f41560a 100644 --- a/src/x.c +++ b/src/x.c @@ -832,11 +832,18 @@ void x_create_convolution_kernel(const conv *kernel, double center, buf[1] = DOUBLE_TO_XFIXED(kernel->h); double sum = center; + bool has_neg = false; for (int i = 0; i < kernel->w * kernel->h; i++) { if (i == kernel->w * kernel->h / 2) { + // Ignore center continue; } sum += kernel->data[i]; + if (kernel->data[i] < 0 && !has_neg) { + has_neg = true; + log_warn("A X convolution kernel with negative values may not " + "work properly."); + } } // Note for floating points a / b != a * (1 / b), but this shouldn't have any real diff --git a/src/x.h b/src/x.h index 3a7b0fd9b4..3a15d6d02d 100644 --- a/src/x.h +++ b/src/x.h @@ -43,21 +43,6 @@ typedef struct winprop_info { uint32_t length; } winprop_info_t; -struct xvisual_info { - /// Bit depth of the red component - int red_size; - /// Bit depth of the green component - int green_size; - /// Bit depth of the blue component - int blue_size; - /// Bit depth of the alpha component - int alpha_size; - /// The depth of X visual - int visual_depth; - - xcb_visualid_t visual; -}; - enum pending_reply_action { PENDING_REPLY_ACTION_IGNORE, PENDING_REPLY_ACTION_ABORT,