diff --git a/completion/zsh/_libinput b/completion/zsh/_libinput index 003e2fba..05323bb4 100644 --- a/completion/zsh/_libinput +++ b/completion/zsh/_libinput @@ -93,6 +93,9 @@ __all_seats() + '(natural-scrolling)' \ '--enable-natural-scrolling[Enable natural scrolling]' \ '--disable-natural-scrolling[Disable natural scrolling]' \ + + '(plugins)' \ + '--enable-plugins[Enable plugins]' \ + '--disable-plugins[Disable plugins]' \ + '(tap-to-click)' \ '--enable-tap[Enable tap-to-click]' \ '--disable-tap[Disable tap-to-click]' diff --git a/meson.build b/meson.build index 44a6a7c1..3da428cb 100644 --- a/meson.build +++ b/meson.build @@ -168,6 +168,8 @@ dep_libevdev = dependency('libevdev', version: '>= 1.10.0') dep_lm = cc.find_library('m', required : false) dep_rt = cc.find_library('rt', required : false) +config_h.set10('HAVE_PLUGINS', true) + # Include directories includes_include = include_directories('include') includes_src = include_directories('src') @@ -381,6 +383,9 @@ if get_option('internal-event-debugging') config_h.set('EVENT_DEBUGGING', 1) endif +config_h.set_quoted('LIBINPUT_PLUGIN_LIBDIR', dir_lib / 'libinput' / 'plugins') +config_h.set_quoted('LIBINPUT_PLUGIN_ETCDIR', dir_etc / 'libinput' / 'plugins') + install_headers('src/libinput.h') src_libinput = src_libfilter + [ 'src/libinput.c', diff --git a/src/libinput-plugin.c b/src/libinput-plugin.c index 107f8cc7..181b8cf7 100644 --- a/src/libinput-plugin.c +++ b/src/libinput-plugin.c @@ -327,6 +327,55 @@ libinput_plugin_notify_device_removed(struct libinput_plugin *plugin, plugin->interface->device_removed(plugin, device); } +LIBINPUT_EXPORT void +libinput_plugin_system_append_path(struct libinput *libinput, const char *path) +{ + if (libinput->plugin_system.loaded) { + log_bug_client(libinput, "plugin system already initialized\n"); + return; + } + + if (strv_find(libinput->plugin_system.directories, path, NULL)) + return; + + libinput->plugin_system.directories = + strv_append_strdup(libinput->plugin_system.directories, path); +} + +LIBINPUT_EXPORT void +libinput_plugin_system_append_default_paths(struct libinput *libinput) +{ + if (libinput->plugin_system.loaded) { + log_bug_client(libinput, "plugin system already initialized\n"); + return; + } + + libinput_plugin_system_append_path(libinput, LIBINPUT_PLUGIN_ETCDIR); + libinput_plugin_system_append_path(libinput, LIBINPUT_PLUGIN_LIBDIR); +} + +LIBINPUT_EXPORT int +libinput_plugin_system_load_plugins(struct libinput *libinput, + enum libinput_plugins_flags flags) +{ + if (libinput->plugin_system.loaded) { + log_bug_client(libinput, "%s() called twice\n", __func__); + return 0; + } + + libinput_plugin_system_load_internal_plugins(libinput, + &libinput->plugin_system); + libinput->plugin_system.loaded = true; + + libinput_plugin_system_run(&libinput->plugin_system); + +#if !HAVE_PLUGINS + return -ENOSYS; +#else + return 0; +#endif +} + void libinput_plugin_system_run(struct libinput_plugin_system *system) { @@ -403,6 +452,7 @@ libinput_plugin_system_load_internal_plugins(struct libinput *libinput, * actually connected to anything yet */ libinput_evdev_dispatch_plugin(libinput); } + void libinput_plugin_system_destroy(struct libinput_plugin_system *system) { diff --git a/src/libinput.h b/src/libinput.h index d99d55ed..70a14372 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -3690,6 +3690,89 @@ libinput_path_add_device(struct libinput *libinput, const char *path); void libinput_path_remove_device(struct libinput_device *device); +/** + * @ingroup base + * + * Appends the given directory path to the libinput plugin lookup path. + * If the path is already in the lookup paths, it is ignored. + * + * A path's priority is determined by its position in the list; the first + * path in the list has the highest priority. + * + * Plugin lookup is performed across all paths in lexical order. If + * a plugin exists in multiple paths, the one in the highest priority + * path (i.e. front of the list) is used. + * + * Paths are not traversed recursively. + * + * Plugins that have a 0 byte size shadow any plugins with the same name + * but do not provide any fuctionality. This allows disabling a plugin + * by simply dropping an empty file in a higher-priority directory. + * + * This function must be called before i + * libinput_plugin_system_load_plugins(). + * + * @see libinput_plugin_system_append_default_paths + * + * @since 1.29 + */ +void +libinput_plugin_system_append_path(struct libinput *libinput, const char *path); + +/** + * @ingroup base + * + * Add the default plugin lookup paths, typically: + * - /etc/libinput/plugins/ + * - /usr/lib{64}/libinput/plugins/ + * + * These paths are inserted at the current priority - to add + * paths with a higher priority than these, call + * libinput_plugin_system_append_path() prior to this function. + * + * See libinput_plugin_system_append_path() for more details. + * + * This function must be called before + * libinput_plugin_system_load_plugins(). + * + * @see libinput_plugin_system_append_paths + * + * @since 1.29 + */ +void +libinput_plugin_system_append_default_paths(struct libinput *libinput); + +enum libinput_plugins_flags { + LIBINPUT_PLUGIN_FLAG_NONE = 0, +}; + +/** + * @ingroup base + * + * Load the plugins from the set of lookup paths. This function does nothing + * if no plugin paths have been configured, see + * libinput_plugin_system_append_default_paths() and + * libinput_plugin_system_append_path(). + * + * The typical use of this function is: + * ``` + * struct libinput *li = libinput_udev_create_context(...); + * libinput_plugin_system_append_default_paths(li); + * libinput_plugin_system_load(li, flags); + * ``` + * + * This function must be called before libinput iterates through the + * devices, i.e. before libinput_udev_assign_seat() or libinput_path_add_device(). + * + * @return 0 or a negative errno on failure + * @retval -ENOSYS libinput was compiled without plugin support + * + * @since 1.29 + */ +int +libinput_plugin_system_load_plugins(struct libinput *libinput, + enum libinput_plugins_flags flags); + /** * @ingroup base * diff --git a/src/libinput.sym b/src/libinput.sym index 74ebad83..74955ecd 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -374,3 +374,9 @@ LIBINPUT_1.29 { libinput_tablet_tool_config_eraser_button_set_button; libinput_tablet_tool_config_eraser_button_set_mode; } LIBINPUT_1.28; + +LIBINPUT_1.30 { + libinput_plugin_system_append_default_paths; + libinput_plugin_system_append_path; + libinput_plugin_system_load_plugins; +} LIBINPUT_1.29; diff --git a/test/litest.c b/test/litest.c index d94a2eaf..fc92cdc3 100644 --- a/test/litest.c +++ b/test/litest.c @@ -2144,6 +2144,8 @@ litest_create_context(void) if (verbose) libinput_log_set_priority(libinput, LIBINPUT_LOG_PRIORITY_DEBUG); + libinput_plugin_system_load_plugins(libinput, LIBINPUT_PLUGIN_FLAG_NONE); + return libinput; } diff --git a/tools/libinput-debug-events.c b/tools/libinput-debug-events.c index 8a633657..7bb8166b 100644 --- a/tools/libinput-debug-events.c +++ b/tools/libinput-debug-events.c @@ -319,7 +319,13 @@ main(int argc, char **argv) if (verbose) printf("libinput version: %s\n", LIBINPUT_VERSION); - li = tools_open_backend(backend, seat_or_devices, verbose, &grab); + bool with_plugins = (options.plugins == 1); + li = tools_open_backend(backend, + seat_or_devices, + verbose, + &grab, + with_plugins, + steal(&options.plugin_paths)); if (!li) return EXIT_FAILURE; diff --git a/tools/libinput-debug-gui.c b/tools/libinput-debug-gui.c index 0c2fcfc5..36aa9dd6 100644 --- a/tools/libinput-debug-gui.c +++ b/tools/libinput-debug-gui.c @@ -2007,7 +2007,13 @@ main(int argc, char **argv) backend = BACKEND_UDEV; } - li = tools_open_backend(backend, seat_or_device, verbose, &w.grab); + bool with_plugins = (options.plugins == 1); + li = tools_open_backend(backend, + seat_or_device, + verbose, + &w.grab, + with_plugins, + steal(&options.plugin_paths)); if (!li) return EXIT_FAILURE; diff --git a/tools/libinput-debug-tablet-pad.c b/tools/libinput-debug-tablet-pad.c index a389b9e2..51b678f6 100644 --- a/tools/libinput-debug-tablet-pad.c +++ b/tools/libinput-debug-tablet-pad.c @@ -528,7 +528,14 @@ main(int argc, char **argv) return EXIT_FAILURE; } - li = tools_open_backend(backend, seat_or_device, false, &grab); + bool with_plugins = (options.plugins == 1); + li = tools_open_backend(backend, + seat_or_device, + false, + &grab, + with_plugins, + steal(&options.plugin_paths)); + if (!li) return EXIT_FAILURE; diff --git a/tools/libinput-debug-tablet.c b/tools/libinput-debug-tablet.c index 0d478751..08845ae1 100644 --- a/tools/libinput-debug-tablet.c +++ b/tools/libinput-debug-tablet.c @@ -602,7 +602,13 @@ main(int argc, char **argv) return EXIT_FAILURE; } - li = tools_open_backend(backend, seat_or_device, false, &grab); + bool with_plugins = (options.plugins == 1); + li = tools_open_backend(backend, + seat_or_device, + false, + &grab, + with_plugins, + steal(&options.plugin_paths)); if (!li) return EXIT_FAILURE; diff --git a/tools/libinput-list-devices.c b/tools/libinput-list-devices.c index 5eb0b4d0..5df5ea91 100644 --- a/tools/libinput-list-devices.c +++ b/tools/libinput-list-devices.c @@ -606,10 +606,15 @@ main(int argc, char **argv) } devices[ndevices++] = argv[optind]; } while (++optind < argc); - li = tools_open_backend(BACKEND_DEVICE, devices, false, &grab); + li = tools_open_backend(BACKEND_DEVICE, + devices, + false, + &grab, + false, + NULL); } else { const char *seat[2] = { "seat0", NULL }; - li = tools_open_backend(BACKEND_UDEV, seat, false, &grab); + li = tools_open_backend(BACKEND_UDEV, seat, false, &grab, false, NULL); } if (!li) return 1; diff --git a/tools/shared.c b/tools/shared.c index 9553aa62..8928ef95 100644 --- a/tools/shared.c +++ b/tools/shared.c @@ -107,6 +107,8 @@ void tools_init_options(struct tools_options *options) { memset(options, 0, sizeof(*options)); + options->plugins = -1; + options->plugin_paths = NULL; options->tapping = -1; options->tap_map = -1; options->drag = -1; @@ -146,6 +148,15 @@ int tools_parse_option(int option, const char *optarg, struct tools_options *options) { switch (option) { + case OPT_PLUGINS_ENABLE: + options->plugins = 1; + break; + case OPT_PLUGINS_DISABLE: + options->plugins = 0; + break; + case OPT_PLUGIN_PATH: + options->plugin_paths = strv_from_string(optarg, ":", NULL); + break; case OPT_TAP_ENABLE: options->tapping = 1; break; @@ -494,8 +505,46 @@ static const struct libinput_interface interface = { .close_restricted = close_restricted, }; +static int +add_path(const char *str, size_t index, void *data) +{ + struct libinput *libinput = data; + libinput_plugin_system_append_default_paths(libinput); + return 0; +} + +static void +tools_load_plugins(struct libinput *libinput, char **plugin_paths) +{ + _autofree_ char *builddir = NULL; + + if (plugin_paths) { + strv_for_each((const char **)plugin_paths, add_path, libinput); + strv_free(plugin_paths); + } else { + if (builddir_lookup(&builddir)) { + _autofree_ char *plugindir = + strdup_printf("%s/plugins", builddir); + libinput_plugin_system_append_path(libinput, plugindir); + } + libinput_plugin_system_append_default_paths(libinput); + } + switch (libinput_plugin_system_load_plugins(libinput, + LIBINPUT_PLUGIN_FLAG_NONE)) { + case -ENOSYS: + fprintf(stderr, "Warning: plugins were disabled at compile time"); + break; + case 0: + break; + } +} + static struct libinput * -tools_open_udev(const char *seat, bool verbose, bool *grab) +tools_open_udev(const char *seat, + bool verbose, + bool *grab, + bool with_plugins, + char **plugin_paths) { _unref_(udev) *udev = udev_new(); if (!udev) { @@ -513,6 +562,9 @@ tools_open_udev(const char *seat, bool verbose, bool *grab) if (verbose) libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG); + if (with_plugins) + tools_load_plugins(li, plugin_paths); + if (libinput_udev_assign_seat(li, seat)) { fprintf(stderr, "Failed to set seat\n"); return NULL; @@ -522,7 +574,11 @@ tools_open_udev(const char *seat, bool verbose, bool *grab) } static struct libinput * -tools_open_device(const char **paths, bool verbose, bool *grab) +tools_open_device(const char **paths, + bool verbose, + bool *grab, + bool with_plugins, + char **plugin_paths) { _unref_(libinput) *li = libinput_path_create_context(&interface, grab); if (!li) { @@ -535,6 +591,9 @@ tools_open_device(const char **paths, bool verbose, bool *grab) libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG); } + if (with_plugins) + tools_load_plugins(li, plugin_paths); + const char **p = paths; while (*p) { struct libinput_device *device = libinput_path_add_device(li, *p); @@ -559,7 +618,9 @@ struct libinput * tools_open_backend(enum tools_backend which, const char **seat_or_device, bool verbose, - bool *grab) + bool *grab, + bool with_plugins, + char **plugin_paths) { struct libinput *li; @@ -567,10 +628,18 @@ tools_open_backend(enum tools_backend which, switch (which) { case BACKEND_UDEV: - li = tools_open_udev(seat_or_device[0], verbose, grab); + li = tools_open_udev(seat_or_device[0], + verbose, + grab, + with_plugins, + plugin_paths); break; case BACKEND_DEVICE: - li = tools_open_device(seat_or_device, verbose, grab); + li = tools_open_device(seat_or_device, + verbose, + grab, + with_plugins, + plugin_paths); break; default: abort(); diff --git a/tools/shared.h b/tools/shared.h index eb4195ec..c9c3bd67 100644 --- a/tools/shared.h +++ b/tools/shared.h @@ -77,10 +77,15 @@ enum configuration_options { OPT_SENDEVENTS, OPT_ERASER_BUTTON_MODE, OPT_ERASER_BUTTON_BUTTON, + OPT_PLUGINS_DISABLE, + OPT_PLUGINS_ENABLE, + OPT_PLUGIN_PATH, }; #define CONFIGURATION_OPTIONS \ { "disable-sendevents", required_argument, 0, OPT_DISABLE_SENDEVENTS }, \ + { "enable-plugins", no_argument, 0, OPT_PLUGINS_ENABLE }, \ + { "disable-plugins", no_argument, 0, OPT_PLUGINS_DISABLE }, \ { "enable-tap", no_argument, 0, OPT_TAP_ENABLE }, \ { "disable-tap", no_argument, 0, OPT_TAP_DISABLE }, \ { "enable-drag", no_argument, 0, OPT_DRAG_ENABLE }, \ @@ -117,7 +122,8 @@ enum configuration_options { { "set-calibration", required_argument, 0, OPT_CALIBRATION }, \ { "set-area", required_argument, 0, OPT_AREA }, \ { "set-eraser-button-mode", required_argument, 0, OPT_ERASER_BUTTON_MODE }, \ - { "set-eraser-button-button", required_argument, 0, OPT_ERASER_BUTTON_BUTTON } + { "set-eraser-button-button", required_argument, 0, OPT_ERASER_BUTTON_BUTTON },\ + { "set-plugin-path", required_argument, 0, OPT_PLUGIN_PATH } /* Note: New arguments should be added to shell completions */ @@ -144,6 +150,9 @@ enum tools_backend { BACKEND_NONE, BACKEND_DEVICE, BACKEND_UDEV }; struct tools_options { char match[256]; + int plugins; + char **plugin_paths; + int tapping; int drag; int drag_lock; @@ -183,7 +192,9 @@ struct libinput * tools_open_backend(enum tools_backend which, const char **seat_or_devices, bool verbose, - bool *grab); + bool *grab, + bool with_plugins, + char **plugin_paths); void tools_device_apply_config(struct libinput_device *device, struct tools_options *options);