aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2026-01-14 00:09:26 -0700
committerJosh Rahm <joshuarahm@gmail.com>2026-01-14 00:11:37 -0700
commitf216280bc0fdc4a6f00187dba1d1ddc22b54097d (patch)
tree64637e9df49011b3ec81738b4347ab7a97e88980
parentcc6302ee2073d1ea40608abb77ca6019feca4a2a (diff)
downloadmontis-f216280bc0fdc4a6f00187dba1d1ddc22b54097d.tar.gz
montis-f216280bc0fdc4a6f00187dba1d1ddc22b54097d.tar.bz2
montis-f216280bc0fdc4a6f00187dba1d1ddc22b54097d.zip
[reog] rename soul -> world.HEADmain
It makes a bit more sense.
-rw-r--r--CMakeLists.txt16
-rw-r--r--Makefile6
-rw-r--r--README.md22
-rw-r--r--ark/CMakeLists.txt1
-rw-r--r--ark/README.md24
-rw-r--r--ark/include/soul.h111
-rw-r--r--ark/include/soul_interface.h23
-rw-r--r--ark/include/wl.h150
-rw-r--r--ark/include/world.h112
-rw-r--r--ark/include/world_exports.h (renamed from ark/include/soul_exports.h)60
-rw-r--r--ark/include/world_interface.h23
-rw-r--r--ark/include/world_types.h (renamed from ark/include/soul_types.h)0
-rw-r--r--ark/src/soul.c198
-rw-r--r--ark/src/wl.c238
-rw-r--r--ark/src/world.c199
-rw-r--r--ark/src/world_load.c (renamed from ark/src/soul_load.c)22
-rw-r--r--cross/README.md8
-rw-r--r--cross/include/util.h20
-rw-r--r--cross/src/runtime_requests.c44
-rw-r--r--cross/src/util.c16
-rw-r--r--montis/README.md12
-rw-r--r--montis/src/Link.hs6
-rw-r--r--montis/src/Montis/Base/Foreign/Runtime.hs16
-rw-r--r--montis/src/Montis/Core/Internal/Foreign/Export.hs50
-rw-r--r--montis/src/Montis/Core/Monad.hs6
-rw-r--r--montis/src/Montis/Core/State.hs4
-rw-r--r--montis/src/Montis/Core/World/Interface.hs (renamed from montis/src/Montis/Core/Soul/Interface.hs)4
-rw-r--r--montis/src/harness_adapter.c16
28 files changed, 781 insertions, 626 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b4921ce..5046a00 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,18 +1,18 @@
cmake_minimum_required(VERSION 3.16)
project(montis LANGUAGES C)
-option(MONTIS_BUILD_BUNDLED_SOUL "Build the bundled Haskell soul (montis.so)" ON)
+option(MONTIS_BUILD_BUNDLED_WORLD "Build the bundled Haskell world (montis.so)" ON)
if(DEFINED MONTIS_BUILD_BUNDLED_PLUGIN)
- set(MONTIS_BUILD_BUNDLED_SOUL "${MONTIS_BUILD_BUNDLED_PLUGIN}")
+ set(MONTIS_BUILD_BUNDLED_WORLD "${MONTIS_BUILD_BUNDLED_PLUGIN}")
endif()
add_subdirectory(ark)
add_subdirectory(cross)
-if(MONTIS_BUILD_BUNDLED_SOUL)
+if(MONTIS_BUILD_BUNDLED_WORLD)
add_custom_target(
- soul_build ALL
+ world_build ALL
COMMAND sh -c "if [ -d \"$1\" ] && [ ! -L \"$1\" ]; then rm -rf \"$2\"; mv \"$1\" \"$2\"; fi" sh "${CMAKE_SOURCE_DIR}/montis/.stack-work" "${CMAKE_BINARY_DIR}/stack-work"
COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_BINARY_DIR}/stack-work"
COMMAND "${CMAKE_COMMAND}" -E create_symlink "${CMAKE_BINARY_DIR}/stack-work" "${CMAKE_SOURCE_DIR}/montis/.stack-work"
@@ -20,14 +20,14 @@ if(MONTIS_BUILD_BUNDLED_SOUL)
# Not sure why stack is generating an a.out file, but remove it.
COMMAND "${CMAKE_COMMAND}" -E rm -f "${CMAKE_SOURCE_DIR}/montis/a.out"
DEPENDS ark cross
- COMMENT "Building bundled Haskell soul with Stack"
+ COMMENT "Building bundled Haskell world with Stack"
VERBATIM
)
add_custom_target(
run
- COMMAND sh -c "SOUL_SO=$(find '${CMAKE_BINARY_DIR}/stack-work' -name montis.so -type f | head -n 1); if [ -z \"$SOUL_SO\" ]; then echo 'montis.so not found in ${CMAKE_BINARY_DIR}/stack-work' 1>&2; exit 1; fi; \"$<TARGET_FILE:ark>\" -s foot -p \"$SOUL_SO\""
- DEPENDS ark soul_build
+ COMMAND sh -c "WORLD_SO=$(find '${CMAKE_BINARY_DIR}/stack-work' -name montis.so -type f | head -n 1); if [ -z \"$WORLD_SO\" ]; then echo 'montis.so not found in ${CMAKE_BINARY_DIR}/stack-work' 1>&2; exit 1; fi; \"$<TARGET_FILE:ark>\" -s foot -p \"$WORLD_SO\""
+ DEPENDS ark world_build
USES_TERMINAL
VERBATIM
)
@@ -36,7 +36,7 @@ endif()
install(TARGETS ark RUNTIME DESTINATION bin)
-if(MONTIS_BUILD_BUNDLED_SOUL)
+if(MONTIS_BUILD_BUNDLED_WORLD)
install(CODE [[
execute_process(
COMMAND sh -c "find '${CMAKE_BINARY_DIR}/stack-work' -name montis.so -type f | head -n 1"
diff --git a/Makefile b/Makefile
index d814302..aa4f004 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: all configure build ark cross soul run install clean distclean
+.PHONY: all configure build ark cross world run install clean distclean
BUILD_DIR ?= build
BUILD_TYPE ?= Debug
@@ -18,8 +18,8 @@ ark: configure
cross: configure
cmake --build $(BUILD_DIR) --target cross
-soul: configure
- cmake --build $(BUILD_DIR) --target soul_build
+world: configure
+ cmake --build $(BUILD_DIR) --target world_build
run: configure
cmake --build $(BUILD_DIR) --target run
diff --git a/README.md b/README.md
index 1240f49..9ba174f 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ runtime and wlroots itself.
**Cross** (`libcross.a`) is a static C library that contains the native
functions Montis uses to interact with Ark/wlroots. It is linked into the
-`montis.so` soul so these FFI bindings hot-reload along with the Haskell code
+`montis.so` world so these FFI bindings hot-reload along with the Haskell code
instead of living inside the long-running compositor process.
Code lives in `cross/src` and headers in `cross/include` (notably
@@ -109,23 +109,23 @@ The solution is a pluggable architecture:
- `meson` (wlroots is built via Meson) and a backend like `ninja`
- `wayland-scanner` and `wayland-protocols`
- `curl` (first build downloads wlroots)
-- Optional (only for the bundled soul): Haskell toolchain via Stack: `stack` (+ GHC)
+- Optional (only for the bundled world): Haskell toolchain via Stack: `stack` (+ GHC)
### Build
This repo uses CMake to build:
- `ark` (the compositor runtime)
- `cross` (`libcross.a`)
-- the bundled Haskell soul (`montis.so`) via Stack
+- the bundled Haskell world (`montis.so`) via Stack
```sh
make build
```
-To build only the runtime + bridge library (no bundled soul):
+To build only the runtime + bridge library (no bundled world):
```sh
-make BUILD_DIR=build BUILD_TYPE=Debug CMAKE_ARGS=-DMONTIS_BUILD_BUNDLED_SOUL=OFF build
+make BUILD_DIR=build BUILD_TYPE=Debug CMAKE_ARGS=-DMONTIS_BUILD_BUNDLED_WORLD=OFF build
```
To run via the CMake helper target:
@@ -137,18 +137,18 @@ make run
Note: the `run` target currently starts `ark` with `-s foot`, so you’ll want
`foot` installed (or adjust the target in `CMakeLists.txt`).
-### Using Your Own Soul
+### Using Your Own World
-If you disable the bundled soul (`-DMONTIS_BUILD_BUNDLED_SOUL=OFF`), you can
-build and supply your own soul `.so` at runtime.
+If you disable the bundled world (`-DMONTIS_BUILD_BUNDLED_WORLD=OFF`), you can
+build and supply your own world `.so` at runtime.
-- Soul ABI: `ark/include/soul.h` (soul header: `ark/include/soul_interface.h`)
+- World ABI: `ark/include/world.h` (world header: `ark/include/world_interface.h`)
- Optional bridge helpers: `cross/include` + `build/cross/libcross.a`
Runtime invocation looks like:
```sh
-./build/ark/ark -p /path/to/your_soul.so
+./build/ark/ark -p /path/to/your_world.so
```
## Installing
@@ -159,4 +159,4 @@ make install PREFIX=~/.local
This installs:
- `ark` to `~/.local/bin`
-- if `-DMONTIS_BUILD_BUNDLED_SOUL=ON`, the latest built soul `.so` to `~/.local/lib`
+- if `-DMONTIS_BUILD_BUNDLED_WORLD=ON`, the latest built world `.so` to `~/.local/lib`
diff --git a/ark/CMakeLists.txt b/ark/CMakeLists.txt
index 35e47a9..4e2c259 100644
--- a/ark/CMakeLists.txt
+++ b/ark/CMakeLists.txt
@@ -127,6 +127,7 @@ pkg_check_modules(WLOPT IMPORTED_TARGET
if(WLOPT_FOUND)
target_link_libraries(ark PRIVATE PkgConfig::WLOPT)
+ target_compile_definitions(ark PRIVATE ARK_HAVE_CAIRO)
endif()
target_link_directories(ark PUBLIC
diff --git a/ark/README.md b/ark/README.md
index d773e0d..a8be7e1 100644
--- a/ark/README.md
+++ b/ark/README.md
@@ -1,35 +1,35 @@
Ark (Runtime)
=============
-Ark is a long-running Wayland compositor runtime with a hot-reloadable soul
+Ark is a long-running Wayland compositor runtime with a hot-reloadable world
interface.
Responsibilities
----------------
- Owns the Wayland display lifecycle and wlroots setup.
-- Loads a soul shared object (`.so`) at runtime and routes input/surface events to it.
-- Supports hot-reloading the soul without restarting the compositor.
+- Loads a world shared object (`.so`) at runtime and routes input/surface events to it.
+- Supports hot-reloading the world without restarting the compositor.
-Soul interface
+World interface
--------------
-The runtime defines a C ABI that souls must implement (load/start/teardown,
+The runtime defines a C ABI that worlds must implement (load/start/teardown,
event handlers, and optional state marshal/unmarshal for hot reload). The ABI is
defined in:
-- `ark/include/soul.h`
+- `ark/include/world.h`
-Souls should include:
+Worlds should include:
-- `ark/include/soul_interface.h`
+- `ark/include/world_interface.h`
Key files
---------
-- `ark/src/wl.c`: compositor setup + event loop + soul callbacks.
-- `ark/src/soul.c`: dynamic loading, lifecycle, and hot-reload logic.
-- `ark/include/soul.h`: C ABI the soul must implement.
+- `ark/src/wl.c`: compositor setup + event loop + world callbacks.
+- `ark/src/world.c`: dynamic loading, lifecycle, and hot-reload logic.
+- `ark/include/world.h`: C ABI the world must implement.
Building
--------
@@ -44,7 +44,7 @@ cmake --build ../build --target ark
Running
-------
-Use the top-level `run` target (builds the bundled soul and launches Ark):
+Use the top-level `run` target (builds the bundled world and launches Ark):
```sh
cmake --build ../build --target run
diff --git a/ark/include/soul.h b/ark/include/soul.h
deleted file mode 100644
index fa3415b..0000000
--- a/ark/include/soul.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#ifndef _SOUL_H_
-#define _SOUL_H_
-
-#include <dlfcn.h>
-#include <linux/limits.h>
-#include <pthread.h>
-#include <stdint.h>
-#include <wlr/types/wlr_input_device.h>
-#include <wlr/types/wlr_keyboard.h>
-#include <wlr/types/wlr_pointer.h>
-
-#include "soul_types.h"
-#include "soul_exports.h"
-
-#define MAX_QUEUED_ACTIONS 8
-
-typedef void *dlhandle_t;
-
-/* Opaque state for a soul. Not to be touched by the harness (not that it
- * really can be.) */
-
-struct SOUL;
-/* This structure represents an action requested by the soul for the harness.
- */
-typedef struct {
- int (*action)(struct SOUL *requester, void *arg);
- void (*arg_dtor)(void *arg);
- union {
- void *ptr_arg;
- int int_arg;
- char *str_arg;
- };
-} requested_action_t;
-
-/*
- * Structure for the soul.
- */
-typedef struct SOUL {
- /* The argc this soul is loaded with. Typically the argc from main(). */
- int argc;
-
- /* The argv this soul is loaded with. Typically the argv from main(). */
- char **argv;
-
- /* Filename the soul is loaded from. */
- char filename[PATH_MAX];
-
- /* Opaque state of this soul. The state is usually some kind of pointer to
- * the soul 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 soul 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 soul'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 soul is initialized, otherwise set to zero. */
- int initialized;
-
- /* The handle to the shared library. */
- dlhandle_t library_handle;
-
- /* Pointer to the soul name. This is in the shared library and a
- * null-terminated string. If the library does not have a soul name, this
- * will be NULL. */
- const char *soul_name;
-
- /* Soul function table populated by the runtime loader. */
-#define SOUL_FN_PTR(ret, name, args) ret (*name) args;
- ARKSOUL_EXPORTS(SOUL_FN_PTR)
-#undef SOUL_FN_PTR
-
- /* List of requested actions by the soul. 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];
-} soul_t;
-
-/* Reloads the soul. This tears down the existing soul, marshals the state
- * for it and reloads it.
- *
- * This function will call dlclose on the soul's library handle.
- */
-int soul_hot_reload(int argc, char **argv, const char *filepath, soul_t *soul);
-
-/*
- * Like hot-reload, but uses the same parameters the soul was originally
- * loaded with.
- */
-int soul_hot_reload_same_state(soul_t *soul);
-
-/* Starts a soul in a cold state. Called after load_soul_from_file. */
-void soul_cold_start(soul_t *soul);
-
-/* Reads a soul from a filename. */
-int load_soul_from_file(int argc, char **argv, const char *filename,
- soul_t *soul);
-
-void soul_run_requested_actions(soul_t *soul);
-
-#endif /* _SOUL_H_ */
diff --git a/ark/include/soul_interface.h b/ark/include/soul_interface.h
deleted file mode 100644
index 6e45573..0000000
--- a/ark/include/soul_interface.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef _SOUL_INTF
-#define _SOUL_INTF
-
-#include <stdint.h>
-
-#include "soul_types.h"
-#include "soul_exports.h"
-
-#include <wlr/types/wlr_input_device.h>
-#include <wlr/types/wlr_keyboard.h>
-#include <wlr/types/wlr_pointer.h>
-
-/*
- * Soul ABI: souls must export these symbols.
- *
- * This header is intended to be included by soul implementations.
- */
-
-#define DECLARE_SOUL_EXPORT(ret, name, args) ret name args;
-ARKSOUL_EXPORTS(DECLARE_SOUL_EXPORT)
-#undef DECLARE_SOUL_EXPORT
-
-#endif /* _SOUL_INTF */
diff --git a/ark/include/wl.h b/ark/include/wl.h
index e5cf4f3..725025c 100644
--- a/ark/include/wl.h
+++ b/ark/include/wl.h
@@ -21,105 +21,109 @@
#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/types/wlr_xdg_decoration_v1.h>
+#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
-#include <soul.h>
+#include <world.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,
+ 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_xdg_decoration_manager_v1 *xdg_decoration_manager;
- struct wl_listener new_xdg_decoration;
-
- 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 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_xdg_decoration_manager_v1 *xdg_decoration_manager;
+ struct wl_listener new_xdg_decoration;
+
+ 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_buffer *background_buffer;
+ char *background_path;
struct wlr_session *session;
- soul_t soul;
+ world_t world;
};
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 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 wlr_scene_buffer *background;
};
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 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_list link;
+ struct montis_server *server;
+ struct wlr_keyboard *wlr_keyboard;
- struct wl_listener modifiers;
- struct wl_listener key;
- struct wl_listener destroy;
+ 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;
+ struct wlr_xdg_popup *xdg_popup;
+ struct wl_listener commit;
+ struct wl_listener destroy;
};
struct montis_xdg_decoration {
- struct wlr_xdg_toplevel_decoration_v1 *decoration;
- struct wl_listener request_mode;
- struct wl_listener destroy;
+ struct wlr_xdg_toplevel_decoration_v1 *decoration;
+ struct wl_listener request_mode;
+ struct wl_listener destroy;
};
diff --git a/ark/include/world.h b/ark/include/world.h
new file mode 100644
index 0000000..307608e
--- /dev/null
+++ b/ark/include/world.h
@@ -0,0 +1,112 @@
+#ifndef _WORLD_H_
+#define _WORLD_H_
+
+#include <dlfcn.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_pointer.h>
+
+#include "world_exports.h"
+#include "world_types.h"
+
+#define MAX_QUEUED_ACTIONS 8
+
+typedef void *dlhandle_t;
+
+/* Opaque state for a world. Not to be touched by the harness (not that it
+ * really can be.) */
+
+struct WORLD;
+/* This structure represents an action requested by the world for the harness.
+ */
+typedef struct {
+ int (*action)(struct WORLD *requester, void *arg);
+ void (*arg_dtor)(void *arg);
+ union {
+ void *ptr_arg;
+ int int_arg;
+ char *str_arg;
+ };
+} requested_action_t;
+
+/*
+ * Structure for the world.
+ */
+typedef struct WORLD {
+ /* The argc this world is loaded with. Typically the argc from main(). */
+ int argc;
+
+ /* The argv this world is loaded with. Typically the argv from main(). */
+ char **argv;
+
+ /* Filename the world is loaded from. */
+ char filename[PATH_MAX];
+
+ /* Opaque state of this world. The state is usually some kind of pointer to
+ * the world 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 world 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 world'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 world is initialized, otherwise set to zero. */
+ int initialized;
+
+ /* The handle to the shared library. */
+ dlhandle_t library_handle;
+
+ /* Pointer to the world name. This is in the shared library and a
+ * null-terminated string. If the library does not have a world name, this
+ * will be NULL. */
+ const char *world_name;
+
+ /* World function table populated by the runtime loader. */
+#define WORLD_FN_PTR(ret, name, args) ret(*name) args;
+ ARKWORLD_EXPORTS(WORLD_FN_PTR)
+#undef WORLD_FN_PTR
+
+ /* List of requested actions by the world. 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];
+} world_t;
+
+/* Reloads the world. This tears down the existing world, marshals the state
+ * for it and reloads it.
+ *
+ * This function will call dlclose on the world's library handle.
+ */
+int world_hot_reload(int argc, char **argv, const char *filepath,
+ world_t *world);
+
+/*
+ * Like hot-reload, but uses the same parameters the world was originally
+ * loaded with.
+ */
+int world_hot_reload_same_state(world_t *world);
+
+/* Starts a world in a cold state. Called after load_world_from_file. */
+void world_cold_start(world_t *world);
+
+/* Reads a world from a filename. */
+int load_world_from_file(int argc, char **argv, const char *filename,
+ world_t *world);
+
+void world_run_requested_actions(world_t *world);
+
+#endif /* _WORLD_H_ */
diff --git a/ark/include/soul_exports.h b/ark/include/world_exports.h
index 8e22b15..c0ee74e 100644
--- a/ark/include/soul_exports.h
+++ b/ark/include/world_exports.h
@@ -1,42 +1,42 @@
#pragma once
/*
- * Single source of truth for the soul ABI.
+ * Single source of truth for the world ABI.
*
* Consumers must define an X-macro like:
* #define X(ret, name, args) ...
* and then invoke:
- * ARKSOUL_EXPORTS(X)
+ * ARKWORLD_EXPORTS(X)
*
* Note: this file intentionally does not include headers. Include whatever you
- * need (e.g. <stdint.h>, soul_types.h, wlroots headers) before expanding.
+ * need (e.g. <stdint.h>, world_types.h, wlroots headers) before expanding.
*/
-#define ARKSOUL_EXPORTS(X) \
+#define ARKWORLD_EXPORTS(X) \
/* \
- * `arksoul_export_metaload` \
+ * `arkworld_export_metaload` \
* \
- * Called at most once per process *per `soul_name`*. This is the place \
+ * Called at most once per process *per `world_name`*. This is the place \
* for truly global initialization that should survive hot reloads (e.g. \
* one-time language runtime init, registering global hooks, etc.). \
* \
* Notes: \
* - May be a no-op. \
- * - Must be safe to call before any other soul entrypoint. \
+ * - Must be safe to call before any other world entrypoint. \
*/ \
- X(void, arksoul_export_metaload, (int argc, char **argv)) \
+ X(void, arkworld_export_metaload, (int argc, char **argv)) \
/* \
- * `arksoul_export_load` \
+ * `arkworld_export_load` \
* \
* Called every time the shared object is loaded (cold start and each hot \
* reload). Use this for per-load initialization that must be re-done after \
* `dlopen`. \
*/ \
- X(void, arksoul_export_load, (int argc, char **argv)) \
+ X(void, arkworld_export_load, (int argc, char **argv)) \
/* \
- * `arksoul_export_rebirth` \
+ * `arkworld_export_rebirth` \
* \
- * Called after a hot reload with the previous soul's preserved state. \
+ * Called after a hot reload with the previous world's preserved state. \
* Must return the new live opaque state (`opqst_t`) to be used for future \
* handler calls. \
* \
@@ -45,56 +45,56 @@
* - `self` is an opaque pointer owned by the runtime; treat it as \
* read-only. \
*/ \
- X(opqst_t, arksoul_export_rebirth, \
+ X(opqst_t, arkworld_export_rebirth, \
(void *self, uint8_t *marshalled_state, uint32_t n)) \
/* \
- * `arksoul_export_ensoul` \
+ * `arkworld_export_enworld` \
* \
* Called on first boot when no previous state exists. Must construct and \
* return an initial opaque state. \
*/ \
- X(opqst_t, arksoul_export_ensoul, (void *self)) \
+ X(opqst_t, arkworld_export_enworld, (void *self)) \
/* \
- * `arksoul_export_preserve` \
+ * `arkworld_export_preserve` \
* \
- * Called before unloading the current soul during hot reload. Must \
+ * Called before unloading the current world during hot reload. Must \
* serialize the provided opaque state into a newly allocated byte buffer. \
* \
* Ownership: \
* - Return a heap-allocated buffer (e.g. `malloc`). \
* - The runtime takes ownership and will `free()` it after \
- * `arksoul_export_rebirth`. \
+ * `arkworld_export_rebirth`. \
*/ \
- X(uint8_t *, arksoul_export_preserve, (opqst_t st, uint32_t *szout)) \
+ X(uint8_t *, arkworld_export_preserve, (opqst_t st, uint32_t * szout)) \
/* \
- * `arksoul_export_release` \
+ * `arkworld_export_release` \
* \
* Called immediately before the shared object is unloaded. Use this to \
* release resources owned by the opaque state (and any per-load resources \
- * created in `arksoul_export_load`). \
+ * created in `arkworld_export_load`). \
*/ \
- X(void, arksoul_export_release, (opqst_t st)) \
+ X(void, arkworld_export_release, (opqst_t st)) \
/* \
- * `arksoul_export_handle_keybinding` \
+ * `arkworld_export_handle_keybinding` \
* \
* Called for keyboard events. Returns the updated opaque state. Set \
* `*out_handled` to non-zero if the event is consumed. \
*/ \
- X(opqst_t, arksoul_export_handle_keybinding, \
+ X(opqst_t, arkworld_export_handle_keybinding, \