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.

This commit is contained in:
Maarten van Gompel 2020-08-02 15:46:16 +02:00 committed by Hiltjo Posthuma
parent 99935775af
commit 4dab556580
4 changed files with 125 additions and 29 deletions

7
README
View File

@ -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 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. ``≅`` 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 Repository
---------- ----------

View File

@ -1,6 +1,7 @@
static const Bool wmborder = True; static const Bool wmborder = True;
static int fontsize = 20; static int fontsize = 20;
static double overlay_delay = 1.0; 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[] = { static const char *fonts[] = {
"DejaVu Sans:bold:size=20" "DejaVu Sans:bold:size=20"
}; };

View File

@ -68,7 +68,7 @@ static Key overlay[OVERLAYS] = {
{ "æ", XK_ae }, { "æ", XK_ae },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ { 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_egrave },
{ "é", XK_eacute }, { "é", XK_eacute },
@ -465,11 +465,45 @@ static Key keys_ru[KEYS] = {
{ "↲ Enter", XK_Return, 2 }, { "↲ 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] = { static char* layer_names[LAYERS] = {
"en", "en",
"symbols", "symbols",
"functions", "functions",
"dialer",
"ru", "ru",
}; };
@ -477,6 +511,7 @@ static Key* available_layers[LAYERS] = {
keys_en, keys_en,
keys_symbols, keys_symbols,
keys_functions, keys_functions,
keys_dialer,
keys_ru keys_ru
}; };

107
svkbd.c
View File

@ -63,6 +63,7 @@ static void buttonrelease(XEvent *e);
static void cleanup(void); static void cleanup(void);
static void configurenotify(XEvent *e); static void configurenotify(XEvent *e);
static void countrows(); static void countrows();
static int countkeys(Key *k);
static void drawkeyboard(void); static void drawkeyboard(void);
static void drawkey(Key *k); static void drawkey(Key *k);
static void expose(XEvent *e); static void expose(XEvent *e);
@ -77,6 +78,7 @@ static void simulate_keyrelease(KeySym keysym);
static void showoverlay(int idx); static void showoverlay(int idx);
static void hideoverlay(); static void hideoverlay();
static void cyclelayer(); static void cyclelayer();
static void setlayer();
static void togglelayer(); static void togglelayer();
static void unpress(Key *k, KeySym mod); static void unpress(Key *k, KeySym mod);
static void updatekeys(); static void updatekeys();
@ -109,6 +111,7 @@ static int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0;
static char *name = "svkbd"; static char *name = "svkbd";
static int debug = 0; static int debug = 0;
static int numlayers = 0; static int numlayers = 0;
static int numkeys = 0;
static KeySym ispressingkeysym; static KeySym ispressingkeysym;
@ -130,7 +133,7 @@ motionnotify(XEvent *e)
XPointerMovedEvent *ev = &e->xmotion; XPointerMovedEvent *ev = &e->xmotion;
int i; int i;
for(i = 0; i < LENGTH(keys); i++) { for(i = 0; i < numkeys; i++) {
if(keys[i].keysym && ev->x > keys[i].x if(keys[i].keysym && ev->x > keys[i].x
&& ev->x < keys[i].x + keys[i].w && ev->x < keys[i].x + keys[i].w
&& ev->y > keys[i].y && ev->y > keys[i].y
@ -221,7 +224,7 @@ cleanup(void) {
} }
} }
if (debug) { printf("Cleanup: simulating key release\n"); fflush(stdout); } 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); XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
} }
} }
@ -253,18 +256,34 @@ void
countrows() { countrows() {
int i = 0; 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) if(keys[i].keysym == 0)
rows++; 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 void
drawkeyboard(void) { drawkeyboard(void) {
int i; int i;
for(i = 0; i < LENGTH(keys); i++) { for(i = 0; i < numkeys; i++) {
if(keys[i].keysym != 0) if(keys[i].keysym != 0)
drawkey(&keys[i]); drawkey(&keys[i]);
} }
@ -315,7 +334,7 @@ Key *
findkey(int x, int y) { findkey(int x, int y) {
int i; int i;
for(i = 0; i < LENGTH(keys); i++) { for(i = 0; i < numkeys; i++) {
if(keys[i].keysym && x > keys[i].x && if(keys[i].keysym && x > keys[i].x &&
x < keys[i].x + keys[i].w && x < keys[i].x + keys[i].w &&
y > keys[i].y && y < keys[i].y + keys[i].h) { y > keys[i].y && y < keys[i].y + keys[i].h) {
@ -375,7 +394,7 @@ press(Key *k, KeySym mod) {
} }
} else { } else {
if (debug) { printf("Simulating press: %ld\n", k->keysym); fflush(stdout); } 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)) { if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
simulate_keypress(keys[i].keysym); simulate_keypress(keys[i].keysym);
} }
@ -386,7 +405,7 @@ press(Key *k, KeySym mod) {
} }
simulate_keypress(k->keysym); 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)) { if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
simulate_keyrelease(keys[i].keysym); simulate_keyrelease(keys[i].keysym);
} }
@ -457,7 +476,7 @@ unpress(Key *k, KeySym mod) {
if (get_press_duration() < overlay_delay) { if (get_press_duration() < overlay_delay) {
if (debug) { printf("Delayed simulation of press after release: %ld\n", k->keysym); fflush(stdout); } 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() //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)) { if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
simulate_keypress(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)) { if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
simulate_keyrelease(keys[i].keysym); simulate_keyrelease(keys[i].keysym);
keys[i].pressed = 0; keys[i].pressed = 0;
@ -492,13 +511,13 @@ unpress(Key *k, KeySym mod) {
break; break;
} }
} }
if(i != LENGTH(keys)) { if(i != numkeys) {
if(pressedmod) { if(pressedmod) {
simulate_keyrelease(mod); simulate_keyrelease(mod);
} }
pressedmod = 0; pressedmod = 0;
for(i = 0; i < LENGTH(keys); i++) { for(i = 0; i < numkeys; i++) {
if(keys[i].pressed) { if(keys[i].pressed) {
simulate_keyrelease(keys[i].keysym); simulate_keyrelease(keys[i].keysym);
keys[i].pressed = 0; keys[i].pressed = 0;
@ -634,7 +653,7 @@ setup(void) {
if(!ww) if(!ww)
ww = sw; ww = sw;
if(!wh) if(!wh)
wh = sh * rows / 32; wh = sh * rows / heightfactor;
if(!wx) if(!wx)
wx = 0; wx = 0;
@ -645,7 +664,7 @@ setup(void) {
if(wy < 0) if(wy < 0)
wy = sh + wy - wh; wy = sh + wy - wh;
for(i = 0; i < LENGTH(keys); i++) for(i = 0; i < numkeys; i++)
keys[i].pressed = 0; keys[i].pressed = 0;
wa.override_redirect = !wmborder; wa.override_redirect = !wmborder;
@ -702,10 +721,10 @@ updatekeys() {
int x = 0, y = 0, h, base, r = rows; int x = 0, y = 0, h, base, r = rows;
h = (wh - 1) / rows; h = (wh - 1) / rows;
for(i = 0; i < LENGTH(keys); i++, r--) { for(i = 0; i < numkeys; i++, r--) {
for(j = i, base = 0; j < LENGTH(keys) && keys[j].keysym != 0; j++) for(j = i, base = 0; j < numkeys && keys[j].keysym != 0; j++)
base += keys[j].width; 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].x = x;
keys[i].y = y; keys[i].y = y;
keys[i].w = keys[i].width * (ww - 1) / base; keys[i].w = keys[i].width * (ww - 1) / base;
@ -720,23 +739,30 @@ updatekeys() {
void void
usage(char *argv0) { 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, "Options:\n");
fprintf(stderr, " -d - Set Dock Window Type\n"); fprintf(stderr, " -d - Set Dock Window Type\n");
fprintf(stderr, " -D - Enable debug\n"); fprintf(stderr, " -D - Enable debug\n");
fprintf(stderr, " -O - Disable overlays\n"); fprintf(stderr, " -O - Disable overlays\n");
fprintf(stderr, " -l - Comma separated list of layers to enable\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"); fprintf(stderr, " -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n");
exit(1); exit(1);
} }
void setlayer() {
numkeys = countkeys(layers[currentlayer]);
memcpy(&keys, layers[currentlayer], sizeof(Key) * numkeys);
}
void void
cyclelayer() { cyclelayer() {
currentlayer++; currentlayer++;
if (currentlayer >= numlayers) if (currentlayer >= numlayers)
currentlayer = 0; currentlayer = 0;
if (debug) { printf("Cycling to layer %d\n", currentlayer); fflush(stdout); } if (debug) { printf("Cycling to layer %d\n", currentlayer); fflush(stdout); }
memcpy(&keys, layers[currentlayer], sizeof(keys_en)); setlayer();
updatekeys(); updatekeys();
drawkeyboard(); drawkeyboard();
} }
@ -749,7 +775,7 @@ togglelayer() {
currentlayer = 1; currentlayer = 1;
} }
if (debug) { printf("Toggling layer %d\n", currentlayer); fflush(stdout); } if (debug) { printf("Toggling layer %d\n", currentlayer); fflush(stdout); }
memcpy(&keys, layers[currentlayer], sizeof(keys_en)); setlayer();
updatekeys(); updatekeys();
drawkeyboard(); drawkeyboard();
} }
@ -760,7 +786,7 @@ showoverlay(int idx) {
if (debug) { printf("Showing overlay %d\n", idx); fflush(stdout); } if (debug) { printf("Showing overlay %d\n", idx); fflush(stdout); }
int i,j; int i,j;
//unpress existing key (visually only) //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)) { if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
keys[i].pressed = 0; keys[i].pressed = 0;
drawkey(&keys[i]); drawkey(&keys[i]);
@ -804,21 +830,32 @@ sigterm(int sig)
void 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) { if (layer_names_list == NULL) {
numlayers = LAYERS; numlayers = LAYERS;
memcpy(&layers, &available_layers, sizeof(available_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 { } else {
char * s; char * s;
int j;
s = strtok(layer_names_list, ","); s = strtok(layer_names_list, ",");
while (s != NULL) { while (s != NULL) {
if (numlayers+1 > LAYERS) die("too many layers specified"); if (numlayers+1 > LAYERS) die("too many layers specified");
int found = 0; int found = 0;
for (j = 0; j < LAYERS; j++) { for (j = 0; j < LAYERS; j++) {
if (strcmp(layer_names[j], s) == 0) { if (strcmp(layer_names[j], s) == 0) {
fprintf(stderr, "Adding layer %s\n", s);
layers[numlayers] = available_layers[j]; 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; found = 1;
break; break;
} }
@ -831,17 +868,19 @@ init_layers(char * layer_names_list) {
s = strtok(NULL,","); s = strtok(NULL,",");
} }
} }
setlayer();
} }
int int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
int i, xr, yr, bitm; int i, xr, yr, bitm;
unsigned int wr, hr; unsigned int wr, hr;
char * initial_layer_name = NULL;
char * layer_names_list = NULL; char * layer_names_list = NULL;
memcpy(&keys, &keys_en, sizeof(keys_en));
signal(SIGTERM, sigterm); signal(SIGTERM, sigterm);
//parse environment variables
const char* enableoverlays_env = getenv("SVKBD_ENABLEOVERLAYS"); const char* enableoverlays_env = getenv("SVKBD_ENABLEOVERLAYS");
if (enableoverlays_env != NULL) enableoverlays = atoi(enableoverlays_env); if (enableoverlays_env != NULL) enableoverlays = atoi(enableoverlays_env);
const char* layers_env = getenv("SVKBD_LAYERS"); const char* layers_env = getenv("SVKBD_LAYERS");
@ -849,8 +888,11 @@ main(int argc, char *argv[]) {
layer_names_list = malloc(128); layer_names_list = malloc(128);
strcpy(layer_names_list, layers_env); 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++) { for (i = 1; argv[i]; i++) {
if(!strcmp(argv[i], "-v")) { if(!strcmp(argv[i], "-v")) {
die("svkbd-"VERSION", © 2006-2020 svkbd engineers," die("svkbd-"VERSION", © 2006-2020 svkbd engineers,"
@ -888,11 +930,22 @@ main(int argc, char *argv[]) {
if(i >= argc - 1) if(i >= argc - 1)
continue; continue;
if (layer_names_list == NULL) layer_names_list = malloc(128); 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()) if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fprintf(stderr, "warning: no locale support\n"); fprintf(stderr, "warning: no locale support\n");