xhk-xhk-v1.2/0000755000175000001760000000000013516604415012464 5ustar kenhysdockerxhk-xhk-v1.2/doc/0000755000175000001760000000000013516604415013231 5ustar kenhysdockerxhk-xhk-v1.2/doc/xhk.md0000644000175000001760000000152213516604415014345 0ustar kenhysdocker% XHK(1) % Kieran Bingham % January 2019 # NAME xhk - HalfKey Xorg Driver Utility # SYNOPSIS **xhk** [**-m**] [**-d**] [**-h**] # DESCRIPTION **xHK** is a half keyboard mirroring implementation to help one handed typist. Running xhk detaches the keyboard, and intercepts all input; processing it for space bar pushes as it goes. The space bar acts as a modifier if it is held at the same time as a standard letter. Modifying acts to mirror the input such that key presses are mirrored down the middle of the home row. Pressing space, and then releasing will still provide a single space character. # OPTIONS **-d** : increase debug verbosity levels. **-h** : display a friendly help message. **-m** : mirror mode - All keys are changed to reversed keyboard layout. **-t** : X11 related keycode test. **-v** : show xHK version xhk-xhk-v1.2/doc/xhk.10000644000175000001760000000203113516604415014101 0ustar kenhysdocker.\" Automatically generated by Pandoc 2.2.1 .\" .TH "XHK" "1" "January 2019" "" "" .hy .SH NAME .PP xhk \- HalfKey Xorg Driver Utility .SH SYNOPSIS .PP \f[B]xhk\f[] [\f[B]\-m\f[]] [\f[B]\-d\f[]] [\f[B]\-h\f[]] .SH DESCRIPTION .PP \f[B]xHK\f[] is a half keyboard mirroring implementation to help one handed typist. .PP Running xhk detaches the keyboard, and intercepts all input; processing it for space bar pushes as it goes. The space bar acts as a modifier if it is held at the same time as a standard letter. Modifying acts to mirror the input such that key presses are mirrored down the middle of the home row. Pressing space, and then releasing will still provide a single space character. .SH OPTIONS .TP .B \f[B]\-d\f[] increase debug verbosity levels. .RS .RE .TP .B \f[B]\-h\f[] display a friendly help message. .RS .RE .TP .B \f[B]\-m\f[] mirror mode \- All keys are changed to reversed keyboard layout. .RS .RE .TP .B \f[B]\-t\f[] X11 related keycode test. .RS .RE .TP .B \f[B]\-v\f[] show xHK version .RS .RE .SH AUTHORS Kieran Bingham. xhk-xhk-v1.2/doc/Makefile0000644000175000001760000000007413516604415014672 0ustar kenhysdockerxhk.1: xhk.md pandoc xhk.md --standalone --to man -o xhk.1 xhk-xhk-v1.2/autobuild.sh0000755000175000001760000000011413516604415015007 0ustar kenhysdocker#!/bin/sh ./autogen.sh automake mkdir -p build cd build ../configure make xhk-xhk-v1.2/debian/0000755000175000001760000000000013516604415013706 5ustar kenhysdockerxhk-xhk-v1.2/README.md0000644000175000001760000000610013516604415013740 0ustar kenhysdocker[xHK] - An Xlib halfkeyboard implementation =========================================== Getting started --------------- To tryout xhk, run the following commands: git clone https://github.com/kbingham/xhk.git cd xhk sudo apt-get install build-essential autoconf automake pkg-config sudo apt-get install libx11-dev libxi-dev libxtst-dev ./autogen.sh ./configure make src/xhk -ddd # -ddd executes with the highest debug level to see it working :) In early 2014, I had an operation on my right elbow to remove some bone fragments. These were remaining from an accident in my teenage years - but had started to cause me some pain and grief. The operation went well - but has a 6 month full recovery time, and for the first 6 weeks my right arm was pretty much immobile. A serious blocker to my coding. There are it would seem some solutions to this. A few companies sell physical keyboards which provide one-handed typing functionality - but these are expensive. Regardless of the price, I have a keyboard already attached to my Laptop - and plugging in an external keyboard isn’t really feasible when sat on the sofa; So I need a better solution - one which uses my existing keyboard. Now - again - there are a few software implementations of half-keyboard / mirrors using different methods, but not one for linux: Alternative Software Solutions ------------------------------ - [http://www.onehandkeyboard.org/] has both a Mac and Windows solution - does link to some other linux solutions - but they are either expired or not appropriate. - [http://warped.org/] has a version utilising AutoHotkeys for Windows - [http://blog.xkcd.com/] provides an XKB mapping - but can only use the caps key as a modifier - and doesn’t meet my needs! My Solution ----------- I write C code. I use linux, it seemed only reasonable that as a version didn’t exist in this space I would create it and open-source it for all to use (and improve) You can get the sources from [GitHub] and build it yourself by following the instructions in the Getting Started section above. Once the application is running - it is processing all your keypresses and decides if it should mirror them. ‘Backspace’ is mirrored with the ‘Tab’ key, whilst ‘Enter’ is mirrored with ‘Caps-Lock’ If you use this - do drop me a mail to let me know how you get on, and if you find any problems I’ll look into them to fix them. Perhaps in the future I’ll pull together a GTK frontend for it, but it depends on how much use it gets. [http://www.onehandkeyboard.org/]: http://www.onehandkeyboard.org/download/ "MacOS/Windows software" [http://warped.org/]: http://warped.org/blog/2008/10/06/the-free-one-handed-keyboard/ "Autokeys implementation" [http://blog.xkcd.com/]: http://blog.xkcd.com/2007/08/14/mirrorboard-a-one-handed-keyboard-layout-for-the-lazy/ "XKCD MirrorBoard" [GitHub]: https://github.com/kbingham/xhk "xhk @ GitHub" This code is released under the GNU GPL v2 or later www.kieranbingham.co.uk xhk-xhk-v1.2/.travis.yml0000644000175000001760000000026313516604415014576 0ustar kenhysdockerlanguage: cpp os: - linux compiler: - gcc - clang before_install: - sudo apt-get update - sudo apt-get -y install libx11-dev libxi-dev libxtst-dev script: - make xhk-xhk-v1.2/src/0000755000175000001760000000000013516604415013253 5ustar kenhysdockerxhk-xhk-v1.2/src/xhk.c0000644000175000001760000005167413516604415014226 0ustar kenhysdocker/* XHalfKey. An Xorg/XLib HalfKeyboard interpreter driver. Copyright (C) 2014 Kieran Bingham This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include /* Go Real Time */ #include #include #include #include #include #define XK_MISCELLANY #define XK_XKB_KEYS #include #include // KeyCode mappings to Layout nomenclatures #include "xhk-layout.h" #ifndef VERSION #define VERSION "1.2" #endif static int verbose = 1; #define PRINT(level, x ...) if (verbose >= level) printf(x) #define ERROR(...) PRINT(0, ##__VA_ARGS__) #define REPORT(...) PRINT(1, ##__VA_ARGS__) #define INFO(...) PRINT(2, ##__VA_ARGS__) #define DEBUG(...) PRINT(3, ##__VA_ARGS__) #define VERBOSE(level, ...) PRINT(level, ##__VA_ARGS__) static bool ApplicationRunning = true; static bool MirrorMode = false; /* KeyStates == key_down / is_pressed */ #define KEYSTATE_DOWN 1 #define KEYSTATE_UP 0 static int keystates[255] = { 0 }; /* Initialised up */ static int ioErrorHandler(Display* d) { printf("ERROR: Closing Down\n"); ApplicationRunning = false; return 0; } typedef struct XWindowsScreen_s { Display* display; XIDeviceInfo keyboard; XKeyboardState KBState; XKeyboardControl KBControl; } XWindowsScreen_t; static XWindowsScreen_t LocalScreen; Display* OpenDisplay(const char* displayName) { Display* display; // get the DISPLAY if (displayName == NULL) { displayName = getenv("DISPLAY"); if (displayName == NULL) { displayName = ":0.0"; } } // open the display INFO("XOpenDisplay(\"%s\")\n", displayName); display = XOpenDisplay(displayName); if (display == NULL) { return NULL; } // verify the availability of the XTest extension { int majorOpcode, firstEvent, firstError; if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) { ERROR("XTEST extension not available"); XCloseDisplay(display); return NULL; } } DEBUG("XOpenDisplay(\"%s\") open as %p\n", displayName, display); return display; } int float_device(Display* dpy, int devid) { XIDetachSlaveInfo c; int ret; c.type = XIDetachSlave; c.deviceid = devid; INFO("Floating device ID %d\n", devid); ret = XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1); return ret; } int reattach_device(Display* dpy, int devid, int master) { XIAttachSlaveInfo c; int ret; c.type = XIAttachSlave; c.deviceid = devid; c.new_master = master; INFO("ReAttaching device ID %d to master %d\n", devid, master); ret = XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1); return ret; } static int ConfigureKeyboard(XWindowsScreen_t * screen) { int ret; int devid = screen->keyboard.deviceid; XGetKeyboardControl(screen->display, &screen->KBState); /* Select to receive all events from device N */ XIEventMask eventmask; unsigned char mask[1] = { 0 }; /* the actual mask */ eventmask.deviceid = devid; eventmask.mask_len = sizeof(mask); /* always in bytes */ eventmask.mask = mask; /* now set the mask */ XISetMask(mask, XI_KeyPress); XISetMask(mask, XI_KeyRelease); /* select on the window */ ret = XISelectEvents(screen->display, DefaultRootWindow(screen->display), &eventmask, 1); DEBUG("XISelectEvents returned %d which could be %s\n", ret, (ret == 0 ? "Ok" : ret == BadValue ? "BadValue" : ret == BadWindow ? "BadWindow" : "Unknown")); /* Detach the keyboard so that no one else receives input from this Keyboard */ float_device(screen->display, devid); return 0; } static XWindowsScreen_t * construct() { LocalScreen = (XWindowsScreen_t) { 0 }; // set the X I/O error handler so we catch the display disconnecting XSetIOErrorHandler(&ioErrorHandler); LocalScreen.display = OpenDisplay(NULL); if (LocalScreen.display == NULL) return NULL; return &LocalScreen; } int destruct(XWindowsScreen_t * screen) { if (screen->display) { // Please Sir, can I have my Keyboard back? /* Attachment describes what the device was attached to before we caused it to float */ reattach_device(screen->display, screen->keyboard.deviceid, screen->keyboard.attachment); XCloseDisplay(screen->display); } return 0; } char * keycode_to_char(XWindowsScreen_t * screen, int keycode) { KeySym ks = XkbKeycodeToKeysym(screen->display, keycode, 0, 0); return XKeysymToString(ks); } static int SendKey(XWindowsScreen_t * screen, int keycode, int key_down, unsigned long time) { DEBUG("Sending keycode %s %d, (%s) to XTest at %lu\n", key_down ? "Down" : "Up", keycode, keycode_to_char(screen, keycode), time); int ret = XTestFakeKeyEvent(screen->display, keycode, key_down ? true : false, CurrentTime); if (ret == 0) ERROR("XTestFakeKeyEvent failed to submit keycode %s %d at %lu\n", key_down ? "Down" : "Up", keycode, time); XFlush(screen->display); /* Record this action in our state table */ keystates[keycode] = key_down; return ret; } int mirror_key(int keycode) { /************************ * Half-Key heavily based on code by John Meacham * john@foo.net * Adapted for X Keycodes, special cases and extra rows. ***********************/ int t=0; if(keycode >= KEY_1 && keycode <= KEY_0) t = KEY_1; // numbers if(keycode >= KEY_Q && keycode <= KEY_P) t = KEY_Q; // top row if(keycode >= KEY_A && keycode <= KEY_SEMICOLON) t = KEY_A; // middle row if(keycode >= KEY_Z && keycode <= KEY_FSLASH) t = KEY_Z; // bottom row if(t) { int temp = keycode; temp -=t+4; if(temp < 1) temp--; temp = -temp; if(temp < 1) temp++; temp +=t+4; keycode = temp; } /* Swap special cases */ switch(keycode) { case KEY_BACKSPACE: keycode = KEY_TAB; break; case KEY_TAB: keycode = KEY_BACKSPACE; break; case KEY_ENTER: keycode = KEY_CAPS; break; case KEY_CAPS: keycode = KEY_ENTER; break; case KEY_MINUS: keycode = KEY_GRAVE; break; case KEY_GRAVE: keycode = KEY_MINUS; break; } return keycode; } #define SPACE_STATE_START 0 #define SPACE_STATE_PRESSED 1 #define SPACE_STATE_MODIFIED 2 char * SpaceStateNames[] = { "Start", "Pressed", "Modified", }; static int space = SPACE_STATE_START; int ProcessKeycode(XWindowsScreen_t * screen, int keycode, int up_flag) { int mirrored_key; bool mirrored; DEBUG("Entering ProcessKeycode in state %s\n", SpaceStateNames[space]); /* MirrorMode mirrors all keys before the state machine operates */ if (MirrorMode) keycode = mirror_key(keycode); /* * SPACE State Table * * START PRESSED MODIFIED * * SpaceDown Discarded Discarded Discarded * -> Pressed -> Pressed -> Modified * * SpaceUp Invalid? InjectSpace UpAllModifiedDowns? * -> Start -> Start -> Start * * MirroredKey InjectKey MirrorKey MirrorKey * -> Start -> Modified -> Modified * * OtherKey InjectKey InjectKey InjectKey * -> Start -> Pressed -> Modified */ if(keycode == KEY_SPACE) { switch(space) { case SPACE_STATE_START: space = SPACE_STATE_PRESSED; return -1; /* Change state but swallow the Space Input Event */ case SPACE_STATE_PRESSED: if(up_flag) { /* Space released before any other key */ /* We discarded the original Space Down event, so provide one now */ SendKey(screen, keycode, true, CurrentTime); space = SPACE_STATE_START; return keycode; /* Space bar released, allow it to be pressed */ } else return -1; /* Ignore and swallow repeated space down events */ break; case SPACE_STATE_MODIFIED: if(up_flag) space = SPACE_STATE_START; return -1; } } /* Mirror the key once to prevent excess checking */ mirrored_key = mirror_key(keycode); /* Determine if the key was modified by our mirror - Not all keys flip */ mirrored = (mirrored_key != keycode); /* Only change state if this action would mirror a key */ if( mirrored && (space != SPACE_STATE_START) ) { space = SPACE_STATE_MODIFIED; /* Space bar can no longer insert a space char */ keycode = mirrored_key; } /* Allow the user to 'cancel' a modifier without performing any further action */ if(keycode == KEY_ESC && space != SPACE_STATE_START) { space = SPACE_STATE_MODIFIED; return -1; } /* Verify that we are only releasing keys that we pressed */ if (up_flag && keystates[keycode] == KEYSTATE_UP) { /* Perhaps this was the wrong key */ int mirror = mirror_key(keycode); if (keystates[mirror] == KEYSTATE_DOWN) { DEBUG("StateInversion: Releasing key %s instead of %s\n", keycode_to_char(screen, mirror), keycode_to_char(screen, keycode)); keycode = mirror; /* We will 'up' this key instead */ /* because of the inversion, we take the SPACE state back a level */ if (space == SPACE_STATE_MODIFIED) space = SPACE_STATE_PRESSED; } } return keycode; } static int handle_key_release(XWindowsScreen_t * screen, XIDeviceEvent *event) { int keycode = event->detail; keycode = ProcessKeycode(screen, keycode, 1); INFO("Keyrelease %d (%s), keycode = %d (%s) time=%ld\n", event->detail, keycode_to_char(screen, event->detail), keycode, keycode_to_char(screen, keycode), event->time); if (keycode < 0) return -1; SendKey(screen, keycode, false, event->time); INFO("\n"); return 1; } static int handle_key_press(XWindowsScreen_t * screen, XIDeviceEvent *event) { int keycode = event->detail; if ( (event->flags & XIKeyRepeat) && (keycode == KEY_SPACE) ) // Ignore SPACE key repeats return -1; keycode = ProcessKeycode(screen, keycode, 0); INFO("Keypress %d (%s), keycode = %d (%s) time=%ld\n", event->detail, keycode_to_char(screen, event->detail), keycode, keycode_to_char(screen, keycode), event->time); if (keycode < 0) return -1; SendKey(screen, keycode, true, event->time); INFO("\n"); return 1; } static int process_event(XWindowsScreen_t * screen) { XEvent ev; Window current_window; int revert; XNextEvent(screen->display, &ev); XGetInputFocus(screen->display, ¤t_window, &revert); if (ev.xcookie.type == GenericEvent && // ev.xcookie.extension == opcode && XGetEventData(screen->display, &ev.xcookie)) { switch(ev.xcookie.evtype) { case XI_KeyPress: handle_key_press(screen, ev.xcookie.data); break; case XI_KeyRelease: handle_key_release(screen, ev.xcookie.data); break; default: INFO("Unhandled Event Received of type %d\n", ev.xcookie.type); break; } XFreeEventData(screen->display, &ev.xcookie); } return 0; } static int enumerate_keyboards(XWindowsScreen_t * screen) { int ndevices; XIDeviceInfo *devices, *device; devices = XIQueryDevice(screen->display, XIAllDevices, &ndevices); for (int i = 0; i < ndevices; i++) { device = &devices[i]; /* We're only interested in Keyboards */ if (device->use != XISlaveKeyboard) continue; printf("Device %s (id: %d) is a ", device->name, device->deviceid); switch(device->use) { case XIMasterPointer: printf("master pointer\n"); break; case XIMasterKeyboard: printf("master keyboard\n"); break; case XISlavePointer: printf("slave pointer\n"); break; case XISlaveKeyboard: printf("slave keyboard\n"); break; case XIFloatingSlave: printf("floating slave\n"); break; } printf("Device is attached to/paired with %d\n", device->attachment); } XIFreeDeviceInfo(devices); return 0; } static int identify_local_keyboard(XWindowsScreen_t * screen) { int keyboard_id = -1; int ndevices; XIDeviceInfo *devices, *device; devices = XIQueryDevice(screen->display, XIAllDevices, &ndevices); for (int i = 0; i < ndevices; i++) { device = &devices[i]; /* We're only interested in Keyboards */ if (device->use != XISlaveKeyboard) continue; /* We're only interested in keyboards which are presented as keyboards too! */ if (strcasestr(device->name, "keyboard") == 0 ) continue; /* And I'm afraid we aren't going to deal with anything which isn't real... */ if (strcasestr(device->name, "virtual") != 0 ) continue; /* However, If we have come this far, we likely have a keyboard! */ keyboard_id = device->deviceid; screen->keyboard = *device; /* Copy the device structure for destruction */ } XIFreeDeviceInfo(devices); return keyboard_id; } int xlib_halfkey(void) { XWindowsScreen_t * screen = construct(); if (screen->display == NULL) { ERROR("Couldn't connect to XServer\n"); return -1; } /* Which version of XI2? We support 2.0 */ int major = 2, minor = 0; if (XIQueryVersion(screen->display, &major, &minor) == BadRequest) { printf("XI2 not available. Server supports %d.%d\n", major, minor); return -1; } INFO("XI Version %d.%d\n", major, minor); enumerate_keyboards(screen); if (identify_local_keyboard(screen) < 0) { destruct(screen); return -1; } ConfigureKeyboard(screen); fflush(stdout); DEBUG("Entering Event Loop...\n"); // Loop until exit receiving and responding to events... while (ApplicationRunning) process_event(screen); destruct(screen); return 0; } #define BOLD "\033[1m" #define NORMAL "\033[0m" void version(void) { printf( "xhk, version %s\n", VERSION); printf( "Copyright (C) 2014 Kieran Bingham.\n" ); printf( "License GPLv3+: GNU GPL version 3 or later \n" "\n" "This is free software; you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" ); } void usage(void) { printf("%s", BOLD); printf("\tusage:\n"); printf("\t\t-m mirror mode - all keys reversed\n"); printf("\t\t-d increase debug verbosity levels\n"); printf("\t\t-h this help\n"); printf("%s", NORMAL); } static void handle_signal(int signum) { ERROR("Received Signal %d\n", signum); switch (signum) { case SIGTERM: /* 15 */ ERROR("Really want me to die huh?\n"); /* Fall Through */ default: if (LocalScreen.display) reattach_device(LocalScreen.display, LocalScreen.keyboard.deviceid, LocalScreen.keyboard.attachment); ApplicationRunning = false; break; } } void install_signal_handlers(void) { signal(SIGTERM, handle_signal); signal(SIGQUIT, handle_signal); signal(SIGINT, handle_signal); } int KeycodeTest(XWindowsScreen_t * screen, int keycode, int up_flag, int expected, int expected_state) { int returned_code = ProcessKeycode(screen, keycode, up_flag); if (returned_code != expected) ERROR("ProcessKeyCode for %d (%s) returned %d {%s} but expected %d {%s}\n", keycode, up_flag ? "Up" : "Down", returned_code, keycode_to_char(screen, returned_code), expected, keycode_to_char(screen, expected)); if (space != expected_state) ERROR("ProcessKeyCode for %d (%s %s) returned in state %d {%s} but expected state %d {%s}\n", keycode, keycode_to_char(screen, keycode), up_flag ? "Up" : "Down", space, SpaceStateNames[space], expected_state, SpaceStateNames[expected_state]); return (returned_code != expected); } #define UPFLAG_KEYDOWN 0 #define UPFLAG_KEYUP 1 int xlib_test(void) { int errors = 0; XWindowsScreen_t * screen = construct(); if (screen->display == NULL) { ERROR("Couldn't connect to XServer\n"); return -1; } /* Which version of XI2? We support 2.0 */ int major = 2, minor = 0; if (XIQueryVersion(screen->display, &major, &minor) == BadRequest) { printf("XI2 not available. Server supports %d.%d\n", major, minor); return -1; } INFO("XI Version %d.%d\n", major, minor); fflush(stdout); INFO("Entering Test Loop...\n"); DEBUG("Check a key returns as expected\n"); errors += KeycodeTest(screen, KEY_0, UPFLAG_KEYDOWN, KEY_0, SPACE_STATE_START); errors += KeycodeTest(screen, KEY_0, UPFLAG_KEYUP, KEY_0, SPACE_STATE_START); DEBUG("\nCheck space works alone\n"); errors += KeycodeTest(screen, KEY_SPACE, UPFLAG_KEYDOWN, -1, SPACE_STATE_PRESSED); errors += KeycodeTest(screen, KEY_SPACE, UPFLAG_KEYUP, KEY_SPACE, SPACE_STATE_START); /* If we expect a space - it gets sent by the state machine, so we have to cancel it out */ SendKey(screen, KEY_SPACE, KEYSTATE_UP, CurrentTime); DEBUG("\nVerify a key gets mirrored\n"); errors += KeycodeTest(screen, KEY_SPACE, UPFLAG_KEYDOWN, -1, SPACE_STATE_PRESSED); errors += KeycodeTest(screen, KEY_F, UPFLAG_KEYDOWN, KEY_J, SPACE_STATE_MODIFIED); errors += KeycodeTest(screen, KEY_F, UPFLAG_KEYUP, KEY_J, SPACE_STATE_MODIFIED); errors += KeycodeTest(screen, KEY_SPACE, UPFLAG_KEYUP, -1, SPACE_STATE_START); /* If we expect a space - it gets sent by the state machine, so we have to cancel it out */ SendKey(screen, KEY_SPACE, KEYSTATE_UP, CurrentTime); DEBUG("\nVerify a non-mirrored key doesn't break the space bar\n"); errors += KeycodeTest(screen, KEY_SPACE, UPFLAG_KEYDOWN, -1, SPACE_STATE_PRESSED); errors += KeycodeTest(screen, KEY_LSHIFT, UPFLAG_KEYDOWN, KEY_LSHIFT, SPACE_STATE_PRESSED); errors += KeycodeTest(screen, KEY_LSHIFT, UPFLAG_KEYUP, KEY_LSHIFT, SPACE_STATE_PRESSED); errors += KeycodeTest(screen, KEY_SPACE, UPFLAG_KEYUP, KEY_SPACE, SPACE_STATE_START); DEBUG("\nVerify pressing paired mirror keys sequentially still works\n"); errors += KeycodeTest(screen, KEY_F, UPFLAG_KEYDOWN, KEY_F, SPACE_STATE_START); errors += KeycodeTest(screen, KEY_J, UPFLAG_KEYDOWN, KEY_J, SPACE_STATE_START); errors += KeycodeTest(screen, KEY_F, UPFLAG_KEYUP, KEY_F, SPACE_STATE_START); errors += KeycodeTest(screen, KEY_J, UPFLAG_KEYUP, KEY_J, SPACE_STATE_START); /* Try the other way too */ errors += KeycodeTest(screen, KEY_R, UPFLAG_KEYDOWN, KEY_R, SPACE_STATE_START); errors += KeycodeTest(screen, KEY_U, UPFLAG_KEYDOWN, KEY_U, SPACE_STATE_START); errors += KeycodeTest(screen, KEY_U, UPFLAG_KEYUP, KEY_U, SPACE_STATE_START); errors += KeycodeTest(screen, KEY_R, UPFLAG_KEYUP, KEY_R, SPACE_STATE_START); INFO("\nExiting Test Loop with %d errors...\n", errors); XCloseDisplay(screen->display); return 0; } int main(int argc, char **argv) { char opt; REPORT("\n-- HalfKey Xorg Driver Utility %s --\n", VERSION); while((opt = getopt(argc, argv, "dvhmt")) != -1) switch(opt) { case 'd': verbose++; break; case 'v': version(); exit(0); case 'h': //print help page usage(); exit(0); case 'm': MirrorMode = true; break; case 't': xlib_test(); exit(0); break; default: usage(); exit(1); } install_signal_handlers(); int ret = setpriority(PRIO_PROCESS, getpid(), -20); if (ret) ERROR("SetPriority call failed : %d\n", ret); INFO("Process Priority set at %d\n", getpriority(PRIO_PROCESS, getpid())); xlib_halfkey(); REPORT("\n-- Terminating --\n"); return 0; } xhk-xhk-v1.2/src/Makefile.am0000644000175000001760000000021313516604415015303 0ustar kenhysdockerbin_PROGRAMS = xhk xhk_SOURCES = xhk.c xhk_LDADD = @X11_LIBS@ @XI_LIBS@ @XTST_LIBS@ xhk_CPPFLAGS = @X11_CFLAGS@ @XI_CFLAGS@ @XTST_CFLAGS@xhk-xhk-v1.2/src/xhk-layout.h0000644000175000001760000000321213516604415015527 0ustar kenhysdocker/* XHalfKey. An Xorg/XLib HalfKeyboard interpreter driver. Copyright (C) 2014 Kieran Bingham This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef XHK_LAYOUT_H_ #define XHK_LAYOUT_H_ enum EN_GB_LAYOUT { KEY_ESC = 9, KEY_1 = 10, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS = 20, KEY_BACKSPACE = 22, KEY_TAB = 23, KEY_Q = 24, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_ENTER = 36, KEY_CTRL = 37, KEY_A = 38, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_AT, KEY_HASH, KEY_GRAVE = 49, KEY_LSHIFT = 50, KEY_BSLASH = 51, KEY_Z = 52, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, KEY_STOP, KEY_FSLASH, KEY_RSHIFT, KEY_SPACE = 65, KEY_CAPS = 66, }; #endif /* XHK_LAYOUT_H_ */ xhk-xhk-v1.2/Makefile.am0000644000175000001760000000004513516604415014517 0ustar kenhysdockerSUBDIRS = src dist_doc_DATA = README xhk-xhk-v1.2/README0000644000175000001760000000033413516604415013344 0ustar kenhysdockerXhk: XLib Half Keyboard implementation Created from necessity following an operation on my elbow leaving me one handed for a period of time. This code is released under the GNU GPL v2 or later www.kieranbingham.co.uk xhk-xhk-v1.2/.gitignore0000644000175000001760000000034313516604415014454 0ustar kenhysdocker # Build Entities *.o *.a *.la # http://www.gnu.org/software/automake Makefile.in # http://www.gnu.org/software/autoconf /autom4te.cache /aclocal.m4 /compile /configure /depcomp /install-sh /missing /stamp-h1 /config.h.in xhk-xhk-v1.2/configure.ac0000644000175000001760000000050713516604415014754 0ustar kenhysdockerAC_INIT([xhk], [1.2], [kieranbingham@gmail.com]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_PROG_CC_C99 AM_PROG_CC_C_O AC_CONFIG_HEADERS([config.h]) PKG_CHECK_MODULES([X11], [x11 >= 1.6]) PKG_CHECK_MODULES([XI], [xi >= 1.6]) PKG_CHECK_MODULES([XTST], [xtst >= 1]) AC_CONFIG_FILES([ Makefile src/Makefile ]) AC_OUTPUT xhk-xhk-v1.2/autogen.sh0000755000175000001760000000006213516604415014463 0ustar kenhysdocker#!/bin/sh autoreconf --force --install || exit 1 xhk-xhk-v1.2/restyle.sh0000755000175000001760000000005313516604415014510 0ustar kenhysdocker#!/bin/sh astyle --style=linux src/*.[ch] xhk-xhk-v1.2/Makefile0000644000175000001760000000276713516604415014140 0ustar kenhysdocker## ## Autotools build wrapper. ## Kieran Bingham ## August 2014 ## ## Configures and builds under build- ## ## Cross compilation can be supported by defining ## ARCH= ## CROSS= ## ## ## Some very useful advice used here from http://mad-scientist.net/make/multi-arch.html ## # Disable all implicit build targets .SUFFIXES: #ARCH?=armv7 ARCH?=$(shell uname -m) BUILD_DIR=build-$(ARCH) SOURCE_DIR=$(CURDIR) export CC=${CROSS}gcc export LD=${CROSS}gcc export CXX=${CROSS}c++ export ARCH export PATH export DESTDIR # No make directory output from this wrapper MAKE:=$(MAKE) --no-print-directory .PHONY: all all: $(BUILD_DIR)/Makefile +@echo " [$(BUILD_DIR)] $(MAKECMDGOALS)" +@$(MAKE) --no-print-directory -C $(BUILD_DIR) $(MAKECMDGOALS) $(BUILD_DIR): @echo " [$(BUILD_DIR)] MKDIR" @mkdir -p $@ $(SOURCE_DIR)/configure: @echo " [$(BUILD_DIR)] Autoconf" cd $(SOURCE_DIR) && autoreconf --verbose --force --install $(BUILD_DIR)/Makefile: $(SOURCE_DIR)/configure | $(BUILD_DIR) @echo " [$(BUILD_DIR)] Configure" cd $(BUILD_DIR) && \ $(SOURCE_DIR)/configure \ --prefix=/usr \ --host=$(CROSS)linux \ --build=$(shell uname -p)-linux \ --with-sysroot \ # Prevent Make from sending these targets through the wrapper CMDGOALS Makefile : ; %.mk :: ; # Send all other targets through the default target % :: all ; deb: @echo " [$(BUILD_DIR)] Packaging" debuild -us -uc distclean: @echo " [$(BUILD_DIR)] $@" @rm -rf $(BUILD_DIR) @git clean -fxd