From 136b4d0ad50bec88e2601ccb5c9bb53028f8134d Mon Sep 17 00:00:00 2001 From: "Adam D. Ruppe" Date: Sun, 17 May 2020 10:20:36 -0400 Subject: [PATCH] new synthetic input on linux --- simpledisplay.d | 134 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 17 deletions(-) diff --git a/simpledisplay.d b/simpledisplay.d index 04a8948..65c0715 100644 --- a/simpledisplay.d +++ b/simpledisplay.d @@ -5185,8 +5185,6 @@ version(Windows) { } - - // global hotkey helper function /// Platform-specific for Windows. Registers a global hotkey. Returns a registration ID. @@ -5235,6 +5233,123 @@ version(Windows) { } } +version (X11) { + pragma(lib, "dl"); + import core.sys.posix.dlfcn; + + /++ + Allows for sending synthetic input to the X server via the Xtst + extension. + + Please remember user input is meant to be user - don't use this + if you have some other alternative! + + If you need this on Windows btw, the top-level [sendSyntheticInput] shows + the Win32 api to start it, but I only did basics there, PR welcome if you like, + it is an easy enough function to use. + + History: Added May 17, 2020. + +/ + struct SyntheticInput { + @disable this(); + + private void* lib; + private int* refcount; + + private extern(C) { + void function(Display*, uint keycode, bool press, arch_ulong delay) XTestFakeKeyEvent; + void function(Display*, uint button, bool press, arch_ulong delay) XTestFakeButtonEvent; + } + + /// The dummy param must be 0. + this(int dummy) { + lib = dlopen("libXtst.so", RTLD_NOW); + if(lib is null) + throw new Exception("cannot load xtest lib extension"); + scope(failure) + dlclose(lib); + + XTestFakeButtonEvent = cast(typeof(XTestFakeButtonEvent)) dlsym(lib, "XTestFakeButtonEvent"); + XTestFakeKeyEvent = cast(typeof(XTestFakeKeyEvent)) dlsym(lib, "XTestFakeKeyEvent"); + + if(XTestFakeKeyEvent is null) + throw new Exception("No XTestFakeKeyEvent"); + if(XTestFakeButtonEvent is null) + throw new Exception("No XTestFakeButtonEvent"); + + refcount = new int; + *refcount = 1; + } + + this(this) { + if(refcount) + *refcount += 1; + } + + ~this() { + if(refcount) { + *refcount -= 1; + if(*refcount == 0) + // I commented this because if I close the lib before + // XCloseDisplay, it is liable to segfault... so just + // gonna keep it loaded if it is loaded, no big deal + // anyway. + {} // dlclose(lib); + } + } + + /// This ONLY works with basic ascii! + void sendSyntheticInput(string s) { + int delay = 0; + foreach(ch; s) { + pressKey(cast(Key) ch, true, delay); + pressKey(cast(Key) ch, false, delay); + delay += 5; + } + } + + /// + void pressKey(Key key, bool pressed, int delay = 0) { + XTestFakeKeyEvent(XDisplayConnection.get, XKeysymToKeycode(XDisplayConnection.get, key), pressed, delay + pressed ? 0 : 5); + } + + /// + void pressMouseButton(MouseButton button, bool pressed, int delay = 0) { + int btn; + + switch(button) { + case MouseButton.left: btn = 1; break; + case MouseButton.middle: btn = 2; break; + case MouseButton.right: btn = 3; break; + case MouseButton.wheelUp: btn = 4; break; + case MouseButton.wheelDown: btn = 5; break; + case MouseButton.backButton: btn = 8; break; + case MouseButton.forwardButton: btn = 9; break; + default: + } + + assert(btn); + + XTestFakeButtonEvent(XDisplayConnection.get, btn, pressed, delay); + } + + /// + static void moveMouseArrowBy(int dx, int dy) { + auto disp = XDisplayConnection.get(); + XWarpPointer(disp, None, None, 0, 0, 0, 0, dx, dy); + XFlush(disp); + } + + /// + static void moveMouseArrowTo(int x, int y) { + auto disp = XDisplayConnection.get(); + auto root = RootWindow(disp, DefaultScreen(disp)); + XWarpPointer(disp, None, root, 0, 0, 0, 0, x, y); + XFlush(disp); + } + } +} + /++ @@ -14761,18 +14876,3 @@ class NotYetImplementedException : Exception { } private alias scriptable = arsd_jsvar_compatible; - -version (linux) -{ - void moveArrowBy(int x, int y) - { - XWarpPointer(XDisplayConnection.get(), None, None, 0, 0, 0, 0, x, y); - XFlush(XDisplayConnection.get()); - } - - void moveArrowTo(int x, int y) - { - moveArrowBy(-10000, -10000); - moveArrowBy(0, 0); - } -}