URI: 
       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. - svkbd - simple virtual keyboard
  HTML git clone git://git.suckless.org/svkbd
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 4dab556580d2249e3f1597219b9c65bf8660f5e8
   DIR parent 99935775afce355bd16964b9a1b96ec35247292a
  HTML 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:
         M README                              |       7 +++++++
         M config.def.h                        |       1 +
         M layout.sxmo.h                       |      39 +++++++++++++++++++++++++++++--
         M svkbd.c                             |     107 +++++++++++++++++++++++--------
       
       4 files changed, 125 insertions(+), 29 deletions(-)
       ---
   DIR 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
        ----------
        
   DIR 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"
        };
   DIR 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
        };
        
   DIR 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");