svkbd

simple virtual keyboard
git clone git://git.suckless.org/svkbd
Log | Files | Refs | README | LICENSE

commit 4dab556580d2249e3f1597219b9c65bf8660f5e8
parent 99935775afce355bd16964b9a1b96ec35247292a
Author: Maarten van Gompel <proycon@anaproy.nl>
Date:   Sun,  2 Aug 2020 15:46:16 +0200

Added a dialer/numpad keyboard, added the ability to handle layouts with less keys/different layouts. Extra configurability: select layout on startup, customisable height factor.

Diffstat:
MREADME | 7+++++++
Mconfig.def.h | 1+
Mlayout.sxmo.h | 39+++++++++++++++++++++++++++++++++++++--
Msvkbd.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
4 files changed, 125 insertions(+), 29 deletions(-)

diff --git a/README b/README @@ -53,6 +53,13 @@ overlay functionality with the ``-O`` flag or by setting the environment variabl also a key on the function layer of the keyboard itself to enable/disable this behaviour on the fly. Its label shows ``≅`` when the overlay functionality is enabled and ``≇`` when not. +Notes +--------- + +This virtual keyboard does not actually modify the X keyboard layout, it simply relies on a standard US QWERTY layout +(setxkbmap us) being activated. If you use another XKB layout you will get unpredictable output that does not match the +labels on the virtual keycaps. + Repository ---------- diff --git a/config.def.h b/config.def.h @@ -1,6 +1,7 @@ static const Bool wmborder = True; static int fontsize = 20; static double overlay_delay = 1.0; +static int heightfactor = 16; //one row of keys takes up 1/x of the screen height static const char *fonts[] = { "DejaVu Sans:bold:size=20" }; diff --git a/layout.sxmo.h b/layout.sxmo.h @@ -68,7 +68,7 @@ static Key overlay[OVERLAYS] = { { "æ", XK_ae }, { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ //-- - { 0, XK_e }, //Overlay for e + { 0, XK_e }, //Overlay for e (first item after boundary defines the trigger) //--- { "è", XK_egrave }, { "é", XK_eacute }, @@ -465,11 +465,45 @@ static Key keys_ru[KEYS] = { { "↲ Enter", XK_Return, 2 }, }; -#define LAYERS 4 +static Key keys_dialer[KEYS] = { + { "Esc", XK_Escape, 1 }, + { "1!", XK_1, 1 }, + { "2@", XK_2, 1 }, + { "3#", XK_3, 1 }, + { "⌫Bksp", XK_BackSpace, 2 }, + { 0 }, /* New row */ + + { "Shift", XK_Shift_L, 1 }, + { "4$", XK_4, 1 }, + { "5%", XK_5, 1 }, + { "6^", XK_6, 1 }, + { "-_", XK_minus, 1 }, + { ",<", XK_comma, 1 }, + { 0 }, /* New row */ + + { "abc", XK_Mode_switch, 1 }, + { "7&", XK_7, 1 }, + { "8*", XK_8, 1 }, + { "9(", XK_9, 1 }, + { "=+", XK_equal, 1 }, + { "/?", XK_slash, 1 }, + { 0 }, /* New row */ + + { "↺", XK_Cancel, 1}, + { "", XK_space, 1 }, + { "0)", XK_0, 1 }, + { ".>", XK_period, 1 }, + { "↲ Enter", XK_Return, 2}, + { 0 }, /* New row */ + { 0 }, /* Last item (double 0) */ +}; + +#define LAYERS 5 static char* layer_names[LAYERS] = { "en", "symbols", "functions", + "dialer", "ru", }; @@ -477,6 +511,7 @@ static Key* available_layers[LAYERS] = { keys_en, keys_symbols, keys_functions, + keys_dialer, keys_ru }; diff --git a/svkbd.c b/svkbd.c @@ -63,6 +63,7 @@ static void buttonrelease(XEvent *e); static void cleanup(void); static void configurenotify(XEvent *e); static void countrows(); +static int countkeys(Key *k); static void drawkeyboard(void); static void drawkey(Key *k); static void expose(XEvent *e); @@ -77,6 +78,7 @@ static void simulate_keyrelease(KeySym keysym); static void showoverlay(int idx); static void hideoverlay(); static void cyclelayer(); +static void setlayer(); static void togglelayer(); static void unpress(Key *k, KeySym mod); static void updatekeys(); @@ -109,6 +111,7 @@ static int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0; static char *name = "svkbd"; static int debug = 0; static int numlayers = 0; +static int numkeys = 0; static KeySym ispressingkeysym; @@ -130,7 +133,7 @@ motionnotify(XEvent *e) XPointerMovedEvent *ev = &e->xmotion; int i; - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].keysym && ev->x > keys[i].x && ev->x < keys[i].x + keys[i].w && ev->y > keys[i].y @@ -221,7 +224,7 @@ cleanup(void) { } } if (debug) { printf("Cleanup: simulating key release\n"); fflush(stdout); } - for (i = 0; i < LENGTH(keys); i++) { + for (i = 0; i < numkeys; i++) { XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0); } } @@ -253,18 +256,34 @@ void countrows() { int i = 0; - for(i = 0, rows = 1; i < LENGTH(keys); i++) { + for(i = 0, rows = 1; i < numkeys; i++) { if(keys[i].keysym == 0) rows++; } } +int +countkeys(Key * layer) { + int keys = 0; + int i; + + for(i = 0; i < KEYS; i++) { + if (i > 0 && layer[i].keysym == 0 && layer[i-1].keysym == 0) { + keys--; + break; + } + keys++; + } + + return keys; +} + void drawkeyboard(void) { int i; - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].keysym != 0) drawkey(&keys[i]); } @@ -315,7 +334,7 @@ Key * findkey(int x, int y) { int i; - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].keysym && x > keys[i].x && x < keys[i].x + keys[i].w && y > keys[i].y && y < keys[i].y + keys[i].h) { @@ -375,7 +394,7 @@ press(Key *k, KeySym mod) { } } else { if (debug) { printf("Simulating press: %ld\n", k->keysym); fflush(stdout); } - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].pressed && IsModifierKey(keys[i].keysym)) { simulate_keypress(keys[i].keysym); } @@ -386,7 +405,7 @@ press(Key *k, KeySym mod) { } simulate_keypress(k->keysym); - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].pressed && IsModifierKey(keys[i].keysym)) { simulate_keyrelease(keys[i].keysym); } @@ -457,7 +476,7 @@ unpress(Key *k, KeySym mod) { if (get_press_duration() < overlay_delay) { if (debug) { printf("Delayed simulation of press after release: %ld\n", k->keysym); fflush(stdout); } //simulate the press event, as we postponed it earlier in press() - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].pressed && IsModifierKey(keys[i].keysym)) { simulate_keypress(keys[i].keysym); } @@ -484,7 +503,7 @@ unpress(Key *k, KeySym mod) { } - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) { simulate_keyrelease(keys[i].keysym); keys[i].pressed = 0; @@ -492,13 +511,13 @@ unpress(Key *k, KeySym mod) { break; } } - if(i != LENGTH(keys)) { + if(i != numkeys) { if(pressedmod) { simulate_keyrelease(mod); } pressedmod = 0; - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].pressed) { simulate_keyrelease(keys[i].keysym); keys[i].pressed = 0; @@ -634,7 +653,7 @@ setup(void) { if(!ww) ww = sw; if(!wh) - wh = sh * rows / 32; + wh = sh * rows / heightfactor; if(!wx) wx = 0; @@ -645,7 +664,7 @@ setup(void) { if(wy < 0) wy = sh + wy - wh; - for(i = 0; i < LENGTH(keys); i++) + for(i = 0; i < numkeys; i++) keys[i].pressed = 0; wa.override_redirect = !wmborder; @@ -702,10 +721,10 @@ updatekeys() { int x = 0, y = 0, h, base, r = rows; h = (wh - 1) / rows; - for(i = 0; i < LENGTH(keys); i++, r--) { - for(j = i, base = 0; j < LENGTH(keys) && keys[j].keysym != 0; j++) + for(i = 0; i < numkeys; i++, r--) { + for(j = i, base = 0; j < numkeys && keys[j].keysym != 0; j++) base += keys[j].width; - for(x = 0; i < LENGTH(keys) && keys[i].keysym != 0; i++) { + for(x = 0; i < numkeys && keys[i].keysym != 0; i++) { keys[i].x = x; keys[i].y = y; keys[i].w = keys[i].width * (ww - 1) / base; @@ -720,23 +739,30 @@ updatekeys() { void usage(char *argv0) { - fprintf(stderr, "usage: %s [-hdvDOl] [-g geometry] [-fn font]\n", argv0); + fprintf(stderr, "usage: %s [-hdvDO] [-g geometry] [-fn font] [-l layers] [-s initial_layer]\n", argv0); fprintf(stderr, "Options:\n"); fprintf(stderr, " -d - Set Dock Window Type\n"); fprintf(stderr, " -D - Enable debug\n"); fprintf(stderr, " -O - Disable overlays\n"); fprintf(stderr, " -l - Comma separated list of layers to enable\n"); + fprintf(stderr, " -s - Layer to select on program start\n"); + fprintf(stderr, " -H [int] - Height fraction, one key row takes 1/x of the screen height"); fprintf(stderr, " -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n"); exit(1); } +void setlayer() { + numkeys = countkeys(layers[currentlayer]); + memcpy(&keys, layers[currentlayer], sizeof(Key) * numkeys); +} + void cyclelayer() { currentlayer++; if (currentlayer >= numlayers) currentlayer = 0; if (debug) { printf("Cycling to layer %d\n", currentlayer); fflush(stdout); } - memcpy(&keys, layers[currentlayer], sizeof(keys_en)); + setlayer(); updatekeys(); drawkeyboard(); } @@ -749,7 +775,7 @@ togglelayer() { currentlayer = 1; } if (debug) { printf("Toggling layer %d\n", currentlayer); fflush(stdout); } - memcpy(&keys, layers[currentlayer], sizeof(keys_en)); + setlayer(); updatekeys(); drawkeyboard(); } @@ -760,7 +786,7 @@ showoverlay(int idx) { if (debug) { printf("Showing overlay %d\n", idx); fflush(stdout); } int i,j; //unpress existing key (visually only) - for(i = 0; i < LENGTH(keys); i++) { + for(i = 0; i < numkeys; i++) { if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) { keys[i].pressed = 0; drawkey(&keys[i]); @@ -804,21 +830,32 @@ sigterm(int sig) void -init_layers(char * layer_names_list) { +init_layers(char * layer_names_list, const char * initial_layer_name) { + int j; if (layer_names_list == NULL) { numlayers = LAYERS; memcpy(&layers, &available_layers, sizeof(available_layers)); + if (initial_layer_name != NULL) { + for (j = 0; j < LAYERS; j++) { + if (strcmp(layer_names[j], initial_layer_name) == 0) { + currentlayer = j; + break; + } + } + } } else { char * s; - int j; s = strtok(layer_names_list, ","); while (s != NULL) { if (numlayers+1 > LAYERS) die("too many layers specified"); int found = 0; for (j = 0; j < LAYERS; j++) { if (strcmp(layer_names[j], s) == 0) { + fprintf(stderr, "Adding layer %s\n", s); layers[numlayers] = available_layers[j]; - printf("Adding layer %s\n", s); + if (initial_layer_name != NULL && strcmp(layer_names[j], initial_layer_name) == 0) { + currentlayer = numlayers; + } found = 1; break; } @@ -831,17 +868,19 @@ init_layers(char * layer_names_list) { s = strtok(NULL,","); } } + setlayer(); } int main(int argc, char *argv[]) { int i, xr, yr, bitm; unsigned int wr, hr; + char * initial_layer_name = NULL; char * layer_names_list = NULL; - memcpy(&keys, &keys_en, sizeof(keys_en)); signal(SIGTERM, sigterm); + //parse environment variables const char* enableoverlays_env = getenv("SVKBD_ENABLEOVERLAYS"); if (enableoverlays_env != NULL) enableoverlays = atoi(enableoverlays_env); const char* layers_env = getenv("SVKBD_LAYERS"); @@ -849,8 +888,11 @@ main(int argc, char *argv[]) { layer_names_list = malloc(128); strcpy(layer_names_list, layers_env); } + const char* heightfactor_s = getenv("SVKBD_HEIGHTFACTOR"); + if (heightfactor_s != NULL) + heightfactor = atoi(heightfactor_s); - + //parse command line arguments for (i = 1; argv[i]; i++) { if(!strcmp(argv[i], "-v")) { die("svkbd-"VERSION", © 2006-2020 svkbd engineers," @@ -888,11 +930,22 @@ main(int argc, char *argv[]) { if(i >= argc - 1) continue; if (layer_names_list == NULL) layer_names_list = malloc(128); - strcpy(layer_names_list, argv[i+1]); + strcpy(layer_names_list, argv[++i]); + } else if(!strcmp(argv[i], "-s")) { + if(i >= argc - 1) + continue; + initial_layer_name = argv[++i]; + } else if(!strcmp(argv[i], "-H")) { + if(i >= argc - 1) + continue; + heightfactor = atoi(argv[++i]); + } else { + fprintf(stderr, "Invalid argument: %s\n", argv[i]); + exit(2); } } - init_layers(layer_names_list); + init_layers(layer_names_list, initial_layer_name); if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fprintf(stderr, "warning: no locale support\n");