From 22571fc455f50d1774e7abb9a77db3a51182a420 Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Fri, 16 Feb 2024 15:41:49 -0700 Subject: Do most of keyboard handling in the plugin now. --- harness/include/plugin.h | 18 ++++++---- harness/src/wl.c | 59 +++++---------------------------- harness/tools/genbuild.pl | 27 ++++++++++----- harness/tools/genintf.pl | 31 ++++++++++++----- package.yaml | 1 + src/Wetterhorn/Core.hs | 39 ++++++++++++++++++---- src/Wetterhorn/Core/ForeignInterface.hs | 3 ++ src/Wetterhorn/FFI.hs | 42 +++++++++++++++++++---- 8 files changed, 132 insertions(+), 88 deletions(-) diff --git a/harness/include/plugin.h b/harness/include/plugin.h index bde8990..37e36ba 100644 --- a/harness/include/plugin.h +++ b/harness/include/plugin.h @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -18,6 +19,7 @@ #define EXPORT_INCLUDE(a) EXPORT_INCLUDE() +EXPORT_INCLUDE() #define MAX_QUEUED_ACTIONS 8 @@ -31,12 +33,12 @@ struct PLUGIN; /* This structure represents an action requested by the plugin for the harness. */ typedef struct { - int (*action)(struct PLUGIN *requester, void* arg); - void (*arg_dtor)(void* arg); + int (*action)(struct PLUGIN *requester, void *arg); + void (*arg_dtor)(void *arg); union { - void* ptr_arg; + void *ptr_arg; int int_arg; - char* str_arg; + char *str_arg; }; } requested_action_t; @@ -96,7 +98,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. * @@ -132,7 +134,9 @@ typedef struct PLUGIN { /* * Handles a keybinding. */ - EXPORT(opqst_t (*plugin_handle_keybinding)(uint32_t keysym, opqst_t state)); + EXPORT(opqst_t (*plugin_handle_keybinding)( + struct wlr_event_keyboard_key *event, uint32_t modifiers, uint32_t keysym, + int *out_handled, opqst_t state)); /* * Handles a surface being mapped, unmapped or destroyed. @@ -166,7 +170,7 @@ 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); +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, diff --git a/harness/src/wl.c b/harness/src/wl.c index d01797c..3f00e06 100644 --- a/harness/src/wl.c +++ b/harness/src/wl.c @@ -171,44 +171,6 @@ static void keyboard_handle_modifiers(struct wl_listener *listener, void *data) &keyboard->device->keyboard->modifiers); } -static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) -{ - plugin_call_update_state(server->plugin, plugin_handle_keybinding, sym); - /* - * Here we handle compositor keybindings. This is when the compositor is - * processing keys, rather than passing them on to the client for its own - * processing. - * - * This function assumes Alt is held down. - */ - dlhandle_t new_handle; - switch (sym) { - case XKB_KEY_Escape: - wl_display_terminate(server->wl_display); - break; - case XKB_KEY_F5: - plugin_hot_reload_same_state(&server->plugin); - break; - case XKB_KEY_F1: - /* Cycle to the next view */ - if (wl_list_length(&server->views) < 2) { - break; - } - struct tinywl_view *current_view = - wl_container_of(server->views.next, current_view, link); - struct tinywl_view *next_view = - wl_container_of(current_view->link.next, next_view, link); - focus_view(next_view, next_view->xdg_surface->surface); - /* Move the previous view to the end of the list */ - wl_list_remove(¤t_view->link); - wl_list_insert(server->views.prev, ¤t_view->link); - break; - default: - return false; - } - return true; -} - static void keyboard_handle_key(struct wl_listener *listener, void *data) { /* This event is raised when a key is pressed or released. */ @@ -223,17 +185,13 @@ static void keyboard_handle_key(struct wl_listener *listener, void *data) const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms); - - bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); - if ((modifiers & WLR_MODIFIER_ALT) && - event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { - /* If alt is held down and this button was _pressed_, we attempt to - * process it as a compositor keybinding. */ - for (int i = 0; i < nsyms; i++) { - handled = handle_keybinding(server, syms[i]); - } - } + /* Pass the information along to the plugin for the plugin to handle. The + * plugin will return via 'handled' whether or not the key event was handled + * or not. */ + int handled = 0; + plugin_call_update_state(server->plugin, plugin_handle_keybinding, event, + modifiers, syms[0], &handled); if (!handled) { /* Otherwise, we pass it along to the client. */ @@ -1043,8 +1001,9 @@ int main(int argc, char *argv[]) setenv("DISPLAY", xwayland->display_name, 1); } else { - fprintf(stderr, - "======= failed to setup XWayland X server. Continuing without it.\n"); + fprintf( + stderr, + "======= failed to setup XWayland X server. Continuing without it.\n"); } /* Set the WAYLAND_DISPLAY environment variable to our socket and run the diff --git a/harness/tools/genbuild.pl b/harness/tools/genbuild.pl index 7020e81..1acabc0 100644 --- a/harness/tools/genbuild.pl +++ b/harness/tools/genbuild.pl @@ -24,15 +24,24 @@ print " plug->state = NULL;\n"; print " plug->library_handle = dl;\n"; print "\n"; while (<>) { - if (/^\s*EXPORT\(\s*((?:\w|\s*\*\s*)+)\s*\(\*(\w+)\)\s*\((.*)\)\);/) { - print "\n"; - print " ptr = dlsym(dl, \"$2\");\n"; - print " if (!ptr) {\n"; - print " fprintf(stderr, \"Plugin missing %s\\n\", \"$2\");\n"; - print " ret |= 1;\n"; - print " }\n"; - print " plug->$2 = ptr;\n"; - $comment=""; + if (/^\s*EXPORT/) { + my $line = "$_"; + while (not ($line =~ /;$/)) { + my $nextline = ; + last unless defined $nextline; + + $line="$line$nextline"; + } + if ($line =~ /^\s*EXPORT\(\s*((?:\w|\s*\*\s*)+)\s*\(\*(\w+)\)\s*\((.*)\)\);/s) { + print "\n"; + print " ptr = dlsym(dl, \"$2\");\n"; + print " if (!ptr) {\n"; + print " fprintf(stderr, \"Plugin missing %s\\n\", \"$2\");\n"; + print " ret |= 1;\n"; + print " }\n"; + print " plug->$2 = ptr;\n"; + $comment=""; + } } } print "\n return ret;\n"; diff --git a/harness/tools/genintf.pl b/harness/tools/genintf.pl index 6f53951..d446d83 100644 --- a/harness/tools/genintf.pl +++ b/harness/tools/genintf.pl @@ -6,25 +6,38 @@ print "#ifndef _PLUG_INTF\n"; print "#define _PLUG_INTF\n"; print "\n#include \n"; print "\ntypedef void* opqst_t;\n"; -while (<>) { + +while () { if (/^\s*\/\*/) { $_ =~ s/^\s*//; $comment="$_"; + next; } if (/^\s*\*/) { $_ =~ s/^\s*/ /; $comment="$comment$_"; + next; } - 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"; + if (/^\s*EXPORT/) { + my $line = "$_"; + while (not ($line =~ /;$/)) { + my $nextline = ; + last unless defined $nextline; + + $line="$line$nextline"; + } + + if ($line =~ /^\s*EXPORT_INCLUDE\((.*)\)/s) { + print "#include $1\n"; + } elsif ($line =~ /^\s*EXPORT\(\s*((?:\w|\s*\*\s*)+)\s*\(\*(\w+)\)\s*\((.*)\)\);/s) { + print "$comment"; + print "$1 $2($3);\n\n"; + $comment=""; + } elsif ($line =~ /^\s*EXPORT\((.*)\);/s) { + print "$1\n"; + } } } print "#endif /* _PLUG_INTF */\n"; diff --git a/package.yaml b/package.yaml index b559bd7..ba0b9d3 100644 --- a/package.yaml +++ b/package.yaml @@ -32,6 +32,7 @@ dependencies: - base >= 4.7 && < 5 - mtl - bytestring +- containers ghc-options: - -Wall diff --git a/src/Wetterhorn/Core.hs b/src/Wetterhorn/Core.hs index 5a9d9e5..16978bb 100644 --- a/src/Wetterhorn/Core.hs +++ b/src/Wetterhorn/Core.hs @@ -16,6 +16,8 @@ module Wetterhorn.Core defaultConfig, requestHotReload, ctxConfig, + KeyEvent (..), + KeyState (..), ) where @@ -24,7 +26,10 @@ 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, castForeignPtr) +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe) +import Foreign (Ptr, StablePtr, Word32, castForeignPtr, newStablePtr, ptrToIntPtr) import Numeric (showHex) import Text.Printf import Wetterhorn.Core.ForeignInterface (ForeignInterface) @@ -66,20 +71,40 @@ data WState = WState data SurfaceState = Map | Unmap | Destroy deriving (Eq, Ord, Show, Enum) +data KeyState = KeyPressed | KeyReleased deriving (Show, Read, Eq, Enum, Ord) + +data KeyEvent = KeyEvent + { timeMs :: Word32, + keycode :: Word32, + state :: KeyState, + modifiers :: Word32, + keysym :: Word32 + } + deriving (Show, Read, Ord, Eq) + data WConfig = WConfig - { keybindingHandler :: Word32 -> W (), + { keybindingHandler :: KeyEvent -> W Bool, surfaceHandler :: SurfaceState -> Ptr () -> W () } +defaultBindings :: Map (KeyState, Word32, Word32) (W ()) +defaultBindings = + Map.fromList + [ ((KeyPressed, 0x8, 0x72), requestHotReload), + ((KeyPressed, 0x8, 0x6c), requestLog "This is a log statement!\n"), + ((KeyPressed, 0x8, 0x71), requestExit 0) + ] + defaultConfig :: WConfig defaultConfig = WConfig - { keybindingHandler = \sym -> do + { keybindingHandler = \keyEvent -> do i <- incrementState - 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") - when (sym == 0x71) (requestExit 0), + wio $ printf "%d - got %s\n" i (show keyEvent) + maybe (return False) (fmap (const True)) $ + Map.lookup + (state keyEvent, modifiers keyEvent, keysym keyEvent) + defaultBindings, 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 0b763b0..14720bb 100644 --- a/src/Wetterhorn/Core/ForeignInterface.hs +++ b/src/Wetterhorn/Core/ForeignInterface.hs @@ -1,6 +1,9 @@ module Wetterhorn.Core.ForeignInterface ( getForeignInterface, ForeignInterface (..), + ForeignDemarshal (..), + runForeignDemarshal, + demarshal, ) where diff --git a/src/Wetterhorn/FFI.hs b/src/Wetterhorn/FFI.hs index 3221903..969f86f 100644 --- a/src/Wetterhorn/FFI.hs +++ b/src/Wetterhorn/FFI.hs @@ -16,8 +16,10 @@ import Foreign mallocBytes, newStablePtr, ) -import Foreign.C (CChar) +import Foreign.C (CChar, CInt (..)) +import System.Posix.Types (CIno) import Wetterhorn.Core +import Wetterhorn.Core.ForeignInterface runForeign :: (WConfig -> W ()) -> Wetterhorn -> IO Wetterhorn runForeign fn stblptr = do @@ -83,26 +85,54 @@ pluginMarshalState stblptr outlen = do foreign export ccall "plugin_handle_keybinding" pluginHandleKeybinding :: - Word32 -> Wetterhorn -> IO Wetterhorn - -pluginHandleKeybinding :: Word32 -> Wetterhorn -> IO Wetterhorn -pluginHandleKeybinding sym = runForeign (`keybindingHandler` sym) + Ptr () -> + Word32 -> + Word32 -> + Ptr CInt -> + Wetterhorn -> + IO Wetterhorn + +pluginHandleKeybinding :: + Ptr () -> + Word32 -> + Word32 -> + Ptr CInt -> + Wetterhorn -> + IO Wetterhorn +pluginHandleKeybinding eventPtr mods sym = + runForeignWithReturn $ \config -> do + event <- wio $ + runForeignDemarshal eventPtr $ do + tMs <- demarshal + kc <- demarshal + _ <- (demarshal :: ForeignDemarshal Word32) + keyState <- demarshal + return $ + KeyEvent + tMs + kc + (if keyState == (0 :: Word8) then KeyReleased else KeyPressed) + mods + sym + (\b -> if b then 1 else 0) <$> keybindingHandler config event foreign export ccall "plugin_handle_surface_map" pluginHandleSurfaceMap :: Ptr () -> Wetterhorn -> IO Wetterhorn + pluginHandleSurfaceMap :: Ptr () -> Wetterhorn -> IO Wetterhorn pluginHandleSurfaceMap p = runForeign (\c -> surfaceHandler c Map p) foreign export ccall "plugin_handle_surface_unmap" pluginHandleSurfaceUnmap :: Ptr () -> Wetterhorn -> IO Wetterhorn + pluginHandleSurfaceUnmap :: Ptr () -> Wetterhorn -> IO Wetterhorn pluginHandleSurfaceUnmap p = runForeign (\c -> surfaceHandler c Unmap p) - foreign export ccall "plugin_handle_surface_destroy" pluginHandleSurfaceDestroy :: Ptr () -> Wetterhorn -> IO Wetterhorn + pluginHandleSurfaceDestroy :: Ptr () -> Wetterhorn -> IO Wetterhorn pluginHandleSurfaceDestroy p = runForeign (\c -> surfaceHandler c Destroy p) -- cgit