aboutsummaryrefslogtreecommitdiff
path: root/rt/include
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2026-01-01 18:04:40 -0700
committerJosh Rahm <joshuarahm@gmail.com>2026-01-01 18:04:40 -0700
commit628174c992a5a740feb4dc119adf8dfb1f89f992 (patch)
tree683361b27cf4b6df2c5cc782d70de9bdf5fd38a8 /rt/include
parentbe1ef8cee5f68eb9afecca94071069a1ff82825e (diff)
downloadmontis-628174c992a5a740feb4dc119adf8dfb1f89f992.tar.gz
montis-628174c992a5a740feb4dc119adf8dfb1f89f992.tar.bz2
montis-628174c992a5a740feb4dc119adf8dfb1f89f992.zip
Have Meson orchestrate the whole build rather than stack.
As a part of this, I changed the file layout to: rt/ - the Montis runtime plug/ - the Montis plugin wlroots/ - wlroots
Diffstat (limited to 'rt/include')
-rw-r--r--rt/include/foreign_intf.h33
-rw-r--r--rt/include/plugin.h190
-rw-r--r--rt/include/plugin_types.h9
-rw-r--r--rt/include/wl.h116
4 files changed, 348 insertions, 0 deletions
diff --git a/rt/include/foreign_intf.h b/rt/include/foreign_intf.h
new file mode 100644
index 0000000..6558fab
--- /dev/null
+++ b/rt/include/foreign_intf.h
@@ -0,0 +1,33 @@
+/* Contains a structure, which contains functions to back-call into
+ * the harness code. */
+
+#ifndef __FOREIGN_INTERFACE
+#define __FOREIGN_INTERFACE
+
+#define EXPORT(a) a
+
+typedef void *ctx_t;
+
+typedef struct FOREIGN_INTERFACE {
+ /* DO NOT ADD ANY UNEXPORTED VARIABLES HERE */
+
+ /* The context, which needs to be passed to each function. This context is
+ * opaque to the plugin and should not be changed. */
+ EXPORT(ctx_t ctx);
+
+ /* Requests the harness hot reload the current plugin. */
+ EXPORT(void (*request_hot_reload)(ctx_t ctx));
+
+ /* Requests the harness hot reload the current plugin. */
+ EXPORT(void (*do_log)(ctx_t ctx, const char *str));
+
+ /* Requestes that the whole system exit. Exits with the given return code. */
+ EXPORT(void (*request_exit)(ctx_t ctx, int rc));
+
+ /* Returns the seat associated with the server. */
+ EXPORT(void *(*get_seat)(ctx_t ctx));
+} foreign_interface_t;
+
+#undef EXPORT
+
+#endif /* __FOREIGN_INTERFACE */
diff --git a/rt/include/plugin.h b/rt/include/plugin.h
new file mode 100644
index 0000000..4d69d76
--- /dev/null
+++ b/rt/include/plugin.h
@@ -0,0 +1,190 @@
+#ifndef _PLUGIN_H_
+#define _PLUGIN_H_
+
+#include <dlfcn.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_pointer.h>
+
+#include "plugin_types.h"
+#include <foreign_intf.h>
+
+/*
+ * 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(<foreign_intf.h>)
+EXPORT_INCLUDE(<wlr/types/wlr_keyboard.h>)
+EXPORT_INCLUDE(<wlr/types/wlr_input_device.h>)
+EXPORT_INCLUDE(<wlr/types/wlr_pointer.h>)
+// 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_ */
diff --git a/rt/include/plugin_types.h b/rt/include/plugin_types.h
new file mode 100644
index 0000000..df1eab5
--- /dev/null
+++ b/rt/include/plugin_types.h
@@ -0,0 +1,9 @@
+#pragma once
+
+typedef void *opqst_t;
+
+typedef enum {
+ SURFACE_MAP = 0,
+ SURFACE_UNMAP,
+ SURFACE_DELETE,
+} surface_event_t;
diff --git a/rt/include/wl.h b/rt/include/wl.h
new file mode 100644
index 0000000..dc7fe9f
--- /dev/null
+++ b/rt/include/wl.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#include <assert.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <wayland-server-core.h>
+#include <wlr/backend.h>
+#include <wlr/backend/session.h>
+#include <wlr/render/allocator.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_data_device.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_pointer.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_seat.h>
+#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_shell.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+
+#include <plugin.h>
+
+/* For brevity's sake, struct members are annotated where they are used. */
+enum montis_cursor_mode {
+ TINYWL_CURSOR_PASSTHROUGH,
+ TINYWL_CURSOR_MOVE,
+ TINYWL_CURSOR_RESIZE,
+};
+
+struct montis_server {
+ struct wl_display *wl_display;
+ struct wlr_backend *backend;
+ struct wlr_renderer *renderer;
+ struct wlr_allocator *allocator;
+ struct wlr_scene *scene;
+ struct wlr_scene_output_layout *scene_layout;
+
+ struct wlr_xdg_shell *xdg_shell;
+ struct wl_listener new_xdg_toplevel;
+ struct wl_listener new_xdg_popup;
+ struct wl_list toplevels;
+
+ struct wlr_cursor *cursor;
+ struct wlr_xcursor_manager *cursor_mgr;
+ struct wl_listener cursor_motion;
+ struct wl_listener cursor_motion_absolute;
+ struct wl_listener cursor_button;
+ struct wl_listener cursor_axis;
+ struct wl_listener cursor_frame;
+
+ struct wlr_seat *seat;
+ struct wl_listener new_input;
+ struct wl_listener request_cursor;
+ struct wl_listener request_set_selection;
+ struct wl_list keyboards;
+ enum montis_cursor_mode cursor_mode;
+ struct montis_toplevel *grabbed_toplevel;
+ double grab_x, grab_y;
+ struct wlr_box grab_geobox;
+ uint32_t resize_edges;
+
+ struct wlr_output_layout *output_layout;
+ struct wl_list outputs;
+ struct wl_listener new_output;
+
+ struct wlr_session *session;
+ plugin_t plugin;
+};
+
+struct montis_output {
+ struct wl_list link;
+ struct montis_server *server;
+ struct wlr_output *wlr_output;
+ struct wl_listener frame;
+ struct wl_listener request_state;
+ struct wl_listener destroy;
+};
+
+struct montis_toplevel {
+ struct wl_list link;
+ struct montis_server *server;
+ struct wlr_xdg_toplevel *xdg_toplevel;
+ struct wlr_scene_tree *scene_tree;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+ struct wl_listener commit;
+ struct wl_listener request_move;
+ struct wl_listener request_resize;
+ struct wl_listener request_maximize;
+ struct wl_listener request_fullscreen;
+};
+
+struct montis_keyboard {
+ struct wl_list link;
+ struct montis_server *server;
+ struct wlr_keyboard *wlr_keyboard;
+
+ struct wl_listener modifiers;
+ struct wl_listener key;
+ struct wl_listener destroy;
+};
+
+struct montis_popup {
+ struct wlr_xdg_popup *xdg_popup;
+ struct wl_listener commit;
+ struct wl_listener destroy;
+};
+