On Fri, Jul 24, 2020 at 09:49:58PM +0200, Maarten van Gompel wrote: > --- > README | 63 ++++++++++++++++--- > layout.sxmo.h | 168 ++++++++++++++++++++++++++++++++++++++++---------- > svkbd.c | 165 ++++++++++++++++++++++++++++++++----------------- > 3 files changed, 297 insertions(+), 99 deletions(-) > > diff --git a/README b/README > index f1f2b82..fd0e4d9 100644 > --- a/README > +++ b/README > @@ -1,5 +1,6 @@ > -SVKBD > -===== > +SVKBD: Simple Virtual Keyboard > +================================= > + > This is a simple virtual keyboard, intended to be used in environments, > where no keyboard is available. > > @@ -9,34 +10,76 @@ Installation > $ make > $ make install > > -This will create by default `svkbd-en`, which is svkbd using an English > -keyboard layout. You can create svkbd for additional layouts by doing: > +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-en > + $ svkbd-intl > > This will open svkbd at the bottom of the screen, showing the default > -English layout. > +international layout. > > - $ svkbd-en -d > + $ svkbd-intl -d > > -This tells svkbd-en to announce itself being a dock window, which then > +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-en -g 400x200+1+1 > + $ svkbd-intl -g 400x200+1+1 > > -This will start svkbd-en with a size of 400x200 and at the upper left > +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 > ---------- > > diff --git a/layout.sxmo.h b/layout.sxmo.h > index 2ca0727..f036fd6 100644 > --- a/layout.sxmo.h > +++ b/layout.sxmo.h > @@ -1,7 +1,8 @@ > -#define KEYS 40 > +#define KEYS 43 > static Key keys[KEYS] = { NULL }; > > static Key keys_en[KEYS] = { > + { "Esc", XK_Escape, 1 }, > { 0, XK_q, 1 }, > { 0, XK_w, 1 }, > { 0, XK_e, 1 }, > @@ -15,6 +16,7 @@ static Key keys_en[KEYS] = { > > { 0 }, /* New row */ > > + { "'\"", XK_apostrophe, 1 }, > { 0, XK_a, 1 }, > { 0, XK_s, 1 }, > { 0, XK_d, 1 }, > @@ -25,10 +27,10 @@ static Key keys_en[KEYS] = { > { 0, XK_k, 1 }, > { 0, XK_l, 1 }, > { "/?", XK_slash, 1 }, > - /*{ "'", XK_apostrophe, 2 },*/ > > { 0 }, /* New row */ > > + { "123", XK_Mode_switch, 1 }, > { 0, XK_z, 1 }, > { 0, XK_x, 1 }, > { 0, XK_c, 1 }, > @@ -36,25 +38,21 @@ static Key keys_en[KEYS] = { > { 0, XK_b, 1 }, > { 0, XK_n, 1 }, > { 0, XK_m, 1 }, > - /*{ "/?", XK_slash, 1 },*/ > { "Tab", XK_Tab, 1 }, > { "⌫Bksp", XK_BackSpace, 2 }, > > { 0 }, /* New row */ > { "↺", XK_Cancel, 1}, > - { "Shft", XK_Shift_L, 1 }, > - /*{ "L", XK_Left, 1 },*/ > + { "Shift", XK_Shift_L, 2 }, > + { "Ctrl", XK_Control_L, 1 }, > + { "Alt", XK_Alt_L, 1 }, > + { "", XK_space, 2 }, > { "↓", XK_Down, 1 }, > { "↑", XK_Up, 1 }, > - /*{ "R", XK_Right, 1 },*/ > - { "", XK_space, 2 }, > - { "Esc", XK_Escape, 1 }, > - { "Ctrl", XK_Control_L, 1 }, > - /*{ "Alt", XK_Alt_L, 1 },*/ > { "↲ Enter", XK_Return, 2 }, > }; > > -#define OVERLAYS 165 > +#define OVERLAYS 197 > static Key overlay[OVERLAYS] = { > { 0, XK_a }, //Overlay for a > //--- > @@ -195,6 +193,58 @@ static Key overlay[OVERLAYS] = { > { 0, XK_r }, //New overlay > //--- > { "ř", XK_rcaron }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_softsign }, //New overlay > + //--- > + { "ъ", XK_Cyrillic_hardsign }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_ie }, //New overlay > + //--- > + { "ё", XK_Cyrillic_io }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_e }, //New overlay > + //--- > + { "Є", XK_Ukrainian_ie }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_i }, //New overlay > + //--- > + { "і", XK_Ukrainian_i }, > + { "ї", XK_Ukrainian_yi }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_u }, //New overlay > + //--- > + { "ў", XK_Byelorussian_shortu }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_shorti }, //New overlay > + //--- > + { "ј", XK_Cyrillic_je }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_el }, //New overlay > + //--- > + { "љ", XK_Cyrillic_lje }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_en }, //New overlay > + //--- > + { "њ", XK_Cyrillic_nje }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_tse }, //New overlay > + //--- > + { "џ", XK_Cyrillic_dzhe }, > + { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > + //--- > + { 0, XK_Cyrillic_che }, //New overlay > + //--- > + { "ћ", XK_Serbian_tshe }, > + { "ђ", XK_Serbian_dje }, > { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ > //--- > { "🙂", 0x101f642 }, //emoji overlay > @@ -262,6 +312,7 @@ static Key overlay[OVERLAYS] = { > > > static Key keys_symbols[KEYS] = { > + { "Esc", XK_Escape, 1 }, > { "1!", XK_1, 1 }, > { "2@", XK_2, 1 }, > { "3#", XK_3, 1 }, > @@ -285,31 +336,34 @@ static Key keys_symbols[KEYS] = { > { ".>", XK_period, 1 }, > { "/?", XK_slash, 1 }, > { "\\|", XK_backslash, 1 }, > + { ";:", XK_colon, 1 }, > > { 0 }, /* New row */ > > + { "abc", XK_Mode_switch, 1 }, > { "☺", 0x101f642, 1 }, > { "⇤", XK_Home, 1 }, > { "←", XK_Left, 1 }, > { "→", XK_Right, 1 }, > { "⇥", XK_End, 1 }, > { "⇊", XK_Next, 1 }, > - { ";:", XK_colon, 1 }, > + { "⇈", XK_Prior, 1 }, > { "Tab", XK_Tab, 1 }, > { "⌫Bksp", XK_BackSpace, 2 }, > > { 0 }, /* New row */ > { "↺", XK_Cancel, 1}, > - { "Shft", XK_Shift_L, 1 }, > + { "Shift", XK_Shift_L, 2 }, > + { "Ctrl", XK_Control_L, 1 }, > + { "Alt", XK_Alt_L, 1 }, > + { "", XK_space, 2 }, > { "↓", XK_Down, 1 }, > { "↑", XK_Up, 1 }, > - { "", XK_space, 2 }, > - { "Esc", XK_Escape, 1 }, > - { "Ctrl", XK_Control_L, 1 }, > { "↲ Enter", XK_Return, 2 }, > }; > > static Key keys_functions[KEYS] = { > + { "Esc", XK_Escape, 1 }, > { "F1", XK_F1, 1 }, > { "F2", XK_F2, 1 }, > { "F3", XK_F3, 1 }, > @@ -323,6 +377,7 @@ static Key keys_functions[KEYS] = { > > { 0 }, /* New row */ > > + { "≅", XK_KP_Insert, 1 }, > { "▶", XF86XK_AudioPlay, 1 }, > { "●", XF86XK_AudioRecord, 1 }, > { "■", XF86XK_AudioStop, 1 }, > @@ -336,6 +391,7 @@ static Key keys_functions[KEYS] = { > > { 0 }, /* New row */ > > + { "abc", XK_Mode_switch, 1 }, > { "Del", XK_Delete, 1 }, > { "⇤", XK_Home, 1 }, > { "←", XK_Left, 1 }, > @@ -348,30 +404,80 @@ static Key keys_functions[KEYS] = { > > { 0 }, /* New row */ > { "↺", XK_Cancel, 1}, > - { "Shft", XK_Shift_L, 1 }, > + { "Shift", XK_Shift_L, 2 }, > + { "Ctrl", XK_Control_L, 1 }, > + { "Alt", XK_Alt_L, 1 }, > + { "", XK_space, 2 }, > { "↓", XK_Down, 1 }, > { "↑", XK_Up, 1 }, > - { "", XK_space, 2 }, > - { "Esc", XK_Escape, 1 }, > - { "Ctrl", XK_Control_L, 1 }, > { "↲ Enter", XK_Return, 2 }, > }; > > > -#define LAYERS 3 > -static Key* layers[LAYERS] = { > - keys_en, > - keys_symbols, > - keys_functions, > +static Key keys_ru[KEYS] = { > + { "и", XK_Cyrillic_shorti, 1 }, > + { "ц", XK_Cyrillic_tse, 1 }, > + { "у", XK_Cyrillic_u, 1 }, > + { "к", XK_Cyrillic_ka, 1 }, > + { "е", XK_Cyrillic_ie, 1 }, > + { "н", XK_Cyrillic_en, 1 }, > + { "г", XK_Cyrillic_ghe, 1 }, > + { "ш", XK_Cyrillic_sha, 1 }, > + { "щ", XK_Cyrillic_shcha, 1 }, > + { "з", XK_Cyrillic_ze, 1 }, > + { "х", XK_Cyrillic_ha, 1 }, > + > + { 0 }, /* New row */ > + > + { "ф", XK_Cyrillic_ef, 1 }, > + { "ы", XK_Cyrillic_yeru, 1 }, > + { "в", XK_Cyrillic_ve, 1 }, > + { "а", XK_Cyrillic_a, 1 }, > + { "п", XK_Cyrillic_pe, 1 }, > + { "о", XK_Cyrillic_o, 1 }, > + { "л", XK_Cyrillic_el, 1 }, > + { "д", XK_Cyrillic_de, 1 }, > + { "ж", XK_Cyrillic_zhe, 1 }, > + { "э", XK_Cyrillic_e, 1 }, > + { "ю", XK_Cyrillic_yu, 1 }, > + > + { 0 }, /* New row */ > + > + { "123", XK_Mode_switch, 1 }, > + { "я", XK_Cyrillic_ya, 1 }, > + { "ч", XK_Cyrillic_che, 1 }, > + { "с", XK_Cyrillic_es, 1 }, > + { "м", XK_Cyrillic_em, 1 }, > + { "и", XK_Cyrillic_i, 1 }, > + { "т", XK_Cyrillic_te, 1 }, > + { "ь", XK_Cyrillic_softsign, 1 }, > + { "б", XK_Cyrillic_be, 1 }, > + { "⌫Bksp", XK_BackSpace, 2 }, > + > + { 0 }, /* New row */ > + { "↺", XK_Cancel, 1}, > + { "Shift", XK_Shift_L, 2 }, > + { "Ctrl", XK_Control_L, 1 }, > + { "Alt", XK_Alt_L, 1 }, > + { "", XK_space, 2 }, > + { "↓", XK_Down, 1 }, > + { "↑", XK_Up, 1 }, > + { "↲ Enter", XK_Return, 2 }, > }; > > +#define LAYERS 4 > +static char* layer_names[LAYERS] = { > + "en", > + "symbols", > + "functions", > + "ru", > +}; > > -#define CYCLEMODKEY (KEYS - 3) //third last key (Escape) > -#define CYCLEMODS 3 > -static Key cyclemods[CYCLEMODS] = { > - { "Esc", XK_Escape, 1 }, > - { "Alt", XK_Alt_L, 1 }, > - { "AGr", XK_ISO_Level3_Shift, 1 }, > +static Key* available_layers[LAYERS] = { > + keys_en, > + keys_symbols, > + keys_functions, > + keys_ru > }; > > > diff --git a/svkbd.c b/svkbd.c > index 6044732..746af77 100644 > --- a/svkbd.c > +++ b/svkbd.c > @@ -67,7 +67,6 @@ static void drawkeyboard(void); > static void drawkey(Key *k); > static void expose(XEvent *e); > static Key *findkey(int x, int y); > -static int iscyclemod(KeySym keysym); > static void leavenotify(XEvent *e); > static void press(Key *k, KeySym mod); > static double get_press_duration(); > @@ -76,9 +75,9 @@ static void setup(void); > static void simulate_keypress(KeySym keysym); > static void simulate_keyrelease(KeySym keysym); > static void showoverlay(int idx); > -static void cyclemod(); > static void hideoverlay(); > static void cyclelayer(); > +static void togglelayer(); > static void unpress(Key *k, KeySym mod); > static void updatekeys(); > > @@ -101,14 +100,15 @@ static Bool running = True, isdock = False; > static KeySym pressedmod = 0; > static struct timeval pressbegin; > static int currentlayer = 0; > +static int enableoverlays = 1; > static int currentoverlay = -1; // -1 = no overlay > -static int currentcyclemod = 0; > static KeySym overlaykeysym = 0; //keysym for which the overlay is presented > static int releaseprotect = 0; //set to 1 after overlay is shown, protecting > against immediate release > static int tmp_keycode = 1; > 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 KeySym ispressingkeysym; > > @@ -122,6 +122,8 @@ Bool sigtermd = False; > #endif > #include LAYOUT > > +static Key* layers[LAYERS]; > + > void > motionnotify(XEvent *e) > { > @@ -210,6 +212,15 @@ cleanup(void) { > // process will be dead before finger lifts - in that case we > // just trigger out fake up presses for all keys > if (sigtermd) { > + //handle last pending events > + XEvent ev; > + while (XPending(dpy)) { > + XNextEvent(dpy, &ev); > + if(handler[ev.type]) { > + (handler[ev.type])(&ev); /* call handler */ > + } > + } > + if (debug) { printf("Cleanup: simulating key release\n"); > fflush(stdout); } > for (i = 0; i < LENGTH(keys); i++) { > XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, > keys[i].keysym), False, 0); > } > @@ -217,8 +228,8 @@ cleanup(void) { > > for (i = 0; i < SchemeLast; i++) > free(scheme[i]); > - drw_free(drw); > drw_sync(drw); > + drw_free(drw); > XSync(dpy, False); > XDestroyWindow(dpy, win); > XSync(dpy, False); > @@ -272,7 +283,13 @@ drawkey(Key *k) { > drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1); > drw_rect(drw, k->x, k->y, k->w, k->h, 0, 0); > > - if(k->label) { > + if (k->keysym == XK_KP_Insert) { > + if (enableoverlays) { > + l = "≅"; > + } else { > + l = "≇"; > + } > + } else if(k->label) { > l = k->label; > } else { > l = XKeysymToString(k->keysym); > @@ -322,17 +339,6 @@ hasoverlay(KeySym keysym) { > return -1; > } > > -int > -iscyclemod(KeySym keysym) { > - int i; > - for(i = 0; i < CYCLEMODS; i++) { > - if(cyclemods[i].keysym == keysym) { > - return i; > - } > - } > - return -1; > -} > - > void > leavenotify(XEvent *e) { > if (currentoverlay != -1) { > @@ -358,16 +364,10 @@ press(Key *k, KeySym mod) { > pressbegin.tv_usec = 0; > ispressingkeysym = 0; > > - int cm = iscyclemod(k->keysym); > - if (cm != -1) { > - if (!pressbegin.tv_sec && !pressbegin.tv_usec) { > - //record the begin of the press, don't simulate the > actual keypress yet > - record_press_begin(k->keysym); > - } > - } else if(!IsModifierKey(k->keysym)) { > - if (currentoverlay == -1) > + if(!IsModifierKey(k->keysym)) { > + if (enableoverlays && currentoverlay == -1) > overlayidx = hasoverlay(k->keysym); > - if (overlayidx != -1) { > + if (enableoverlays && overlayidx != -1) { > if (!pressbegin.tv_sec && !pressbegin.tv_usec) { > //record the begin of the press, don't simulate > the actual keypress yet > record_press_begin(k->keysym); > @@ -437,6 +437,12 @@ unpress(Key *k, KeySym mod) { > case XK_Cancel: > cyclelayer(); > break; > + case XK_script_switch: > + togglelayer(); > + break; > + case XK_KP_Insert: > + enableoverlays = !enableoverlays; > + break; > case XK_Break: > running = False; > default: > @@ -445,7 +451,7 @@ unpress(Key *k, KeySym mod) { > } > > > - if ((pressbegin.tv_sec || pressbegin.tv_usec) && k && k->keysym == > ispressingkeysym) { > + if ((pressbegin.tv_sec || pressbegin.tv_usec) && enableoverlays && k && > k->keysym == ispressingkeysym) { > if (currentoverlay == -1) { > if (get_press_duration() < overlay_delay) { > if (debug) { printf("Delayed simulation of > press after release: %ld\n", k->keysym); fflush(stdout); } > @@ -472,7 +478,7 @@ unpress(Key *k, KeySym mod) { > if (k) { > printf("Simulation of release: %ld\n", k->keysym); > fflush(stdout); > } else { > - printf("Simulation of release (all keys)"); > fflush(stdout); > + printf("Simulation of release (all keys)\n"); > fflush(stdout); > } > } > > @@ -500,7 +506,7 @@ unpress(Key *k, KeySym mod) { > } > } > > - if (currentoverlay != -1) { > + if (enableoverlays && currentoverlay != -1) { > if (releaseprotect) { > releaseprotect = 0; > } else { > @@ -516,7 +522,6 @@ run(void) { > fd_set fds; > struct timeval tv; > double duration = 0.0; > - int cyclemodidx; > > > xfd = ConnectionNumber(dpy); > @@ -528,6 +533,7 @@ run(void) { > XFlush(dpy); > > while (running) { > + usleep(100000L); > FD_ZERO(&fds); > FD_SET(xfd, &fds); > if (select(xfd + 1, &fds, NULL, NULL, &tv)) { > @@ -543,19 +549,13 @@ run(void) { > if (debug == 2) { printf("%f\n", duration); > fflush(stdout); } > if (get_press_duration() >= overlay_delay) { > if (debug) { printf("press duration > %f\n", duration); fflush(stdout); } > - cyclemodidx = > iscyclemod(ispressingkeysym); > - if (cyclemodidx != -1) { > - cyclemod(); > - } else { > - > showoverlay(hasoverlay(ispressingkeysym)); > - } > + > showoverlay(hasoverlay(ispressingkeysym)); > pressbegin.tv_sec = 0; > pressbegin.tv_usec = 0; > ispressingkeysym = 0; > } > } > } > - usleep(100000L); > } > } > > @@ -719,14 +719,20 @@ updatekeys() { > > void > usage(char *argv0) { > - fprintf(stderr, "usage: %s [-hdvD] [-g geometry] [-fn font]\n", argv0); > + fprintf(stderr, "usage: %s [-hdvDOl] [-g geometry] [-fn font]\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, " -fn [font] - Set font (Xft, e.g: DejaVu > Sans:bold:size=20)\n"); > exit(1); > } > > void > cyclelayer() { > currentlayer++; > - if (currentlayer >= LAYERS) > + if (currentlayer >= numlayers) > currentlayer = 0; > if (debug) { printf("Cycling to layer %d\n", currentlayer); > fflush(stdout); } > memcpy(&keys, layers[currentlayer], sizeof(keys_en)); > @@ -735,29 +741,19 @@ cyclelayer() { > } > > void > -cyclemod() { > - int i; > - //unpress all pressed keys > - for(i = 0; i < LENGTH(keys); i++) { > - if(keys[i].pressed) { > - keys[i].pressed = 0; > - drawkey(&keys[i]); > - } > +togglelayer() { > + if (currentlayer > 0) { > + currentlayer = 0; > + } else if (numlayers > 1) { > + currentlayer = 1; > } > - pressedmod = 0; > - pressbegin.tv_sec = 0; > - pressbegin.tv_usec = 0; > - ispressingkeysym = 0; > - currentcyclemod++; > - if (currentcyclemod >= CYCLEMODS) > - currentcyclemod = 0; > - if (debug) { printf("Cycling modifier to %d\n", currentcyclemod); > fflush(stdout); } > - keys[CYCLEMODKEY].label = cyclemods[currentcyclemod].label; > - keys[CYCLEMODKEY].keysym = cyclemods[currentcyclemod].keysym; > - drawkey(&keys[CYCLEMODKEY]); > - XSync(dpy, False); > + if (debug) { printf("Toggling layer %d\n", currentlayer); > fflush(stdout); } > + memcpy(&keys, layers[currentlayer], sizeof(keys_en)); > + updatekeys(); > + drawkeyboard(); > } > > + > void > showoverlay(int idx) { > if (debug) { printf("Showing overlay %d\n", idx); fflush(stdout); } > @@ -802,15 +798,58 @@ sigterm(int sig) > { > running = False; > sigtermd = True; > + if (debug) { printf("Sigterm received\n"); fflush(stdout); } > +} > + > + > +void > +init_layers(char * layer_names_list) { > + if (layer_names_list == NULL) { > + numlayers = LAYERS; > + memcpy(&layers, &available_layers, sizeof(available_layers)); > + } 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) { > + layers[numlayers] = available_layers[j]; > + printf("Adding layer %s\n", s); > + found = 1; > + break; > + } > + } > + if (!found) { > + fprintf(stderr, "Undefined layer: %s\n", s); > + exit(3); > + } > + numlayers++; > + s = strtok(NULL,","); > + } > + } > } > > int > main(int argc, char *argv[]) { > int i, xr, yr, bitm; > unsigned int wr, hr; > + char * layer_names_list = NULL; > > memcpy(&keys, &keys_en, sizeof(keys_en)); > signal(SIGTERM, sigterm); > + > + const char* enableoverlays_env = getenv("SVKBD_ENABLEOVERLAYS"); > + if (enableoverlays_env != NULL) enableoverlays = > atoi(enableoverlays_env); > + const char* layers_env = getenv("SVKBD_LAYERS"); > + if (layers_env != NULL) { > + layer_names_list = malloc(128);
No malloc check. > + strcpy(layer_names_list, layers_env); > + } > + Buffer overflow. > + > for (i = 1; argv[i]; i++) { > if(!strcmp(argv[i], "-v")) { > die("svkbd-"VERSION", © 2006-2020 svkbd engineers," > @@ -842,9 +881,18 @@ main(int argc, char *argv[]) { > debug = 1; > } else if(!strcmp(argv[i], "-h")) { > usage(argv[0]); > + } else if(!strcmp(argv[i], "-O")) { > + enableoverlays = 0; > + } else if(!strcmp(argv[i], "-l")) { > + if(i >= argc - 1) > + continue; > + if (layer_names_list == NULL) layer_names_list = > malloc(128); No malloc check. > + strcpy(layer_names_list, argv[i+1]); Buffer overflow. > } > } > > + init_layers(layer_names_list); > + > if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) > fprintf(stderr, "warning: no locale support\n"); > if(!(dpy = XOpenDisplay(0))) > @@ -853,5 +901,6 @@ main(int argc, char *argv[]) { > run(); > cleanup(); > XCloseDisplay(dpy); > + if (layer_names_list != NULL) free(layer_names_list); No need to check free(NULL). free(NULL) is valid. > return 0; > } > -- > 2.27.0 > > -- Kind regards, Hiltjo