diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2026-01-14 00:09:26 -0700 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2026-01-14 00:11:37 -0700 |
| commit | f216280bc0fdc4a6f00187dba1d1ddc22b54097d (patch) | |
| tree | 64637e9df49011b3ec81738b4347ab7a97e88980 | |
| parent | cc6302ee2073d1ea40608abb77ca6019feca4a2a (diff) | |
| download | montis-f216280bc0fdc4a6f00187dba1d1ddc22b54097d.tar.gz montis-f216280bc0fdc4a6f00187dba1d1ddc22b54097d.tar.bz2 montis-f216280bc0fdc4a6f00187dba1d1ddc22b54097d.zip | |
It makes a bit more sense.
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" @@ -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 @@ -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, \ (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)) \ /* \ - * `arksoul_export_handle_button` \ + * `arkworld_export_handle_button` \ * \ * Called for pointer button events (mouse/trackpad clicks). Returns the \ * updated opaque state. \ */ \ - X(opqst_t, arksoul_export_handle_button, \ + X(opqst_t, arkworld_export_handle_button, \ (struct wlr_pointer_button_event * event, uint32_t modifiers, \ opqst_t state)) \ /* \ - * `arksoul_export_handle_motion` \ + * `arkworld_export_handle_motion` \ * \ * Called for pointer motion. Returns the updated opaque state. \ * \ @@ -103,15 +103,15 @@ * (type depends on `is_absolute`). \ * - `lx`/`ly` are layout coordinates in compositor space. \ */ \ - X(opqst_t, arksoul_export_handle_motion, \ + X(opqst_t, arkworld_export_handle_motion, \ (void *event, uint32_t modifiers, uint32_t is_absolute, double lx, \ double ly, opqst_t state)) \ /* \ - * `arksoul_export_handle_surface` \ + * `arkworld_export_handle_surface` \ * \ * Called when a surface is mapped/unmapped/destroyed. `surface` is an \ * opaque pointer to the runtime surface representation. Returns the updated \ * opaque state. \ */ \ - X(opqst_t, arksoul_export_handle_surface, \ + X(opqst_t, arkworld_export_handle_surface, \ (void *surface, surface_event_t event, opqst_t state)) diff --git a/ark/include/world_interface.h b/ark/include/world_interface.h new file mode 100644 index 0000000..9e703fa --- /dev/null +++ b/ark/include/world_interface.h @@ -0,0 +1,23 @@ +#ifndef _WORLD_INTF +#define _WORLD_INTF + +#include <stdint.h> + +#include "world_exports.h" +#include "world_types.h" + +#include <wlr/types/wlr_input_device.h> +#include <wlr/types/wlr_keyboard.h> +#include <wlr/types/wlr_pointer.h> + +/* + * World ABI: worlds must export these symbols. + * + * This header is intended to be included by world implementations. + */ + +#define DECLARE_WORLD_EXPORT(ret, name, args) ret name args; +ARKWORLD_EXPORTS(DECLARE_WORLD_EXPORT) +#undef DECLARE_WORLD_EXPORT + +#endif /* _WORLD_INTF */ diff --git a/ark/include/soul_types.h b/ark/include/world_types.h index df1eab5..df1eab5 100644 --- a/ark/include/soul_types.h +++ b/ark/include/world_types.h diff --git a/ark/src/soul.c b/ark/src/soul.c deleted file mode 100644 index 4268cd5..0000000 --- a/ark/src/soul.c +++ /dev/null @@ -1,198 +0,0 @@ -#include "soul.h" - -#include <ctype.h> -#include <dlfcn.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -/* Utility function for showing the marshalled states as hex code */ -static void shx(uint8_t *state, uint32_t sz) -{ - uint32_t i = 0; - while (i < sz) { - for (int j = 0; j < 16; ++j) { - if (i < sz) { - printf("%02x ", (unsigned int)state[i]); - } - else { - printf(" "); - } - ++i; - } - - i -= 16; - - printf(" "); - - for (int j = 0; j < 16; ++j) { - if (i < sz) { - if (isprint(state[i]) && !isspace(state[i])) { - printf("%c", state[i]); - } - else { - printf("."); - } - } - else { - printf(" "); - } - ++i; - } - printf("\n"); - } -} - -int load_soul_from_dl_(dlhandle_t dl, soul_t *soul); - -static void lock(soul_t *soul) { pthread_mutex_lock(&soul->lock); }; - -static void unlock(soul_t *soul) { pthread_mutex_unlock(&soul->lock); }; - -static int load_soul_from_file_(int argc, char **argv, const char *filename, - soul_t *soul) -{ - dlhandle_t lib = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); - int ec = 0; - - if (!lib) { - fprintf(stderr, "Failed to open library: %s: %s\n", filename, dlerror()); - ec = 1; - goto end; - } - - printf("Loading file.\n"); - ec = load_soul_from_dl_(lib, soul); - - if (ec) { - goto end; - } - - strncpy(soul->filename, filename, sizeof(soul->filename)); - soul->argc = argc; - soul->argv = argv; - - soul->arksoul_export_load(soul->argc, soul->argv); -end: - return ec; -} - -static void maybe_run_metaload(int argc, char **argv, soul_t *soul) -{ - static char *loaded_souls[12]; - int i; - for (i = 0; i < 12 && loaded_souls[i]; ++i) { - if (strcmp(loaded_souls[i], soul->soul_name) == 0) { - return; // Soul is already loaded - } - } - loaded_souls[i] = strdup(soul->soul_name); - - printf("First time loading %s, running metaload.\n", soul->soul_name); - if (soul->arksoul_export_metaload) { - soul->arksoul_export_metaload(argc, argv); - } -} - -int load_soul_from_file(int argc, char **argv, const char *filename, - soul_t *soul) -{ - memset(soul, 0, sizeof(*soul)); - - pthread_mutexattr_t attr; - if (pthread_mutexattr_init(&attr)) { - perror("pthread_mutexattr_init"); - return 1; - } - - if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) { - perror("pthread_mutexattr_settype"); - return 1; - } - - if (pthread_mutex_init(&soul->lock, &attr)) { - pthread_mutexattr_destroy(&attr); - perror("pthread_mutexattr_init"); - return 1; - } - pthread_mutexattr_destroy(&attr); - int rc = load_soul_from_file_(argc, argv, filename, soul); - - if (rc == 0) { - maybe_run_metaload(argc, argv, soul); - } - - return rc; -} - -int soul_hot_reload_same_state(soul_t *soul) -{ - char filename_cpy[PATH_MAX]; - strncpy(filename_cpy, soul->filename, sizeof(filename_cpy)); - return soul_hot_reload(soul->argc, soul->argv, filename_cpy, soul); -} - -int soul_hot_reload(int argc, char **argv, const char *filepath, soul_t *soul) -{ - int ec = 0; - uint32_t sz = 0; - uint8_t *marshalled_state = NULL; - - printf("Hot Reloading %s\n", soul->soul_name); - lock(soul); - - printf("Marshalling state ...\n"); - marshalled_state = soul->arksoul_export_preserve(soul->state, &sz); - - printf("Calling teardown ...\n"); - soul->arksoul_export_release(soul->state); - - printf("State Marshalled:\n"); - shx(marshalled_state, sz); - - printf("Unloading old library handle.\n"); - if (dlclose(soul->library_handle)) { - printf("Could not close library handle: %s\n", dlerror()); - } - - if ((ec = load_soul_from_file_(argc, argv, filepath, soul))) { - goto fail; - } - - printf("Hot starting soul ...\n"); - soul->state = soul->arksoul_export_rebirth(soul, marshalled_state, sz); - -fail: - free(marshalled_state); - unlock(soul); - return ec; -} - -void soul_run_requested_actions(soul_t *soul) -{ - lock(soul); - requested_action_t requested_actions[MAX_QUEUED_ACTIONS]; - size_t n_requested_actions = soul->n_requested_actions; - memcpy(&requested_actions, soul->requested_actions, - sizeof(requested_actions)); - soul->n_requested_actions = 0; - unlock(soul); - - size_t i; - for (i = 0; i < n_requested_actions; ++i) { - requested_actions[i].action(soul, requested_actions[i].str_arg); - if (requested_actions[i].arg_dtor) { - requested_actions[i].arg_dtor(requested_actions[i].ptr_arg); - } - } -} - -void soul_cold_start(soul_t *soul) -{ - lock(soul); - soul->state = soul->arksoul_export_ensoul(soul); - unlock(soul); -} diff --git a/ark/src/wl.c b/ark/src/wl.c index b9ce952..fc0ddc7 100644 --- a/ark/src/wl.c +++ b/ark/src/wl.c @@ -9,8 +9,15 @@ #include "xdg-decoration-unstable-v1-client-protocol.h" -// This macro is responsible for calling a handler on a soul. This macro will -// acquire the soul's lock, call the member with the arguments and update the +#include <wlr/interfaces/wlr_buffer.h> + +#ifdef ARK_HAVE_CAIRO +#include <cairo.h> +#include <drm_fourcc.h> +#endif + +// This macro is responsible for calling a handler on a world. This macro will +// acquire the world's lock, call the member with the arguments and update the // state. // // This only works on function which have the format: @@ -18,15 +25,133 @@ // opqst_t function(args ..., opqst_t state); // // Note that the state parameter is omitted from this macro. -#define soul_call_update_state(soul, member, ...) \ +#define world_call_update_state(world, member, ...) \ do { \ - soul_t *sl__ = &(soul); \ + world_t *sl__ = &(world); \ pthread_mutex_lock(&sl__->lock); \ sl__->state = sl__->member(__VA_ARGS__, sl__->state); \ pthread_mutex_unlock(&sl__->lock); \ - soul_run_requested_actions(sl__); \ + world_run_requested_actions(sl__); \ } while (0) +#ifdef ARK_HAVE_CAIRO +struct montis_image_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; + uint32_t drm_format; +}; + +static void image_buffer_destroy(struct wlr_buffer *wlr_buffer) +{ + struct montis_image_buffer *buffer = + wl_container_of(wlr_buffer, buffer, base); + cairo_surface_destroy(buffer->surface); + free(buffer); +} + +static bool image_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, void **data, + uint32_t *format, size_t *stride) +{ + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { + return false; + } + struct montis_image_buffer *buffer = + wl_container_of(wlr_buffer, buffer, base); + cairo_surface_flush(buffer->surface); + *data = cairo_image_surface_get_data(buffer->surface); + *stride = cairo_image_surface_get_stride(buffer->surface); + *format = buffer->drm_format; + return true; +} + +static void image_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {} + +static const struct wlr_buffer_impl image_buffer_impl = { + .destroy = image_buffer_destroy, + .begin_data_ptr_access = image_buffer_begin_data_ptr_access, + .end_data_ptr_access = image_buffer_end_data_ptr_access, +}; + +static struct wlr_buffer *load_background_buffer(const char *path) +{ + cairo_surface_t *surface = cairo_image_surface_create_from_png(path); + if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(surface); + wlr_log(WLR_ERROR, "failed to load background image: %s", path); + return NULL; + } + + cairo_format_t format = cairo_image_surface_get_format(surface); + uint32_t drm_format = DRM_FORMAT_ARGB8888; + if (format == CAIRO_FORMAT_RGB24) { + drm_format = DRM_FORMAT_XRGB8888; + } + else if (format != CAIRO_FORMAT_ARGB32) { + cairo_surface_destroy(surface); + wlr_log(WLR_ERROR, "unsupported background image format: %d", format); + return NULL; + } + + int width = cairo_image_surface_get_width(surface); + int height = cairo_image_surface_get_height(surface); + + struct montis_image_buffer *buffer = calloc(1, sizeof(*buffer)); + if (!buffer) { + cairo_surface_destroy(surface); + return NULL; + } + + wlr_buffer_init(&buffer->base, &image_buffer_impl, width, height); + buffer->surface = surface; + buffer->drm_format = drm_format; + return &buffer->base; +} +#else +static struct wlr_buffer *load_background_buffer(const char *path) +{ + (void)path; + wlr_log(WLR_ERROR, "backgrounds require cairo support"); + return NULL; +} +#endif + +static bool background_accepts_input(struct wlr_scene_buffer *buffer, + double *sx, double *sy) +{ + (void)buffer; + (void)sx; + (void)sy; + return false; +} + +static void output_update_background(struct montis_output *output) +{ + struct montis_server *server = output->server; + if (!server->background_buffer) { + return; + } + + if (!output->background) { + output->background = wlr_scene_buffer_create(&server->scene->tree, + server->background_buffer); + if (!output->background) { + wlr_log(WLR_ERROR, "failed to create background node"); + return; + } + output->background->point_accepts_input = background_accepts_input; + wlr_scene_node_lower_to_bottom(&output->background->node); + } + else { + wlr_scene_buffer_set_buffer(output->background, server->background_buffer); + } + + struct wlr_box box; + wlr_output_layout_get_box(server->output_layout, output->wlr_output, &box); + wlr_scene_node_set_position(&output->background->node, box.x, box.y); + wlr_scene_buffer_set_dest_size(output->background, box.width, box.height); +} + static void focus_toplevel(struct montis_toplevel *toplevel, struct wlr_surface *surface) { @@ -113,9 +238,9 @@ static void keyboard_handle_key(struct wl_listener *listener, void *data) if (nsyms > 0 && syms[0] >= XKB_KEY_XF86Switch_VT_1 && syms[0] <= XKB_KEY_XF86Switch_VT_12) { /* Escape-hatch to change sessions. These should always be available key - * bindings regardless of what the soul dictates. This allows an escape - * hatch to edit the soul in a different vterm and then use the escape - * hatch below to hot-restart the soul if things get borked. */ + * bindings regardless of what the world dictates. This allows an escape + * hatch to edit the world in a different vterm and then use the escape + * hatch below to hot-restart the world if things get borked. */ if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { wlr_session_change_vt(server->session, syms[0] - XKB_KEY_XF86Switch_VT_1 + 1); @@ -124,26 +249,26 @@ static void keyboard_handle_key(struct wl_listener *listener, void *data) else if (modifiers == (WLR_MODIFIER_SHIFT | WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT) && nsyms > 0 && syms[0] == XKB_KEY_Escape) { - /* Escape-hatch to hot-reload the soul in case the soul got borked and + /* Escape-hatch to hot-reload the world in case the world got borked and * stops accepting keybindings. Ctrl+Shift+Alt+Escape will always reload the - * soul. */ + * world. */ if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - if ((ec = soul_hot_reload_same_state(&server->soul)) != 0) { - fprintf(stderr, "Failed to hot reload soul"); + if ((ec = world_hot_reload_same_state(&server->world)) != 0) { + fprintf(stderr, "Failed to hot reload world"); exit(1); } } } else { - /* Pass the information along to the soul for the soul to handle. The - * soul will return via 'handled' whether or not the key event was handled + /* Pass the information along to the world for the world to handle. The + * world will return via 'handled' whether or not the key event was handled * or not. */ if (nsyms > 0) { codepoint = xkb_state_key_get_utf32(keyboard->wlr_keyboard->xkb_state, keycode); - soul_call_update_state(server->soul, arksoul_export_handle_keybinding, - keyboard->wlr_keyboard, event, modifiers, syms[0], - codepoint, &handled); + world_call_update_state(server->world, arkworld_export_handle_keybinding, + keyboard->wlr_keyboard, event, modifiers, syms[0], + codepoint, &handled); } } } @@ -440,9 +565,8 @@ static void server_cursor_motion(struct wl_listener *listener, void *data) wlr_cursor_move(server->cursor, &event->pointer->base, event->delta_x, event->delta_y); process_cursor_motion(server, event->time_msec); - soul_call_update_state(server->soul, arksoul_export_handle_motion, event, - modifiers, - 0, server->cursor->x, server->cursor->y); + world_call_update_state(server->world, arkworld_export_handle_motion, event, + modifiers, 0, server->cursor->x, server->cursor->y); } static void server_cursor_motion_absolute(struct wl_listener *listener, @@ -465,9 +589,8 @@ static void server_cursor_motion_absolute(struct wl_listener *listener, wlr_cursor_warp_absolute(server->cursor, &event->pointer->base, event->x, event->y); process_cursor_motion(server, event->time_msec); - soul_call_update_state(server->soul, arksoul_export_handle_motion, event, - modifiers, - 1, server->cursor->x, server->cursor->y); + world_call_update_state(server->world, arkworld_export_handle_motion, event, + modifiers, 1, server->cursor->x, server->cursor->y); } static void server_cursor_button(struct wl_listener *listener, void *data) @@ -481,8 +604,8 @@ static void server_cursor_button(struct wl_listener *listener, void *data) struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); - soul_call_update_state(server->soul, arksoul_export_handle_button, event, - modifiers); + world_call_update_state(server->world, arkworld_export_handle_button, event, + modifiers); /* Notify the client with pointer focus that a button press has occurred */ // wlr_seat_pointer_notify_button(server->seat, event->time_msec, @@ -553,12 +676,18 @@ static void output_request_state(struct wl_listener *listener, void *data) wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; wlr_output_commit_state(output->wlr_output, event->state); + output_update_background(output); } static void output_destroy(struct wl_listener *listener, void *data) { struct montis_output *output = wl_container_of(listener, output, destroy); + if (output->background) { + wlr_scene_node_destroy(&output->background->node); + output->background = NULL; + } + wl_list_remove(&output->frame.link); wl_list_remove(&output->request_state.link); wl_list_remove(&output->destroy.link); @@ -630,6 +759,8 @@ static void server_new_output(struct wl_listener *listener, void *data) wlr_scene_output_create(server->scene, wlr_output); wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output); + + output_update_background(output); } static void xdg_toplevel_map(struct wl_listener *listener, void *data) @@ -640,9 +771,9 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) wl_list_insert(&toplevel->server->toplevels, &toplevel->link); fprintf(stderr, "Surface map ...\n"); - soul_call_update_state(toplevel->server->soul, arksoul_export_handle_surface, - toplevel, - SURFACE_MAP); + world_call_update_state(toplevel->server->world, + arkworld_export_handle_surface, toplevel, + SURFACE_MAP); fprintf(stderr, "/ Surface map ...\n"); focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); @@ -659,9 +790,9 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) } fprintf(stderr, "Surface unmap ...\n"); - soul_call_update_state(toplevel->server->soul, arksoul_export_handle_surface, - toplevel, - SURFACE_UNMAP); + world_call_update_state(toplevel->server->world, + arkworld_export_handle_surface, toplevel, + SURFACE_UNMAP); fprintf(stderr, "/ Surface map ...\n"); wl_list_remove(&toplevel->link); @@ -682,9 +813,9 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) wl_list_remove(&toplevel->request_fullscreen.link); fprintf(stderr, "Surface destroy ...\n"); - soul_call_update_state(toplevel->server->soul, arksoul_export_handle_surface, - toplevel, - SURFACE_DELETE); + world_call_update_state(toplevel->server->world, + arkworld_export_handle_surface, toplevel, + SURFACE_DELETE); fprintf(stderr, "/ Surface destroy ...\n"); free(toplevel); @@ -843,8 +974,7 @@ static void xdg_decoration_request_mode(struct wl_listener *listener, static void xdg_decoration_destroy(struct wl_listener *listener, void *data) { - struct montis_xdg_decoration *dec = - wl_container_of(listener, dec, destroy); + struct montis_xdg_decoration *dec = wl_container_of(listener, dec, destroy); wl_list_remove(&dec->request_mode.link); wl_list_remove(&dec->destroy.link); free(dec); @@ -927,35 +1057,42 @@ int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); char *startup_cmd = NULL; - char *soul_path = NULL; + char *world_path = NULL; + char *background_path = NULL; int c; - while ((c = getopt(argc, argv, "s:p:h")) != -1) { + while ((c = getopt(argc, argv, "s:p:b:h")) != -1) { switch (c) { case 's': startup_cmd = optarg; break; case 'p': - soul_path = optarg; + world_path = optarg; + break; + case 'b': + background_path = optarg; break; default: - printf("Usage: %s -p [soul] [-s startup command]\n", argv[0]); + printf("Usage: %s -p [world] [-s startup command] [-b background.png]\n", + argv[0]); return 0; } } - if (optind < argc || !soul_path) { - printf("Usage: %s -p [soul] [-s startup command]\n", argv[0]); + if (optind < argc || !world_path) { + printf("Usage: %s -p [world] [-s startup command] [-b background.png]\n", + argv[0]); return 0; } struct montis_server server = {0}; + server.background_path = background_path; - if (load_soul_from_file(argc, argv, soul_path, &server.soul)) { - fprintf(stderr, "Failed to read soul from file.\n"); + if (load_world_from_file(argc, argv, world_path, &server.world)) { + fprintf(stderr, "Failed to read world from file.\n"); return 1; } - soul_cold_start(&server.soul); + world_cold_start(&server.world); /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ @@ -1024,6 +1161,13 @@ int main(int argc, char *argv[]) server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); + if (server.background_path) { + server.background_buffer = load_background_buffer(server.background_path); + if (!server.background_buffer) { + wlr_log(WLR_ERROR, "background image disabled"); + } + } + /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is * used for application windows. For more detail on shells, refer to * https://drewdevault.com/2018/07/29/Wayland-shells.html. @@ -1133,6 +1277,10 @@ int main(int argc, char *argv[]) * server. */ wl_display_destroy_clients(server.wl_display); wlr_scene_node_destroy(&server.scene->tree.node); + if (server.background_buffer) { + wlr_buffer_drop(server.background_buffer); + server.background_buffer = NULL; + } wlr_xcursor_manager_destroy(server.cursor_mgr); wlr_output_layout_destroy(server.output_layout); wl_display_destroy(server.wl_display); diff --git a/ark/src/world.c b/ark/src/world.c new file mode 100644 index 0000000..e7641ed --- /dev/null +++ b/ark/src/world.c @@ -0,0 +1,199 @@ +#include "world.h" + +#include <ctype.h> +#include <dlfcn.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +/* Utility function for showing the marshalled states as hex code */ +static void shx(uint8_t *state, uint32_t sz) +{ + uint32_t i = 0; + while (i < sz) { + for (int j = 0; j < 16; ++j) { + if (i < sz) { + printf("%02x ", (unsigned int)state[i]); + } + else { + printf(" "); + } + ++i; + } + + i -= 16; + + printf(" "); + + for (int j = 0; j < 16; ++j) { + if (i < sz) { + if (isprint(state[i]) && !isspace(state[i])) { + printf("%c", state[i]); + } + else { + printf("."); + } + } + else { + printf(" "); + } + ++i; + } + printf("\n"); + } +} + +int load_world_from_dl_(dlhandle_t dl, world_t *world); + +static void lock(world_t *world) { pthread_mutex_lock(&world->lock); }; + +static void unlock(world_t *world) { pthread_mutex_unlock(&world->lock); }; + +static int load_world_from_file_(int argc, char **argv, const char *filename, + world_t *world) +{ + dlhandle_t lib = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + int ec = 0; + + if (!lib) { + fprintf(stderr, "Failed to open library: %s: %s\n", filename, dlerror()); + ec = 1; + goto end; + } + + printf("Loading file.\n"); + ec = load_world_from_dl_(lib, world); + + if (ec) { + goto end; + } + + strncpy(world->filename, filename, sizeof(world->filename)); + world->argc = argc; + world->argv = argv; + + world->arkworld_export_load(world->argc, world->argv); +end: + return ec; +} + +static void maybe_run_metaload(int argc, char **argv, world_t *world) +{ + static char *loaded_worlds[12]; + int i; + for (i = 0; i < 12 && loaded_worlds[i]; ++i) { + if (strcmp(loaded_worlds[i], world->world_name) == 0) { + return; // World is already loaded + } + } + loaded_worlds[i] = strdup(world->world_name); + + printf("First time loading %s, running metaload.\n", world->world_name); + if (world->arkworld_export_metaload) { + world->arkworld_export_metaload(argc, argv); + } +} + +int load_world_from_file(int argc, char **argv, const char *filename, + world_t *world) +{ + memset(world, 0, sizeof(*world)); + + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) { + perror("pthread_mutexattr_init"); + return 1; + } + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) { + perror("pthread_mutexattr_settype"); + return 1; + } + + if (pthread_mutex_init(&world->lock, &attr)) { + pthread_mutexattr_destroy(&attr); + perror("pthread_mutexattr_init"); + return 1; + } + pthread_mutexattr_destroy(&attr); + int rc = load_world_from_file_(argc, argv, filename, world); + + if (rc == 0) { + maybe_run_metaload(argc, argv, world); + } + + return rc; +} + +int world_hot_reload_same_state(world_t *world) +{ + char filename_cpy[PATH_MAX]; + strncpy(filename_cpy, world->filename, sizeof(filename_cpy)); + return world_hot_reload(world->argc, world->argv, filename_cpy, world); +} + +int world_hot_reload(int argc, char **argv, const char *filepath, + world_t *world) +{ + int ec = 0; + uint32_t sz = 0; + uint8_t *marshalled_state = NULL; + + printf("Hot Reloading %s\n", world->world_name); + lock(world); + + printf("Marshalling state ...\n"); + marshalled_state = world->arkworld_export_preserve(world->state, &sz); + + printf("Calling teardown ...\n"); + world->arkworld_export_release(world->state); + + printf("State Marshalled:\n"); + shx(marshalled_state, sz); + + printf("Unloading old library handle.\n"); + if (dlclose(world->library_handle)) { + printf("Could not close library handle: %s\n", dlerror()); + } + + if ((ec = load_world_from_file_(argc, argv, filepath, world))) { + goto fail; + } + + printf("Hot starting world ...\n"); + world->state = world->arkworld_export_rebirth(world, marshalled_state, sz); + +fail: + free(marshalled_state); + unlock(world); + return ec; +} + +void world_run_requested_actions(world_t *world) +{ + lock(world); + requested_action_t requested_actions[MAX_QUEUED_ACTIONS]; + size_t n_requested_actions = world->n_requested_actions; + memcpy(&requested_actions, world->requested_actions, + sizeof(requested_actions)); + world->n_requested_actions = 0; + unlock(world); + + size_t i; + for (i = 0; i < n_requested_actions; ++i) { + requested_actions[i].action(world, requested_actions[i].str_arg); + if (requested_actions[i].arg_dtor) { + requested_actions[i].arg_dtor(requested_actions[i].ptr_arg); + } + } +} + +void world_cold_start(world_t *world) +{ + lock(world); + world->state = world->arkworld_export_enworld(world); + unlock(world); +} diff --git a/ark/src/soul_load.c b/ark/src/world_load.c index fcf04a9..098c4b0 100644 --- a/ark/src/soul_load.c +++ b/ark/src/world_load.c @@ -1,37 +1,37 @@ -#include "soul.h" +#include "world.h" #include <dlfcn.h> #include <stdio.h> -#include "soul_exports.h" +#include "world_exports.h" -int load_soul_from_dl_(dlhandle_t dl, soul_t *soul) +int load_world_from_dl_(dlhandle_t dl, world_t *world) { void *ptr; int ret = 0; - const char **name = dlsym(dl, "soul_name"); + const char **name = dlsym(dl, "world_name"); if (name) { - soul->soul_name = *name; + world->world_name = *name; } else { - soul->soul_name = NULL; + world->world_name = NULL; } - soul->state = NULL; - soul->library_handle = dl; + world->state = NULL; + world->library_handle = dl; #define LOAD_SYM(ret_type, sym, args) \ do { \ ptr = dlsym(dl, #sym); \ if (!ptr) { \ - fprintf(stderr, "Soul missing %s\n", #sym); \ + fprintf(stderr, "World missing %s\n", #sym); \ ret |= 1; \ } \ - soul->sym = (ret_type(*) args)ptr; \ + world->sym = (ret_type(*) args)ptr; \ } while (0); - ARKSOUL_EXPORTS(LOAD_SYM); + ARKWORLD_EXPORTS(LOAD_SYM); #undef LOAD_SYM diff --git a/cross/README.md b/cross/README.md index cea84bb..b435e7c 100644 --- a/cross/README.md +++ b/cross/README.md @@ -2,21 +2,21 @@ Cross (Native Bridge Library) ============================= Cross is a static C library (`libcross.a`) that provides native helper -functions for a dynamically loaded soul to interact with the runtime and +functions for a dynamically loaded world to interact with the runtime and wlroots. Why a static library? --------------------- -These helpers are linked into the soul (`.so`) so they hot-reload along with -the soul, instead of being stuck inside the long-running runtime process. +These helpers are linked into the world (`.so`) so they hot-reload along with +the world, instead of being stuck inside the long-running runtime process. API surface ----------- Public headers live in `cross/include`. -- `cross/include/util.h`: soul-facing functions used via FFI, including: +- `cross/include/util.h`: world-facing functions used via FFI, including: - runtime requests (log/exit/hot-reload) - seat access - toplevel queries and basic positioning/geometry helpers diff --git a/cross/include/util.h b/cross/include/util.h index 3707ae1..bf89c2d 100644 --- a/cross/include/util.h +++ b/cross/include/util.h @@ -2,23 +2,23 @@ #define MONTIS_UTIL_H /* - * Runtime helpers exposed to souls. These operate on compositor state and are - * intended for direct FFI use from a soul implementation. + * Runtime helpers exposed to worlds. These operate on compositor state and are + * intended for direct FFI use from a world implementation. */ void montis_do_request_hot_reload(void *plugv); void montis_do_request_log(void *plugv, const char *str); void montis_do_request_exit(void *plugv, int ec); -void *arksoul_get_seat(void *ctx); -void *arksoul_toplevel_at(void *ctx, double lx, double ly); -void arksoul_get_toplevel_position(void *toplevel, double *x, double *y); -void arksoul_set_toplevel_position(void *toplevel, double x, double y); -void arksoul_get_toplevel_geometry(void *toplevel, double *x, double *y, +void *arkworld_get_seat(void *ctx); +void *arkworld_toplevel_at(void *ctx, double lx, double ly); +void arkworld_get_toplevel_position(void *toplevel, double *x, double *y); +void arkworld_set_toplevel_position(void *toplevel, double x, double y); +void arkworld_get_toplevel_geometry(void *toplevel, double *x, double *y, double *w, double *h); -void arksoul_set_toplevel_geometry(void *toplevel, double x, double y, +void arkworld_set_toplevel_geometry(void *toplevel, double x, double y, double w, double h); -void arksoul_focus_toplevel(void *toplevel); -void arksoul_warp_cursor(void *ctx, double lx, double ly); +void arkworld_focus_toplevel(void *toplevel); +void arkworld_warp_cursor(void *ctx, double lx, double ly); #endif /* MONTIS_UTIL_H */ diff --git a/cross/src/runtime_requests.c b/cross/src/runtime_requests.c index adb2b2f..cf0955c 100644 --- a/cross/src/runtime_requests.c +++ b/cross/src/runtime_requests.c @@ -5,26 +5,26 @@ #include <stdlib.h> #include <string.h> -static int soul_hot_reload_same_state_action_(soul_t *soul, void *ignore) +static int world_hot_reload_same_state_action_(world_t *world, void *ignore) { (void)ignore; - return soul_hot_reload_same_state(soul); + return world_hot_reload_same_state(world); } void montis_do_request_hot_reload(void *plugv) { - soul_t *soul = plugv; + world_t *world = plugv; - size_t n = soul->n_requested_actions++; + size_t n = world->n_requested_actions++; if (n < MAX_QUEUED_ACTIONS) { - soul->requested_actions[n].action = soul_hot_reload_same_state_action_; - soul->requested_actions[n].arg_dtor = NULL; + world->requested_actions[n].action = world_hot_reload_same_state_action_; + world->requested_actions[n].arg_dtor = NULL; } } -static int soul_do_log(soul_t *soul, void *chrs) +static int world_do_log(world_t *world, void *chrs) { - (void)soul; + (void)world; char *str = chrs; puts(str); return 0; @@ -32,17 +32,17 @@ static int soul_do_log(soul_t *soul, void *chrs) void montis_do_request_log(void *plugv, const char *str) { - soul_t *soul = plugv; + world_t *world = plugv; - size_t n = soul->n_requested_actions++; + size_t n = world->n_requested_actions++; if (n < MAX_QUEUED_ACTIONS) { - soul->requested_actions[n].action = soul_do_log; - soul->requested_actions[n].str_arg = strdup(str); - soul->requested_actions[n].arg_dtor = free; + world->requested_actions[n].action = world_do_log; + world->requested_actions[n].str_arg = strdup(str); + world->requested_actions[n].arg_dtor = free; } } -static int arksoul_do_exit(void *plugv, int ec) +static int arkworld_do_exit(void *plugv, int ec) { (void)plugv; exit(ec); @@ -51,19 +51,19 @@ static int arksoul_do_exit(void *plugv, int ec) void montis_do_request_exit(void *plugv, int ec) { - soul_t *soul = plugv; + world_t *world = plugv; - size_t n = soul->n_requested_actions++; + size_t n = world->n_requested_actions++; if (n < MAX_QUEUED_ACTIONS) { - soul->requested_actions[n].action = - (int (*)(soul_t *, void *))arksoul_do_exit; - soul->requested_actions[n].int_arg = ec; - soul->requested_actions[n].arg_dtor = NULL; + world->requested_actions[n].action = + (int (*)(world_t *, void *))arkworld_do_exit; + world->requested_actions[n].int_arg = ec; + world->requested_actions[n].arg_dtor = NULL; } } -void *arksoul_get_seat(void *ctx) +void *arkworld_get_seat(void *ctx) { - struct montis_server *server = wl_container_of(ctx, server, soul); + struct montis_server *server = wl_container_of(ctx, server, world); return server->seat; } diff --git a/cross/src/util.c b/cross/src/util.c index 0c02dfe..6497d89 100644 --- a/cross/src/util.c +++ b/cross/src/util.c @@ -6,7 +6,7 @@ static struct montis_server *server_from_ctx(void *ctx) { - struct montis_server *server = wl_container_of(ctx, server, soul); + struct montis_server *server = wl_container_of(ctx, server, world); return server; } @@ -35,7 +35,7 @@ static struct montis_toplevel *toplevel_at(struct montis_server *server, return tree ? tree->node.data : NULL; } -void *arksoul_toplevel_at(void *ctx, double lx, double ly) +void *arkworld_toplevel_at(void *ctx, double lx, double ly) { if (!ctx) { return NULL; @@ -44,7 +44,7 @@ void *arksoul_toplevel_at(void *ctx, double lx, double ly) return toplevel_at(server, lx, ly); } -void arksoul_get_toplevel_position(void *toplevel, double *x, double *y) +void arkworld_get_toplevel_position(void *toplevel, double *x, double *y) { if (!toplevel || !x || !y) { return; @@ -54,7 +54,7 @@ void arksoul_get_toplevel_position(void *toplevel, double *x, double *y) *y = tl->scene_tree->node.y; } -void arksoul_set_toplevel_position(void *toplevel, double x, double y) +void arkworld_set_toplevel_position(void *toplevel, double x, double y) { if (!toplevel) { return; @@ -63,7 +63,7 @@ void arksoul_set_toplevel_position(void *toplevel, double x, double y) wlr_scene_node_set_position(&tl->scene_tree->node, (int)x, (int)y); } -void arksoul_get_toplevel_geometry(void *toplevel, double *x, double *y, +void arkworld_get_toplevel_geometry(void *toplevel, double *x, double *y, double *w, double *h) { if (!toplevel || !x || !y || !w || !h) { @@ -78,7 +78,7 @@ void arksoul_get_toplevel_geometry(void *toplevel, double *x, double *y, *h = geo_box.height; } -void arksoul_set_toplevel_geometry(void *toplevel, double x, double y, +void arkworld_set_toplevel_geometry(void *toplevel, double x, double y, double w, double h) { if (!toplevel) { @@ -89,7 +89,7 @@ void arksoul_set_toplevel_geometry(void *toplevel, double x, double y, wlr_xdg_toplevel_set_size(tl->xdg_toplevel, (int)w, (int)h); } -void arksoul_warp_cursor(void *ctx, double lx, double ly) +void arkworld_warp_cursor(void *ctx, double lx, double ly) { if (!ctx) { return; @@ -98,7 +98,7 @@ void arksoul_warp_cursor(void *ctx, double lx, double ly) wlr_cursor_warp(server->cursor, NULL, lx, ly); } -void arksoul_focus_toplevel(void *toplevel) +void arkworld_focus_toplevel(void *toplevel) { if (!toplevel) { return; diff --git a/montis/README.md b/montis/README.md index cfe264e..f3974c2 100644 --- a/montis/README.md +++ b/montis/README.md @@ -1,30 +1,30 @@ -Montis (Haskell Window Manager Soul) +Montis (Haskell Window Manager World) ==================================== Montis is the hot-reloadable window manager logic for the Ark runtime. It builds to a shared object (`montis.so`) that Ark loads at runtime. You can -edit/rebuild the soul and hot-reload it without restarting the compositor. +edit/rebuild the world and hot-reload it without restarting the compositor. Native interface ---------------- Montis talks to the runtime through: -- the soul ABI defined by Ark (`ark/include/soul.h`), implemented in Haskell via +- the world ABI defined by Ark (`ark/include/world.h`), implemented in Haskell via `montis/src/Montis/Core/Internal/Foreign/Export.hs` and a small C shim `montis/src/harness_adapter.c`. -- the Cross bridge library (`libcross.a`), which provides soul-facing helper +- the Cross bridge library (`libcross.a`), which provides world-facing helper functions used via FFI (see `montis/src/Montis/Base/Foreign/Runtime.hs`). Building -------- -The soul is built by Stack, typically driven by the top-level CMake build: +The world is built by Stack, typically driven by the top-level CMake build: ```sh cmake -S .. -B ../build -cmake --build ../build --target soul_build +cmake --build ../build --target world_build ``` Or directly: diff --git a/montis/src/Link.hs b/montis/src/Link.hs index 9c0a07f..295d153 100644 --- a/montis/src/Link.hs +++ b/montis/src/Link.hs @@ -1,14 +1,14 @@ -- | Module that provides the start hooks using the config required to link the --- soul's shared library. +-- world's shared library. module Link () where import Config (config) import Montis.Core -foreign export ccall "arksoul_export_ensoul" +foreign export ccall "arkworld_export_enworld" coldStart :: MontisColdStart -foreign export ccall "arksoul_export_rebirth" +foreign export ccall "arkworld_export_rebirth" hotStart :: MontisHotStart coldStart :: MontisColdStart diff --git a/montis/src/Montis/Base/Foreign/Runtime.hs b/montis/src/Montis/Base/Foreign/Runtime.hs index 46507b5..5690b51 100644 --- a/montis/src/Montis/Base/Foreign/Runtime.hs +++ b/montis/src/Montis/Base/Foreign/Runtime.hs @@ -12,26 +12,26 @@ foreign import ccall "montis_do_request_log" foreign_doRequestLog :: Ptr Void -> foreign import ccall "montis_do_request_exit" foreign_doRequestExit :: Ptr Void -> CInt -> IO () -foreign import ccall "arksoul_get_seat" foreign_getSeat :: Ptr Void -> IO (Ptr Void) +foreign import ccall "arkworld_get_seat" foreign_getSeat :: Ptr Void -> IO (Ptr Void) -foreign import ccall "arksoul_toplevel_at" +foreign import ccall "arkworld_toplevel_at" foreign_toplevelAt :: Ptr Void -> CDouble -> CDouble -> IO (Ptr ForeignMontisToplevel) -foreign import ccall "arksoul_get_toplevel_position" +foreign import ccall "arkworld_get_toplevel_position" foreign_getToplevelPosition :: Ptr ForeignMontisToplevel -> Ptr CDouble -> Ptr CDouble -> IO () -foreign import ccall "arksoul_set_toplevel_position" +foreign import ccall "arkworld_set_toplevel_position" foreign_setToplevelPosition :: Ptr ForeignMontisToplevel -> CDouble -> CDouble -> IO () -foreign import ccall "arksoul_get_toplevel_geometry" +foreign import ccall "arkworld_get_toplevel_geometry" foreign_getToplevelGeometry :: Ptr ForeignMontisToplevel -> Ptr CDouble -> Ptr CDouble -> Ptr CDouble -> Ptr CDouble -> IO () -foreign import ccall "arksoul_set_toplevel_geometry" +foreign import ccall "arkworld_set_toplevel_geometry" foreign_setToplevelGeometry :: Ptr ForeignMontisToplevel -> CDouble -> CDouble -> CDouble -> CDouble -> IO () -foreign import ccall "arksoul_focus_toplevel" +foreign import ccall "arkworld_focus_toplevel" foreign_focusToplevel :: Ptr ForeignMontisToplevel -> IO () -foreign import ccall "arksoul_warp_cursor" +foreign import ccall "arkworld_warp_cursor" foreign_warpCursor :: Ptr Void -> CDouble -> CDouble -> IO () diff --git a/montis/src/Montis/Core/Internal/Foreign/Export.hs b/montis/src/Montis/Core/Internal/Foreign/Export.hs index 635955a..d3d5662 100644 --- a/montis/src/Montis/Core/Internal/Foreign/Export.hs +++ b/montis/src/Montis/Core/Internal/Foreign/Export.hs @@ -1,5 +1,5 @@ -- | This module has no public functions, but provides the surface interface --- between the Montis runtime and the soul. +-- between the Montis runtime and the world. module Montis.Core.Internal.Foreign.Export () where import Control.Monad (forM_) @@ -64,11 +64,11 @@ runForeignWithReturn fn outptr stableptr = do -- State marshal/export -- | Marshals the opaque state to a C-style byte array and size pointer. -foreign export ccall "arksoul_export_preserve" - soulMarshalState :: OpqStT -> Ptr Word32 -> IO (Ptr Word8) +foreign export ccall "arkworld_export_preserve" + worldMarshalState :: OpqStT -> Ptr Word32 -> IO (Ptr Word8) -soulMarshalState :: OpqStT -> Ptr Word32 -> IO (Ptr Word8) -soulMarshalState opqStT outlen = do +worldMarshalState :: OpqStT -> Ptr Word32 -> IO (Ptr Word8) +worldMarshalState opqStT outlen = do (_, st) <- deRefStablePtr opqStT let bs = CH.pack (marshalState st) ret <- mallocBytes (BS.length bs) @@ -80,11 +80,11 @@ soulMarshalState opqStT outlen = do -- ---------------------------------------------------------------------- -- Input handlers -foreign export ccall "arksoul_export_handle_button" - soulHandleButton :: Ptr WlrPointerButtonEvent -> Word32 -> OpqStT -> IO OpqStT +foreign export ccall "arkworld_export_handle_button" + worldHandleButton :: Ptr WlrPointerButtonEvent -> Word32 -> OpqStT -> IO OpqStT -soulHandleButton :: Ptr WlrPointerButtonEvent -> Word32 -> OpqStT -> IO OpqStT -soulHandleButton eventPtr modifiers = +worldHandleButton :: Ptr WlrPointerButtonEvent -> Word32 -> OpqStT -> IO OpqStT +worldHandleButton eventPtr modifiers = runForeign $ do s <- gets currentHooks event <- liftIO $ @@ -107,8 +107,8 @@ soulHandleButton eventPtr modifiers = -- ---------------------------------------------------------------------- -- Keybinding handler -foreign export ccall "arksoul_export_handle_keybinding" - soulHandleKeybinding :: +foreign export ccall "arkworld_export_handle_keybinding" + worldHandleKeybinding :: Ptr ForeignWlrInputDevice -> Ptr WlrEventKeyboardKey -> Word32 -> @@ -118,7 +118,7 @@ foreign export ccall "arksoul_export_handle_keybinding" OpqStT -> IO OpqStT -soulHandleKeybinding :: +worldHandleKeybinding :: Ptr ForeignWlrInputDevice -> Ptr WlrEventKeyboardKey -> Word32 -> @@ -127,7 +127,7 @@ soulHandleKeybinding :: Ptr CInt -> OpqStT -> IO OpqStT -soulHandleKeybinding inputDevicePtr eventPtr mods sym cp = +worldHandleKeybinding inputDevicePtr eventPtr mods sym cp = runForeignWithReturn $ do s <- gets currentHooks event <- liftIO $ @@ -153,11 +153,11 @@ soulHandleKeybinding inputDevicePtr eventPtr mods sym cp = -- ---------------------------------------------------------------------- -- Motion handler -foreign export ccall "arksoul_export_handle_motion" - soulHandleMotion :: Ptr Void -> Word32 -> Word32 -> CDouble -> CDouble -> OpqStT -> IO OpqStT +foreign export ccall "arkworld_export_handle_motion" + worldHandleMotion :: Ptr Void -> Word32 -> Word32 -> CDouble -> CDouble -> OpqStT -> IO OpqStT -soulHandleMotion :: Ptr Void -> Word32 -> Word32 -> CDouble -> CDouble -> OpqStT -> IO OpqStT -soulHandleMotion eventPtr modifiers isAbsolute lx ly = +worldHandleMotion :: Ptr Void -> Word32 -> Word32 -> CDouble -> CDouble -> OpqStT -> IO OpqStT +worldHandleMotion eventPtr modifiers isAbsolute lx ly = runForeign $ do s <- gets currentHooks event <- liftIO $ @@ -201,26 +201,26 @@ soulHandleMotion eventPtr modifiers isAbsolute lx ly = -- | Function exported to the harness to handle the mapping/unmapping/deletion -- of an XDG surface. -foreign export ccall "arksoul_export_handle_surface" - soulHandleSurface :: +foreign export ccall "arkworld_export_handle_surface" + worldHandleSurface :: Ptr ForeignWlrXdgSurface -> CInt -> OpqStT -> IO OpqStT -soulHandleSurface :: +worldHandleSurface :: Ptr ForeignWlrXdgSurface -> CInt -> OpqStT -> IO OpqStT -soulHandleSurface p t = +worldHandleSurface p t = runForeign $ do s <- gets currentHooks surfaceHook s (SurfaceEvent (toEnum $ fromIntegral t) (toSurface p)) -- | Function exported to the harness to handle the mapping/unmapping/deletion -- of an XWayland surface. -foreign export ccall "arksoul_export_handle_xwayland_surface" - soulHandleXWaylandSurface :: +foreign export ccall "arkworld_export_handle_xwayland_surface" + worldHandleXWaylandSurface :: Ptr ForeignWlrXWaylandSurface -> CInt -> OpqStT -> IO OpqStT -soulHandleXWaylandSurface :: +worldHandleXWaylandSurface :: Ptr ForeignWlrXWaylandSurface -> CInt -> OpqStT -> IO OpqStT -soulHandleXWaylandSurface p t = +worldHandleXWaylandSurface p t = runForeign $ do s <- gets currentHooks surfaceHook s (SurfaceEvent (toEnum $ fromIntegral t) (toSurface p)) diff --git a/montis/src/Montis/Core/Monad.hs b/montis/src/Montis/Core/Monad.hs index 0eee819..72b2321 100644 --- a/montis/src/Montis/Core/Monad.hs +++ b/montis/src/Montis/Core/Monad.hs @@ -23,7 +23,7 @@ type MontisContext = Context Montis -- | A State type for the Montis monad. type MontisState = State Montis --- | The Opaque State Type passed between the soul and the runtime. The +-- | The Opaque State Type passed between the world and the runtime. The -- OpqStT *is* the opq_st_t from the runtime code. type OpqStT = StablePtr (MontisContext, MontisState) @@ -34,7 +34,7 @@ newtype Montis a where deriving (Functor, Applicative, Monad, MonadState MontisState, MonadIO) -- | Reader access is scoped to the config portion of the full context; this --- keeps soul code from mutating the context while still allowing read-only +-- keeps world code from mutating the context while still allowing read-only -- access to configuration. instance MonadReader MontisConfig Montis where ask :: Montis MontisConfig @@ -47,7 +47,7 @@ instance MonadReader MontisConfig Montis where local cfn (Montis fn) = Montis $ local (\ctx -> ctx {ctxConfig = cfn (ctxConfig ctx)}) fn --- | Access the soul self pointer stored in the context. +-- | Access the world self pointer stored in the context. getSelfPtr :: Montis SelfPtr getSelfPtr = Montis $ asks ctxSelfPtr diff --git a/montis/src/Montis/Core/State.hs b/montis/src/Montis/Core/State.hs index 6a2d5d0..3df06b2 100644 --- a/montis/src/Montis/Core/State.hs +++ b/montis/src/Montis/Core/State.hs @@ -10,11 +10,11 @@ import Montis.Core.Events import Montis.Core.Extensions import Text.Read (readMaybe) --- | An opaque type used for the soul's self-reference. +-- | An opaque type used for the world's self-reference. newtype SelfPtr where SelfPtr :: Ptr Void -> SelfPtr --- | This is the context the soul operates under. The context contains data +-- | This is the context the world operates under. The context contains data -- which must be provided by the runtime or the configuration. This data may not -- be cold-created. -- diff --git a/montis/src/Montis/Core/Soul/Interface.hs b/montis/src/Montis/Core/World/Interface.hs index 3271ab7..39d4e4c 100644 --- a/montis/src/Montis/Core/Soul/Interface.hs +++ b/montis/src/Montis/Core/World/Interface.hs @@ -1,5 +1,5 @@ --- | Provides the soul interface through foreign exports. -module Montis.Core.Soul.Interface where +-- | Provides the world interface through foreign exports. +module Montis.Core.World.Interface where import Data.ByteString (ByteString) import Data.Data (Typeable) diff --git a/montis/src/harness_adapter.c b/montis/src/harness_adapter.c index fb5646c..b6c1979 100644 --- a/montis/src/harness_adapter.c +++ b/montis/src/harness_adapter.c @@ -4,25 +4,25 @@ // Currently these functions exclusively enable/disable the Haskell runtime. #include "HsFFI.h" -#include "soul_interface.h" +#include "world_interface.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> -const char *soul_name = "Montis"; +const char *world_name = "Montis"; extern void performMajorGC(); -void arksoul_export_metaload(int argc, char** argv) +void arkworld_export_metaload(int argc, char** argv) { // hs_init(&argc, &argv); } -void arksoul_export_load(int argc, char **argv) { +void arkworld_export_load(int argc, char **argv) { hs_init(&argc, &argv); } -void arksoul_export_release(opqst_t st) { +void arkworld_export_release(opqst_t st) { hs_exit(); } @@ -34,10 +34,10 @@ void shell_exec(const char* cmd) { } static const char msg[] = - "Montis Soul v 0.01\n\n" + "Montis World v 0.01\n\n" "Welcome, and thank you for your interest.\n\n" - "This is merely a soul for the Montis Compositor and not meant to be\n" - "executed as a standalone binary. This soul requires a harness to run\n" + "This is merely a world for the Montis Compositor and not meant to be\n" + "executed as a standalone binary. This world requires a harness to run\n" "To use this file, please use './wtr_harness [full-path-to-wtr.so]'\n" "That will allow you to see how this compositor works in all its glory!\n"; static const int msg_sz = sizeof(msg); |