On Fri, Jul 24, 2020 at 09:49:59PM +0200, Maarten van Gompel wrote: > --- > README.md | 87 ++++++++++++++++++++++++++++++++++++++++ > config.def.h | 1 + > layout.sxmo.h | 39 +++++++++++++++++- > svkbd.c | 107 +++++++++++++++++++++++++++++++++++++------------- > 4 files changed, 205 insertions(+), 29 deletions(-) > create mode 100644 README.md > > diff --git a/README.md b/README.md > new file mode 100644 > index 0000000..fd0e4d9 > --- /dev/null > +++ b/README.md > @@ -0,0 +1,87 @@ > +SVKBD: Simple Virtual Keyboard > +================================= > + > +This is a simple virtual keyboard, intended to be used in environments, > +where no keyboard is available. > + > +Installation > +------------ > + > + $ make > + $ make install > + > +This will create by default `svkbd-intl`, which is svkbd using an > international > +layout with multiple layers and overlays, and optimised for mobile devices. > + > +You can create svkbd for additional layouts by doing: > + > + $ make LAYOUT=$layout > + > +This will take the file `layout.$layout.h` and create `svkbd-$layout`. > +`make install` will then pick up the new file and install it accordingly. > + > +Layouts > +--------- > + > +The following layouts are available: > + > +* **Mobile Layouts:** > + * ``intl`` - A small international layout optimised for mobile devices. > This layout consists of multiple layers which > + can be switched on the fly, and overlays that appear on long-press > of certain keys, adding input ability for > + diacritics and other variants, as well as some emoji. The layers are: > + * a basic qwerty layer > + * a layer for numeric input, arrows, and punctuation > + * a layer for function keys, media keys, and arrows > + * a cyrillic layer (ЙЦУКЕН) > + * a dialer/numeric layer > + * ``sxmo`` - This is the original English layout for > [sxmo](https://sr.ht/~mil/Sxmo) with only a qwerty layer and > numeric/punctuation layer. > +* **Traditional layouts**: > + * ``en`` - An english layout without layers (QWERTY) > + * ``de`` - A german layout (QWERTZ) > + * ``ru`` - A russian layout (ЙЦУКЕН) > + * ``sh`` - A serbo-croatian layout using latin script (QWERTZ) > + > +Usage > +----- > + > + $ svkbd-intl > + > +This will open svkbd at the bottom of the screen, showing the default > +international layout. > + > + $ svkbd-intl -d > + > +This tells svkbd to announce itself being a dock window, which then > +is managed differently between different window managers. If using dwm > +and the dock patch, then this will make svkbd being managed by dwm and > +some space of the screen being reserved for it. > + > + $ svkbd-intl -g 400x200+1+1 > + > +This will start svkbd-intl with a size of 400x200 and at the upper left > +window corner. > + > +For layouts that consist of multiple layers, you can enable layers on > program start through either the ``-l`` flag or > +through the ``SVKBD_LAYERS`` environment variable. They both take a comma > separated list of layer names (as defined in > +your ``layout.*.h``). Use the ``↺`` button in the bottom-left to cycle > through all the layers. > + > +Some layouts come with overlays that will show when certain keys are hold > pressed for a longer time. For > +example, a long press on the ``a`` key will enable an overview showing all > kinds of diacritic combinations for ``a``. > + > +Overlay functionality interferes with the ability to hold a key and have it > outputted repeatedly. You can disable > +overlay functionality with the ``-O`` flag or by setting the environment > variable ``SVKBD_ENABLEOVERLAYS=0``. There is > +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, the > ``intl``, ``sxmo`` and ``en`` layouts simply rely on a standard US QWERTY > layout (setxkbmap us) being activated, the other layouts (``de``, ``ru``, > ``sh``) require their respective XKB keymaps to be active. > + > +If you use another XKB layout you will get unpredictable output that does > not match the labels on the virtual keycaps! > + > +Repository > +---------- > + > + git clone https://git.suckless.org/svkbd > + > diff --git a/config.def.h b/config.def.h > index 42d0c38..df37ff9 100644 > --- 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 > index f036fd6..6aafdb3 100644 > --- 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 > index 746af77..ae0267e 100644 > --- 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); > } > } > @@ -252,18 +255,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]); > } > @@ -314,7 +333,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) { > @@ -374,7 +393,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); > } > @@ -385,7 +404,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); > } > @@ -456,7 +475,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); > } > @@ -483,7 +502,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; > @@ -491,13 +510,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; > @@ -633,7 +652,7 @@ setup(void) { > if(!ww) > ww = sw; > if(!wh) > - wh = sh * rows / 32; > + wh = sh * rows / heightfactor; > > if(!wx) > wx = 0; > @@ -644,7 +663,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; > @@ -701,10 +720,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; > @@ -719,23 +738,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(); > } > @@ -748,7 +774,7 @@ togglelayer() { > currentlayer = 1; > } > if (debug) { printf("Toggling layer %d\n", currentlayer); > fflush(stdout); } > - memcpy(&keys, layers[currentlayer], sizeof(keys_en)); > + setlayer(); > updatekeys(); > drawkeyboard(); > } > @@ -759,7 +785,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]); > @@ -803,21 +829,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; > } > @@ -830,17 +867,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"); > @@ -848,8 +887,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," > @@ -887,11 +929,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); > } > } >
If heightfactor <= 0 it should probably error out with some "invalid argument" message (to prevent divide by zero later etc). > - 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"); > -- > 2.27.0 > > -- Kind regards, Hiltjo