config: support loading plugins

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-05-23 06:25:57 +01:00
parent e512542d71
commit 7ae185850f
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
6 changed files with 59 additions and 9 deletions

View File

@ -26,6 +26,7 @@
! 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

View File

@ -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')

View File

@ -3,13 +3,11 @@
// Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
#include <ctype.h>
#include <errno.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -20,16 +18,11 @@
#include <test.h>
#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,6 +625,18 @@ 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){

View File

@ -354,6 +354,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);

View File

@ -628,6 +628,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);

View File

@ -332,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."},
@ -691,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
@ -706,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) {
@ -714,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