aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--harness/include/foreign_intf.h3
-rw-r--r--harness/include/plugin.h23
-rw-r--r--harness/src/main.c5
-rw-r--r--harness/src/plugin.c123
-rw-r--r--harness/src/wl.c25
-rw-r--r--harness/tools/genbuild.pl7
-rw-r--r--harness/tools/genintf.pl6
-rw-r--r--package.yaml2
-rw-r--r--src/Wetterhorn/Core.hs13
-rw-r--r--src/Wetterhorn/Core/ForeignInterface.hs16
-rw-r--r--src/Wetterhorn/FFI.hs18
-rw-r--r--src/harness_adapter.c13
12 files changed, 190 insertions, 64 deletions
diff --git a/harness/include/foreign_intf.h b/harness/include/foreign_intf.h
index 3a4b2a3..fc079e2 100644
--- a/harness/include/foreign_intf.h
+++ b/harness/include/foreign_intf.h
@@ -18,6 +18,9 @@ typedef struct FOREIGN_INTERFACE {
/* Requests the harness hot reload the current plugin. */
EXPORT(void (*request_hot_reload)(ctx_t ctx));
+ /* Requests the harness hot reload the current plugin. */
+ EXPORT(void (*do_log)(ctx_t ctx, const char* str));
+
} foreign_interface_t;
#undef EXPORT
diff --git a/harness/include/plugin.h b/harness/include/plugin.h
index db56845..1e4a161 100644
--- a/harness/include/plugin.h
+++ b/harness/include/plugin.h
@@ -7,7 +7,7 @@
#include <stdint.h>
#include <stdlib.h>
-#include "foreign_intf.h"
+#include <foreign_intf.h>
/*
* Marker macro to define what functions should be exported. This generates the
@@ -15,6 +15,12 @@
*/
#define EXPORT(a) a
+#define EXPORT_INCLUDE(a)
+
+EXPORT_INCLUDE(<foreign_intf.h>)
+
+#define MAX_QUEUED_ACTIONS 8
+
typedef void *dlhandle_t;
/* Opaque state for a plugin. Not to be touched by the harness (not that it
@@ -25,7 +31,9 @@ struct PLUGIN;
/* This structure represents an action requested by the plugin for the harness.
*/
typedef struct {
- int (*action)(struct PLUGIN *requester);
+ int (*action)(struct PLUGIN *requester, void* arg);
+ void (*arg_dtor)(void* arg);
+ void* arg;
} requested_action_t;
/*
@@ -62,6 +70,9 @@ typedef struct PLUGIN {
* outcomes. */
pthread_mutex_t lock;
+ /** Set to not-zero if this plugin is initialized, otherwise set to zero. */
+ int initialized;
+
/* The handle to the shared library. */
dlhandle_t library_handle;
@@ -73,7 +84,7 @@ typedef struct PLUGIN {
/** Intializes the plugin with the given argc/argv. This is the first thing
* called on the plugin and is called immediately after the library is loaded.
*/
- EXPORT(void (*plugin_load)(int argc, char **argv, foreign_interface_t *intf));
+ EXPORT(void (*plugin_load)(int argc, char** argv, foreign_interface_t *intf));
/* Start the plugin with the marshalled state from the previous plugin.
*
@@ -122,10 +133,11 @@ typedef struct PLUGIN {
* 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[8];
+ requested_action_t requested_actions[MAX_QUEUED_ACTIONS];
} plugin_t;
#undef EXPORT
+#undef EXPORT_INCLUDE
/* Reloads the plugin. This tears down the existing plugin, marshals the state
* for it and reloads it.
@@ -141,6 +153,9 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
*/
int plugin_hot_reload_same_state(plugin_t *plugin);
+/* Starts a plugin in a cold state. Called after load_plugin_from_file. */
+void plugin_cold_start(plugin_t* plugin);
+
/* Reads a plugin from a filename. */
int load_plugin_from_file(int argc, char **argv, const char *filename,
plugin_t *plugin);
diff --git a/harness/src/main.c b/harness/src/main.c
index 995d11e..05b06ff 100644
--- a/harness/src/main.c
+++ b/harness/src/main.c
@@ -65,12 +65,11 @@ int main_(int argc, char **argv) {
}
plugin_t plugin;
- if (load_plugin_from_file(argv[1], &plugin)) {
+ if (load_plugin_from_file(argc, argv, argv[1], &plugin)) {
fprintf(stderr, "Exiting due to other failures.\n");
return 1;
}
- plugin.plugin_load(argc, argv);
- plugin.state = plugin.plugin_cold_start();
+ plugin_cold_start(&plugin);
run_plugin(argc, argv, &plugin);
diff --git a/harness/src/plugin.c b/harness/src/plugin.c
index 808088f..f1cc361 100644
--- a/harness/src/plugin.c
+++ b/harness/src/plugin.c
@@ -3,6 +3,7 @@
#include <ctype.h>
#include <dlfcn.h>
+#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -45,31 +46,68 @@ static void shx(uint8_t *state, uint32_t sz)
int load_plugin_from_dl_(dlhandle_t dl, plugin_t *plug);
+static void lock(plugin_t *plugin)
+{
+ pthread_mutex_lock(&plugin->lock);
+};
+
+static void unlock(plugin_t *plugin)
+{
+ pthread_mutex_unlock(&plugin->lock);
+};
+
+static int plugin_hot_reload_same_state_action_(plugin_t *plugin, void* ignore)
+{
+ return plugin_hot_reload_same_state(plugin);
+}
+
void do_request_hot_reload(void *plugv)
{
plugin_t *plugin = plugv;
- if (plugin->n_requested_actions < 8) {
- plugin->requested_actions[plugin->n_requested_actions++].action =
- plugin_hot_reload_same_state;
+ size_t n = plugin->n_requested_actions++;
+ if (n < 8) {
+ plugin->requested_actions[n].action = plugin_hot_reload_same_state_action_;
+ plugin->requested_actions[n].arg_dtor = NULL;
}
}
-int load_plugin_from_file(int argc, char **argv, const char *filename,
- plugin_t *plugin)
+static int plugin_do_log(plugin_t* plugin, void* chrs)
+{
+ char* str = chrs;
+ puts(str);
+ return 0;
+}
+
+void do_request_log(void *plugv, const char* str)
+{
+ plugin_t *plugin = plugv;
+
+ size_t n = plugin->n_requested_actions++;
+ if (n < 8) {
+ plugin->requested_actions[n].action = plugin_do_log;
+ plugin->requested_actions[n].arg = strdup(str);
+ plugin->requested_actions[n].arg_dtor = free;
+ }
+}
+
+static int load_plugin_from_file_(int argc, char **argv, const char *filename,
+ plugin_t *plugin)
{
dlhandle_t lib = dlopen(filename, RTLD_LAZY);
+ int ec = 0;
if (!lib) {
fprintf(stderr, "Failed to open library: %s: %s\n", filename, dlerror());
- return 1;
+ ec = 1;
+ goto end;
}
printf("Loading file.\n");
- int ec = load_plugin_from_dl_(lib, plugin);
+ ec = load_plugin_from_dl_(lib, plugin);
if (ec) {
- return ec;
+ goto end;
}
strncpy(plugin->filename, filename, sizeof(plugin->filename));
@@ -78,13 +116,43 @@ int load_plugin_from_file(int argc, char **argv, const char *filename,
plugin->foreign_intf.ctx = plugin;
plugin->foreign_intf.request_hot_reload = do_request_hot_reload;
- return 0;
+ plugin->foreign_intf.do_log = do_request_log;
+
+ plugin->plugin_load(plugin->argc, plugin->argv, &plugin->foreign_intf);
+end:
+ return ec;
+}
+
+int load_plugin_from_file(int argc, char **argv, const char *filename,
+ plugin_t *plugin)
+{
+ memset(plugin, 0, sizeof(*plugin));
+
+ 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(&plugin->lock, &attr)) {
+ pthread_mutexattr_destroy(&attr);
+ perror("pthread_mutexattr_init");
+ return 1;
+ }
+ pthread_mutexattr_destroy(&attr);
+ return load_plugin_from_file_(argc, argv, filename, plugin);
}
int plugin_hot_reload_same_state(plugin_t *plugin)
{
- return plugin_hot_reload(plugin->argc, plugin->argv, plugin->filename,
- plugin);
+ char filename_cpy[PATH_MAX];
+ strncpy(filename_cpy, plugin->filename, sizeof(filename_cpy));
+ return plugin_hot_reload(plugin->argc, plugin->argv, filename_cpy, plugin);
}
int plugin_hot_reload(int argc, char **argv, const char *filepath,
@@ -95,7 +163,7 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
uint8_t *marshalled_state = NULL;
printf("Hot Reloading %s\n", plugin->plugin_name);
- pthread_mutex_lock(&plugin->lock);
+ lock(plugin);
printf("Marshalling state ...\n");
marshalled_state = plugin->plugin_marshal_state(plugin->state, &sz);
@@ -107,9 +175,11 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
shx(marshalled_state, sz);
printf("Unloading old library handle.\n");
- dlclose(plugin->library_handle);
+ if (dlclose(plugin->library_handle)) {
+ printf("Could not close library handle: %s\n", dlerror());
+ }
- if ((ec = load_plugin_from_file(argc, argv, filepath, plugin))) {
+ if ((ec = load_plugin_from_file_(argc, argv, filepath, plugin))) {
goto fail;
}
@@ -120,15 +190,32 @@ int plugin_hot_reload(int argc, char **argv, const char *filepath,
fail:
free(marshalled_state);
- pthread_mutex_unlock(&plugin->lock);
+ unlock(plugin);
return ec;
}
void plugin_run_requested_actions(plugin_t *plugin)
{
+ lock(plugin);
+ requested_action_t requested_actions[MAX_QUEUED_ACTIONS];
+ size_t n_requested_actions = plugin->n_requested_actions;
+ memcpy(&requested_actions, plugin->requested_actions,
+ sizeof(requested_actions));
+ plugin->n_requested_actions = 0;
+ unlock(plugin);
+
size_t i;
- for (i = 0; i < plugin->n_requested_actions; ++i) {
- plugin->requested_actions[i].action(plugin);
+ for (i = 0; i < n_requested_actions; ++i) {
+ requested_actions[i].action(plugin, requested_actions[i].arg);
+ if (requested_actions[i].arg_dtor) {
+ requested_actions[i].arg_dtor(requested_actions[i].arg);
+ }
}
- plugin->n_requested_actions = 0;
+}
+
+void plugin_cold_start(plugin_t *plugin)
+{
+ lock(plugin);
+ plugin->state = plugin->plugin_cold_start();
+ unlock(plugin);
}
diff --git a/harness/src/wl.c b/harness/src/wl.c
index 53932e7..474123a 100644
--- a/harness/src/wl.c
+++ b/harness/src/wl.c
@@ -22,6 +22,7 @@
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
+#include <wlr/xwayland.h>
#include <xkbcommon/xkbcommon.h>
// This macro is responsible for calling a handler on a plugin. This macro will
@@ -81,9 +82,6 @@ struct tinywl_server {
struct wl_list outputs;
struct wl_listener new_output;
- int plugin_argc;
- char **plugin_argv;
- const char *plugin_fpath;
plugin_t plugin;
};
@@ -189,8 +187,7 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym)
wl_display_terminate(server->wl_display);
break;
case XKB_KEY_F5:
- plugin_hot_reload(server->plugin_argc, server->plugin_argv,
- server->plugin_fpath, &server->plugin);
+ plugin_hot_reload_same_state(&server->plugin);
break;
case XKB_KEY_F1:
/* Cycle to the next view */
@@ -256,8 +253,12 @@ static void server_new_keyboard(struct tinywl_server *server,
/* We need to prepare an XKB keymap and assign it to the keyboard. This
* assumes the defaults (e.g. layout = "us"). */
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ struct xkb_rule_names names = (struct xkb_rule_names) { 0 };
+ names.layout = "jr";
+ names.variant = "jdvprk";
+ names.options = "numpad:mac";
struct xkb_keymap *keymap =
- xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
wlr_keyboard_set_keymap(device->keyboard, keymap);
xkb_keymap_unref(keymap);
@@ -908,17 +909,13 @@ int main(int argc, char *argv[])
struct tinywl_server server;
- if (load_plugin_from_file(plugin, &server.plugin)) {
+ if (load_plugin_from_file(argc, argv, plugin, &server.plugin)) {
fprintf(stderr, "Failed to read plugin from file.\n");
return 1;
}
// server.plugin.plugin_metaload(argc, argv);
- server.plugin.plugin_load(argc, argv);
- server.plugin.state = server.plugin.plugin_cold_start();
- server.plugin_fpath = plugin;
- server.plugin_argv = argv;
- server.plugin_argc = argc;
+ plugin_cold_start(&server.plugin);
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
@@ -941,7 +938,8 @@ int main(int argc, char *argv[])
* to dig your fingers in and play with their behavior if you want. Note that
* the clients cannot set the selection directly without compositor approval,
* see the handling of the request_set_selection event below.*/
- wlr_compositor_create(server.wl_display, server.renderer);
+ struct wlr_compositor* compositor;
+ compositor = wlr_compositor_create(server.wl_display, server.renderer);
wlr_data_device_manager_create(server.wl_display);
/* Creates an output layout, which a wlroots utility for working with an
@@ -1048,6 +1046,7 @@ int main(int argc, char *argv[])
* loop configuration to listen to libinput events, DRM events, generate
* frame events at the refresh rate, and so on. */
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
+ wlr_xwayland_create(server.wl_display, compositor, false);
wl_display_run(server.wl_display);
/* Once wl_display_run returns, we shut down the server. */
diff --git a/harness/tools/genbuild.pl b/harness/tools/genbuild.pl
index 1b1a005..7020e81 100644
--- a/harness/tools/genbuild.pl
+++ b/harness/tools/genbuild.pl
@@ -5,6 +5,7 @@ $comment="";
print "#include <stdio.h>\n";
print "#include <dlfcn.h>\n";
print "#include <pthread.h>\n";
+print "#include <string.h>\n";
print "#include \"plugin.h\"\n\n";
print "int load_plugin_from_dl_(dlhandle_t dl, plugin_t* plug)\n";
@@ -34,11 +35,5 @@ while (<>) {
$comment="";
}
}
-print "\n if (!ret) {\n";
-print " if(pthread_mutex_init(&plug->lock, NULL)) {\n";
-print " fprintf(stderr, \"Failed to initalize plugin lock\");\n";
-print " ret |= 1;\n";
-print " }\n";
-print " }\n";
print "\n return ret;\n";
print "}\n";
diff --git a/harness/tools/genintf.pl b/harness/tools/genintf.pl
index 6e9d477..6f53951 100644
--- a/harness/tools/genintf.pl
+++ b/harness/tools/genintf.pl
@@ -17,10 +17,14 @@ while (<>) {
$comment="$comment$_";
}
- if (/^\s*EXPORT\(\s*((?:\w|\s*\*\s*)+)\s*\(\*(\w+)\)\s*\((.*)\)\);/) {
+ if (/^\s*EXPORT_INCLUDE\((.*)\)/) {
+ print "#include $1\n";
+ } elsif (/^\s*EXPORT\(\s*((?:\w|\s*\*\s*)+)\s*\(\*(\w+)\)\s*\((.*)\)\);/) {
print "$comment";
print "$1 $2($3);\n\n";
$comment="";
+ } elsif (/^\s*EXPORT\((.*)\)/) {
+ print "$1\n";
}
}
print "#endif /* _PLUG_INTF */\n";
diff --git a/package.yaml b/package.yaml
index 7ecbbc1..1232b90 100644
--- a/package.yaml
+++ b/package.yaml
@@ -44,6 +44,7 @@ ghc-options:
- -Wpartial-fields
- -Wredundant-constraints
- -XTupleSections
+- -XViewPatterns
- -fPIC
executables:
@@ -60,6 +61,7 @@ executables:
- -g3
- -shared
- -Iharness/build/
+ - -Iharness/include/
tests:
wetterhorn-test:
diff --git a/src/Wetterhorn/Core.hs b/src/Wetterhorn/Core.hs
index d3b3f56..8d4e6b7 100644
--- a/src/Wetterhorn/Core.hs
+++ b/src/Wetterhorn/Core.hs
@@ -15,14 +15,16 @@ module Wetterhorn.Core
readWState,
defaultConfig,
requestHotReload,
+ ctxConfig,
)
where
import Control.Arrow (first)
import Control.Exception
+import Control.Monad (when)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as CH
-import Foreign (Ptr, StablePtr, Word32, newStablePtr, ptrToIntPtr)
+import Foreign (Ptr, StablePtr, Word32, newStablePtr, ptrToIntPtr, castForeignPtr)
import Numeric (showHex)
import Text.Printf
import Wetterhorn.Core.ForeignInterface (ForeignInterface)
@@ -41,6 +43,11 @@ requestHotReload = do
fi <- ctxForeignInterface <$> getWContext
wio $ ForeignInterface.requestHotReload fi
+requestLog :: String -> W ()
+requestLog str = do
+ fi <- ctxForeignInterface <$> getWContext
+ wio $ ForeignInterface.requestLog fi str
+
initWetterhorn :: WConfig -> IO Wetterhorn
initWetterhorn conf = do
foreignInterface <- ForeignInterface.getForeignInterface
@@ -64,7 +71,9 @@ defaultConfig =
WConfig
{ keybindingHandler = \sym -> do
i <- incrementState
- wio (printf "[%d] Got key %d\n" i sym),
+ wio (printf "[%d] Got key: %d\n" i sym)
+ when (sym == 111) requestHotReload
+ when (sym == 112) (requestLog "Hey daddy ths is a log statement.\n"),
surfaceHandler = \state ptr -> wio (printf "Surface %s is %s\n" (showHex (ptrToIntPtr ptr) "") (show state))
}
diff --git a/src/Wetterhorn/Core/ForeignInterface.hs b/src/Wetterhorn/Core/ForeignInterface.hs
index acd98d6..de4779b 100644
--- a/src/Wetterhorn/Core/ForeignInterface.hs
+++ b/src/Wetterhorn/Core/ForeignInterface.hs
@@ -6,18 +6,24 @@ where
import Data.Void (Void)
import Foreign (Ptr, Storable (peekByteOff, sizeOf))
+import Foreign.C.String
import GHC.Exts (FunPtr)
type CtxT = Ptr Void
type ForeignCall = CtxT -> IO ()
+type ForeignCallStr = CtxT -> CString -> IO ()
+
foreign import ccall "get_foreign_interface" foreignInterfacePtr :: IO (Ptr ())
foreign import ccall "dynamic" toForeignCall :: FunPtr ForeignCall -> ForeignCall
+foreign import ccall "dynamic" toForeignCallStr :: FunPtr ForeignCallStr -> ForeignCallStr
+
data ForeignInterface = ForeignInterface
- { requestHotReload :: IO ()
+ { requestHotReload :: IO (),
+ requestLog :: String -> IO ()
}
getForeignInterface :: IO ForeignInterface
@@ -26,11 +32,11 @@ getForeignInterface = do
ctx <- peekByteOff ptr 0
requestHotReloadFn <- peekByteOff ptr (sizeOf ctx)
-
- let _ = (ctx :: CtxT)
- let _ = (requestHotReloadFn :: FunPtr ForeignCall)
+ doLogFn <- peekByteOff ptr (sizeOf ctx + sizeOf requestHotReloadFn)
return $
ForeignInterface
- { requestHotReload = toForeignCall requestHotReloadFn ctx
+ { requestHotReload = toForeignCall requestHotReloadFn ctx,
+ requestLog = \str ->
+ withCString str $ \cs -> toForeignCallStr doLogFn ctx cs
}
diff --git a/src/Wetterhorn/FFI.hs b/src/Wetterhorn/FFI.hs
index 1d74248..3221903 100644
--- a/src/Wetterhorn/FFI.hs
+++ b/src/Wetterhorn/FFI.hs
@@ -21,27 +21,27 @@ import Wetterhorn.Core
runForeign :: (WConfig -> W ()) -> Wetterhorn -> IO Wetterhorn
runForeign fn stblptr = do
- (conf, st) <- deRefStablePtr stblptr
+ (ctx, st) <- deRefStablePtr stblptr
freeStablePtr stblptr
- (_, state') <- runW (fn conf) (conf, st)
- newStablePtr (conf, state')
+ (_, state') <- runW (fn $ ctxConfig ctx) (ctx, st)
+ newStablePtr (ctx, state')
runForeignWithReturn :: (Storable a) => (WConfig -> W a) -> Ptr a -> Wetterhorn -> IO Wetterhorn
runForeignWithReturn fn ptr stableptr = do
- (conf, st) <- deRefStablePtr stableptr
+ (ctx, st) <- deRefStablePtr stableptr
freeStablePtr stableptr
- (val, state') <- runW (fn conf) (conf, st)
+ (val, state') <- runW (fn $ ctxConfig ctx) (ctx, st)
poke ptr val
- newStablePtr (conf, state')
+ newStablePtr (ctx, state')
runForeignWithReturn2 :: (Storable a, Storable b) => (WConfig -> W (a, b)) -> Ptr a -> Ptr b -> Wetterhorn -> IO Wetterhorn
runForeignWithReturn2 fn ptrA ptrB stableptr = do
- (conf, st) <- deRefStablePtr stableptr
+ (ctx, st) <- deRefStablePtr stableptr
freeStablePtr stableptr
- ((vA, vB), state') <- runW (fn conf) (conf, st)
+ ((vA, vB), state') <- runW (fn $ ctxConfig ctx) (ctx, st)
poke ptrA vA
poke ptrB vB
- newStablePtr (conf, state')
+ newStablePtr (ctx, state')
-- | This function is the implementation of the "hotstart" mechanism. It gives a
-- pointer to the previously marshalled state and the length of that array and
diff --git a/src/harness_adapter.c b/src/harness_adapter.c
index aa45ce6..8585d7e 100644
--- a/src/harness_adapter.c
+++ b/src/harness_adapter.c
@@ -5,7 +5,7 @@
#include "HsFFI.h"
#include "plugin_interface.h"
-#include <stdio.h>
+#include <unistd.h>
const char *plugin_name = "Wetterhorn";
@@ -16,11 +16,18 @@ void* get_foreign_interface()
return foreign_interface;
}
-void plugin_load(int argc, char **argv, void* fintf) {
+void plugin_load(int argc, char **argv, foreign_interface_t* fintf) {
hs_init(&argc, &argv);
foreign_interface = fintf;
}
-void plugin_teardown(opqst_t st) { hs_exit(); }
+void plugin_teardown(opqst_t st) {
+ hs_exit();
+
+ // There's a race condition between when this shared library is unloaded and
+ // when the haskell runtime actually exits. Don't have a good solution for
+ // this at the moment, so just sleep for a second.
+ sleep(1);
+}
static const char msg[] =
"Wetterhorn Plugin v 0.01\n\n"