Skip to content

Commit

Permalink
backend: prepare to pluginify the backends
Browse files Browse the repository at this point in the history
Instead of a fixed table, allow an arbitrary number of backends to be
registered through `backend_register`.

Slightly refactored configuration validation.

As a side-effect, you now have to explicitly specify a backend, because
due to the dynamic nature of backends, there is no way to choose a
default.

Signed-off-by: Yuxuan Shui <[email protected]>
  • Loading branch information
yshui committed May 23, 2024
1 parent f28df36 commit 7d9570a
Show file tree
Hide file tree
Showing 18 changed files with 394 additions and 223 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
* `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

Expand Down
75 changes: 61 additions & 14 deletions src/backend/backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,67 @@
#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;

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 %lu, expected %lu",
name, major, PICOM_BACKEND_MAJOR);
return false;
}
if (minor > PICOM_BACKEND_MINOR) {
log_error("Backend %s has incompatible minor version %lu, 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");
Expand Down
28 changes: 25 additions & 3 deletions src/backend/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#include "types.h"
#include "x.h"

#define PICOM_BACKEND_MAJOR (1UL)
#define PICOM_BACKEND_MINOR (0UL)
#define PICOM_BACKEND_MAKE_VERSION(major, minor) ((major) * 1000 + (minor))

typedef struct session session_t;
struct managed_win;

Expand Down Expand Up @@ -450,12 +454,30 @@ struct backend_operations {

enum device_status (*device_status)(backend_t *backend_data);
};

extern struct backend_operations *backend_list[];

struct backend_info;
bool backend_execute(struct backend_base *backend, image_handle target, unsigned ncmds,
const struct backend_command cmds[ncmds]);

/// 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);
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)
7 changes: 7 additions & 0 deletions src/backend/dummy/dummy.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,10 @@ struct backend_operations dummy_ops = {
.destroy_blur_context = dummy_destroy_blur_context,
.get_blur_size = dummy_get_blur_size,
};

BACKEND_ENTRYPOINT(egl_register) {
if (!backend_register(PICOM_BACKEND_MAJOR, PICOM_BACKEND_MINOR, "dummy",
dummy_ops.init, false)) {
log_error("Failed to register dummy backend");
}
}
9 changes: 9 additions & 0 deletions src/backend/gl/egl.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,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[]){
Expand Down Expand Up @@ -389,3 +391,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");
}
}
7 changes: 7 additions & 0 deletions src/backend/gl/glx.c
Original file line number Diff line number Diff line change
Expand Up @@ -575,3 +575,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");
}
}
14 changes: 13 additions & 1 deletion src/backend/xrender/xrender.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,12 @@ static void xrender_get_blur_size(void *blur_context, int *width, int *height) {
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);
Expand Down Expand Up @@ -1043,4 +1048,11 @@ struct backend_operations xrender_ops = {
// end
};

BACKEND_ENTRYPOINT(glx_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:
2 changes: 1 addition & 1 deletion src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,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;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,8 @@ void *parse_window_shader_prefix_with_cwd(const char *src, const char **end, voi
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,
Expand Down
9 changes: 5 additions & 4 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,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
Expand Down Expand Up @@ -407,7 +409,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;
}

Expand Down
7 changes: 4 additions & 3 deletions src/config_libconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -743,9 +743,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;
}
}
Expand Down
15 changes: 13 additions & 2 deletions src/dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) { \
Expand All @@ -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));
Expand All @@ -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);
Expand Down
20 changes: 10 additions & 10 deletions src/diagnostic.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/opengl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down
Loading

0 comments on commit 7d9570a

Please sign in to comment.