xhk-xhk-v1.2/ 0000755 0001750 0000176 00000000000 13516604415 012464 5 ustar kenhys docker xhk-xhk-v1.2/doc/ 0000755 0001750 0000176 00000000000 13516604415 013231 5 ustar kenhys docker xhk-xhk-v1.2/doc/xhk.md 0000644 0001750 0000176 00000001522 13516604415 014345 0 ustar kenhys docker % 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.1 0000644 0001750 0000176 00000002031 13516604415 014101 0 ustar kenhys docker .\" 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/Makefile 0000644 0001750 0000176 00000000074 13516604415 014672 0 ustar kenhys docker xhk.1: xhk.md
pandoc xhk.md --standalone --to man -o xhk.1
xhk-xhk-v1.2/autobuild.sh 0000755 0001750 0000176 00000000114 13516604415 015007 0 ustar kenhys docker #!/bin/sh
./autogen.sh
automake
mkdir -p build
cd build
../configure
make
xhk-xhk-v1.2/debian/ 0000755 0001750 0000176 00000000000 13516604415 013706 5 ustar kenhys docker xhk-xhk-v1.2/README.md 0000644 0001750 0000176 00000006100 13516604415 013740 0 ustar kenhys docker [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.yml 0000644 0001750 0000176 00000000263 13516604415 014576 0 ustar kenhys docker language: 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/ 0000755 0001750 0000176 00000000000 13516604415 013253 5 ustar kenhys docker xhk-xhk-v1.2/src/xhk.c 0000644 0001750 0000176 00000051674 13516604415 014226 0 ustar kenhys docker /*
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.am 0000644 0001750 0000176 00000000213 13516604415 015303 0 ustar kenhys docker bin_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.h 0000644 0001750 0000176 00000003212 13516604415 015527 0 ustar kenhys docker /*
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.am 0000644 0001750 0000176 00000000045 13516604415 014517 0 ustar kenhys docker SUBDIRS = src
dist_doc_DATA = README
xhk-xhk-v1.2/README 0000644 0001750 0000176 00000000334 13516604415 013344 0 ustar kenhys docker Xhk: 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/.gitignore 0000644 0001750 0000176 00000000343 13516604415 014454 0 ustar kenhys docker
# 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.ac 0000644 0001750 0000176 00000000507 13516604415 014754 0 ustar kenhys docker AC_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.sh 0000755 0001750 0000176 00000000062 13516604415 014463 0 ustar kenhys docker #!/bin/sh
autoreconf --force --install || exit 1
xhk-xhk-v1.2/restyle.sh 0000755 0001750 0000176 00000000053 13516604415 014510 0 ustar kenhys docker #!/bin/sh
astyle --style=linux src/*.[ch]
xhk-xhk-v1.2/Makefile 0000644 0001750 0000176 00000002767 13516604415 014140 0 ustar kenhys docker ##
## 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