diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2026-01-07 00:26:32 -0700 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2026-01-07 00:26:32 -0700 |
| commit | 4847aa0b4a6f945c24e1523e729652271bbc9680 (patch) | |
| tree | 9ffac8b1934153cbde6f6814277f80c337d937b1 /cross | |
| parent | 37e5037b68fb35276f0c426779f61aa278d5b21b (diff) | |
| download | montis-4847aa0b4a6f945c24e1523e729652271bbc9680.tar.gz montis-4847aa0b4a6f945c24e1523e729652271bbc9680.tar.bz2 montis-4847aa0b4a6f945c24e1523e729652271bbc9680.zip | |
[rebrand] Arken -> Ark, Plugin -> Soul, Erebor -> Cross
Diffstat (limited to 'cross')
| -rw-r--r-- | cross/CMakeLists.txt | 27 | ||||
| -rw-r--r-- | cross/README.md | 32 | ||||
| -rw-r--r-- | cross/include/util.h | 24 | ||||
| -rw-r--r-- | cross/src/runtime_requests.c | 69 | ||||
| -rw-r--r-- | cross/src/util.c | 133 |
5 files changed, 285 insertions, 0 deletions
diff --git a/cross/CMakeLists.txt b/cross/CMakeLists.txt new file mode 100644 index 0000000..7180c6e --- /dev/null +++ b/cross/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.10) +project(cross LANGUAGES C) + +set(WLROOTS_SOURCE_DIR "${CMAKE_BINARY_DIR}/wlroots-src") +set(WLROOTS_BUILD_DIR "${CMAKE_BINARY_DIR}/wlroots") + +file(GLOB_RECURSE CROSS_SOURCES src/*.c) + +add_library(cross STATIC ${CROSS_SOURCES}) +set_target_properties(cross PROPERTIES + POSITION_INDEPENDENT_CODE ON + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/cross" +) + +add_dependencies(cross wlroots_build) + +target_include_directories(cross PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/ark/include + ${CMAKE_BINARY_DIR} + ${WLROOTS_SOURCE_DIR}/include + ${WLROOTS_BUILD_DIR}/include + ${WLROOTS_BUILD_DIR}/protocol + /usr/include/pixman-1 +) + +target_compile_definitions(cross PRIVATE WLR_USE_UNSTABLE) diff --git a/cross/README.md b/cross/README.md new file mode 100644 index 0000000..cea84bb --- /dev/null +++ b/cross/README.md @@ -0,0 +1,32 @@ +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 +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. + +API surface +----------- + +Public headers live in `cross/include`. + +- `cross/include/util.h`: soul-facing functions used via FFI, including: + - runtime requests (log/exit/hot-reload) + - seat access + - toplevel queries and basic positioning/geometry helpers + +Building +-------- + +Cross is built via the top-level CMake project: + +```sh +cmake -S .. -B ../build +cmake --build ../build --target cross +``` diff --git a/cross/include/util.h b/cross/include/util.h new file mode 100644 index 0000000..3707ae1 --- /dev/null +++ b/cross/include/util.h @@ -0,0 +1,24 @@ +#ifndef MONTIS_UTIL_H +#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. + */ + +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, + double *w, double *h); +void arksoul_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); + +#endif /* MONTIS_UTIL_H */ diff --git a/cross/src/runtime_requests.c b/cross/src/runtime_requests.c new file mode 100644 index 0000000..adb2b2f --- /dev/null +++ b/cross/src/runtime_requests.c @@ -0,0 +1,69 @@ +#include "util.h" +#include "wl.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int soul_hot_reload_same_state_action_(soul_t *soul, void *ignore) +{ + (void)ignore; + return soul_hot_reload_same_state(soul); +} + +void montis_do_request_hot_reload(void *plugv) +{ + soul_t *soul = plugv; + + size_t n = soul->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; + } +} + +static int soul_do_log(soul_t *soul, void *chrs) +{ + (void)soul; + char *str = chrs; + puts(str); + return 0; +} + +void montis_do_request_log(void *plugv, const char *str) +{ + soul_t *soul = plugv; + + size_t n = soul->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; + } +} + +static int arksoul_do_exit(void *plugv, int ec) +{ + (void)plugv; + exit(ec); + return 0; +} + +void montis_do_request_exit(void *plugv, int ec) +{ + soul_t *soul = plugv; + + size_t n = soul->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; + } +} + +void *arksoul_get_seat(void *ctx) +{ + struct montis_server *server = wl_container_of(ctx, server, soul); + return server->seat; +} diff --git a/cross/src/util.c b/cross/src/util.c new file mode 100644 index 0000000..0c02dfe --- /dev/null +++ b/cross/src/util.c @@ -0,0 +1,133 @@ +#include "util.h" +#include "wl.h" + +#include <wlr/types/wlr_scene.h> +#include <wlr/types/wlr_xdg_shell.h> + +static struct montis_server *server_from_ctx(void *ctx) +{ + struct montis_server *server = wl_container_of(ctx, server, soul); + return server; +} + +static struct montis_toplevel *toplevel_at(struct montis_server *server, + double lx, double ly) +{ + double sx = 0.0; + double sy = 0.0; + + struct wlr_scene_node *node = + wlr_scene_node_at(&server->scene->tree.node, lx, ly, &sx, &sy); + if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { + return NULL; + } + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_try_from_buffer(scene_buffer); + if (!scene_surface) { + return NULL; + } + + struct wlr_scene_tree *tree = node->parent; + while (tree != NULL && tree->node.data == NULL) { + tree = tree->node.parent; + } + return tree ? tree->node.data : NULL; +} + +void *arksoul_toplevel_at(void *ctx, double lx, double ly) +{ + if (!ctx) { + return NULL; + } + struct montis_server *server = server_from_ctx(ctx); + return toplevel_at(server, lx, ly); +} + +void arksoul_get_toplevel_position(void *toplevel, double *x, double *y) +{ + if (!toplevel || !x || !y) { + return; + } + struct montis_toplevel *tl = toplevel; + *x = tl->scene_tree->node.x; + *y = tl->scene_tree->node.y; +} + +void arksoul_set_toplevel_position(void *toplevel, double x, double y) +{ + if (!toplevel) { + return; + } + struct montis_toplevel *tl = toplevel; + wlr_scene_node_set_position(&tl->scene_tree->node, (int)x, (int)y); +} + +void arksoul_get_toplevel_geometry(void *toplevel, double *x, double *y, + double *w, double *h) +{ + if (!toplevel || !x || !y || !w || !h) { + return; + } + struct montis_toplevel *tl = toplevel; + struct wlr_box geo_box; + wlr_xdg_surface_get_geometry(tl->xdg_toplevel->base, &geo_box); + *x = tl->scene_tree->node.x; + *y = tl->scene_tree->node.y; + *w = geo_box.width; + *h = geo_box.height; +} + +void arksoul_set_toplevel_geometry(void *toplevel, double x, double y, + double w, double h) +{ + if (!toplevel) { + return; + } + struct montis_toplevel *tl = toplevel; + wlr_scene_node_set_position(&tl->scene_tree->node, (int)x, (int)y); + wlr_xdg_toplevel_set_size(tl->xdg_toplevel, (int)w, (int)h); +} + +void arksoul_warp_cursor(void *ctx, double lx, double ly) +{ + if (!ctx) { + return; + } + struct montis_server *server = server_from_ctx(ctx); + wlr_cursor_warp(server->cursor, NULL, lx, ly); +} + +void arksoul_focus_toplevel(void *toplevel) +{ + if (!toplevel) { + return; + } + struct montis_toplevel *tl = toplevel; + struct montis_server *server = tl->server; + struct wlr_seat *seat = server->seat; + struct wlr_surface *surface = tl->xdg_toplevel->base->surface; + struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; + + if (prev_surface == surface) { + return; + } + if (prev_surface) { + struct wlr_xdg_toplevel *prev_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); + if (prev_toplevel != NULL) { + wlr_xdg_toplevel_set_activated(prev_toplevel, false); + } + } + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + wlr_scene_node_raise_to_top(&tl->scene_tree->node); + wl_list_remove(&tl->link); + wl_list_insert(&server->toplevels, &tl->link); + wlr_xdg_toplevel_set_activated(tl->xdg_toplevel, true); + if (keyboard != NULL) { + wlr_seat_keyboard_notify_enter(seat, surface, keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers); + } +} |