#ifndef _PLUGIN_H_ #define _PLUGIN_H_ #include #include #include #include #include #include #include #include #include "plugin_types.h" #include /* * Marker macro to define what functions should be exported. This generates the * interface which the plugin needs to implement. */ #define EXPORT(a) a #define EXPORT_INCLUDE(a) // clang-format off EXPORT_INCLUDE() EXPORT_INCLUDE() EXPORT_INCLUDE() EXPORT_INCLUDE() // clang-format on #define MAX_QUEUED_ACTIONS 8 typedef void *dlhandle_t; /* Opaque state for a plugin. Not to be touched by the harness (not that it * really can be.) */ struct PLUGIN; /* This structure represents an action requested by the plugin for the harness. */ typedef struct { int (*action)(struct PLUGIN *requester, void *arg); void (*arg_dtor)(void *arg); union { void *ptr_arg; int int_arg; char *str_arg; }; } requested_action_t; /* * Structure for the plugin. */ typedef struct PLUGIN { /* The argc this plugin is loaded with. Typically the argc from main(). */ int argc; /* The argv this plugin is loaded with. Typically the argv from main(). */ char **argv; /* Filename the plugin is loaded from. */ char filename[PATH_MAX]; /* Interface to the harness that this plugin can use. */ foreign_interface_t foreign_intf; /* Opaque state of this plugin. The state is usually some kind of pointer to * the plugin state, but all the harness knows is the opaque state is a * pointer-sized piece of data. * * This opaque state is used in a linear pattern where the handlers take the * opaque state, maybe operate on it, and return a new opaque state, which is * then passed to the next handler, etc. It is on the plugin to properly * manager the memory for this state and to destroy it upon teardown. * * It's guaranteed that this state is used linearly, meaning the harness gives * up all ownership to it once passed into a handler. */ opqst_t state; /* This plugin's lock. This avoids potential issues with multiple threads * trying to change the opaque state at once which can lead to undesireable * outcomes. */ pthread_mutex_t lock; /** Set to not-zero if this plugin is initialized, otherwise set to zero. */ int initialized; /* The handle to the shared library. */ dlhandle_t library_handle; /* Pointer to the plugin name. This is in the shared library and a * null-terminated string. If the library does not have a plugin name, this * will be NULL. */ const char *plugin_name; /** * Initializes the plugin on the first time, and only the first time, it is * loaded. This is used to do things like setup a runtime that cannot be * reliably torn down. It is up to the plugin to ensure this won't interfere * with hot-reloading. */ EXPORT(void (*plugin_metaload)(int argc, char **argv)); /** Intializes the plugin with the given argc/argv. This is the first thing * called on the plugin and is called immediately after the library is loaded. */ EXPORT(void (*plugin_load)(int argc, char **argv, foreign_interface_t *intf)); /* Start the plugin with the marshalled state from the previous plugin. * * This should return the opaque state from the mashalled_state. * * This function should not fail if the state cannot be demarshalled, rather a * default state should be returned. This is because changing the plugin and * hot-reloading can produce incompatibilities between the old state and the * new state, and this should not cause a failure. */ EXPORT(opqst_t (*plugin_hot_start)(uint8_t *mashalled_state, uint32_t n)); /* * Starts the plugin without a marshalled state. Happens during the first boot * when there is not state. */ EXPORT(opqst_t (*plugin_cold_start)()); /* * Marshals the state to a bytestring. The returned pointer should be malloc'd * on the heap. The harness takes ownership of the malloc'd pointer. * * This is usually called in preparation for a teardown followed by a * hot-start. */ EXPORT(uint8_t *(*plugin_marshal_state)(opqst_t st, uint32_t *szout)); /* * Teardown the plugin in preperation for the library's imminent unloading. */ EXPORT(void (*plugin_teardown)(opqst_t)); /* * Handles a keybinding. */ EXPORT(opqst_t (*plugin_handle_keybinding)( struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event, uint32_t modifiers, uint32_t keysym, uint32_t codepoint, int *out_handled, opqst_t state)); EXPORT(opqst_t (*plugin_handle_button)(struct wlr_pointer_button_event *event, uint32_t modifiers, opqst_t state)); /* * Handles a surface being mapped, unmapped or destroyed. */ EXPORT(opqst_t (*plugin_handle_surface)(void *surface, surface_event_t event, opqst_t)); /* List of requested actions by the plugin. Right now there is a maximum of 8 * allowed at one time. That should be plenty. The actions should be flushed * after each call to a handler anyway. */ size_t n_requested_actions; requested_action_t requested_actions[MAX_QUEUED_ACTIONS]; } plugin_t; #undef EXPORT #undef EXPORT_INCLUDE /* Reloads the plugin. This tears down the existing plugin, marshals the state * for it and reloads it. * * This function will call dlclose on the plugin's library handle. */ int plugin_hot_reload(int argc, char **argv, const char *filepath, plugin_t *plugin); /* * Like hot-reload, but uses the same parameters the plugin was originally * loaded with. */ int plugin_hot_reload_same_state(plugin_t *plugin); /* Starts a plugin in a cold state. Called after load_plugin_from_file. */ void plugin_cold_start(plugin_t *plugin); /* Reads a plugin from a filename. */ int load_plugin_from_file(int argc, char **argv, const char *filename, plugin_t *plugin); void plugin_run_requested_actions(plugin_t *plugin); #endif /* _PLUGIN_H_ */