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

Reply via email to