~mil/sxmo-devel

Layers & Overlay support, dialer keyboard, and various fixes. v7 UNKNOWN

Maarten van Gompel: 5
 Added overlays (appearing on long press), multiple layer support (rather than just a toggle) with new layers, style changes
 added an extra key column (alt now on keyboard by default), added cyrillic keymap, added a toggle to enable/disable overlays, added quick toggle buttons to toggle primary two layers. Added some configurability using command line parameters and environment variables.
 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.
 adding some minor error catching
 fixed the select() logic in run() to handle sigterm situations

 12 files changed, 1005 insertions(+), 203 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~mil/sxmo-devel/patches/11835/mbox | git am -3
Learn more about email & git

[PATCH v7 1/5] Added overlays (appearing on long press), multiple layer support (rather than just a toggle) with new layers, style changes Export this patch

---
 config.def.h  |   9 +-
 layout.sxmo.h | 291 +++++++++++++++++++++++++++++++++++++++++--
 svkbd.c       | 338 +++++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 582 insertions(+), 56 deletions(-)

diff --git a/config.def.h b/config.def.h
index f748b93..fe404b4 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,12 +1,13 @@
static const Bool wmborder = True;
static int fontsize = 16;
static int fontsize = 20;
static double overlay_delay = 1.0;
static const char *fonts[] = {
	"monospace:size=16"
	"DejaVu Sans:bold:size=20"
};
static const char *colors[SchemeLast][2] = {
	/*     fg         bg       */
	[SchemeNorm] = { "#58a7c6", "#14313d" },
	[SchemePress] = { "#ffffff", "#005577" },
	[SchemeNorm] = { "#ffffff", "#14313d" },
	[SchemePress] = { "#ffffff", "#000000" },
	[SchemeHighlight] = { "#58a7c6", "#005577" },
};

diff --git a/layout.sxmo.h b/layout.sxmo.h
index 1ee74bc..2ca0727 100644
--- a/layout.sxmo.h
+++ b/layout.sxmo.h
@@ -1,6 +1,7 @@
static Key keys[40] = { NULL };
#define KEYS 40
static Key keys[KEYS] = { NULL };

static Key keys_en[40] = {
static Key keys_en[KEYS] = {
        { 0, XK_q, 1 },
        { 0, XK_w, 1 },
        { 0, XK_e, 1 },
@@ -23,7 +24,7 @@ static Key keys_en[40] = {
        { 0, XK_j, 1 },
        { 0, XK_k, 1 },
        { 0, XK_l, 1 },
        { ";:", XK_colon, 1 },
        { "/?", XK_slash, 1 },
        /*{ "'", XK_apostrophe, 2 },*/

        { 0 }, /* New row */
@@ -37,7 +38,7 @@ static Key keys_en[40] = {
        { 0, XK_m, 1 },
        /*{ "/?", XK_slash, 1 },*/
        { "Tab", XK_Tab, 1 },
        { "⇍ Bksp", XK_BackSpace, 2 },
        { "⌫Bksp", XK_BackSpace, 2 },

        { 0 }, /* New row */
        { "↺", XK_Cancel, 1},
@@ -53,7 +54,214 @@ static Key keys_en[40] = {
        { "↲ Enter", XK_Return, 2 },
};

static Key keys_symbols[40] = {
#define OVERLAYS 165
static Key overlay[OVERLAYS] = {
        { 0, XK_a }, //Overlay for a
        //---
        { "à", XK_agrave },
        { "á", XK_aacute },
        { "â", XK_acircumflex },
        { "ä", XK_adiaeresis },
        { "ą", XK_aogonek },
        { "ã", XK_atilde },
        { "ā", XK_amacron },
        { "ă", XK_abreve },
        { "å", XK_aring },
        { "æ", XK_ae },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
        { 0, XK_e }, //Overlay for e
        //---
        { "è", XK_egrave },
        { "é", XK_eacute },
        { "ê", XK_ecircumflex },
        { "ë", XK_ediaeresis },
        { "ę", XK_eogonek },
        { "ē", XK_emacron },
        { "ė", XK_eabovedot },
        { 0, XK_Cancel },
        //--
        { 0, XK_y }, //New overlay
        //---
        { "ỳ", XK_ygrave },
        { "ý", XK_yacute },
        { "ŷ", XK_ycircumflex },
        { "ÿ", XK_ydiaeresis },
        { 0, XK_Cancel },
        //--
        { 0, XK_u }, //New overlay
        //---
        { "ù", XK_ugrave },
        { "ú", XK_uacute },
        { "û", XK_ucircumflex },
        { "ü", XK_udiaeresis },
        { "ų", XK_uogonek },
        { "ū", XK_umacron },
        { "ů", XK_uring},
        { "ŭ", XK_ubreve},
        { "ű", XK_udoubleacute },
        { 0, XK_Cancel },
        //--
        { 0, XK_i }, //New overlay
        //---
        { "ì", XK_igrave },
        { "í", XK_iacute },
        { "î", XK_icircumflex },
        { "ï", XK_idiaeresis },
        { "į", XK_iogonek },
        { "ī", XK_imacron },
        { "ı", XK_idotless },
        { 0, XK_Cancel },
        //--
        { 0, XK_o }, //New overlay
        //---
        { "ò", XK_ograve },
        { "ó", XK_oacute },
        { "ô", XK_ocircumflex },
        { "ö", XK_odiaeresis },
        { "ǫ", XK_ogonek },
        { "õ", XK_otilde },
        { "ō", XK_omacron },
        { "ø", XK_oslash },
        { "ő", XK_odoubleacute },
        { "œ", XK_oe },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
        { 0, XK_d }, //New overlay
        //---
        { "ď", XK_dcaron },
        { "ð", XK_eth },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
        { 0, XK_c }, //New overlay
        //---
        { "ç", XK_ccedilla },
        { "ĉ", XK_ccircumflex },
        { "č", XK_ccaron },
        { "ć", XK_cacute },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
        { 0, XK_s }, //New overlay
        //---
        { "ş", XK_scedilla },
        { "ŝ", XK_scircumflex },
        { "š", XK_scaron },
        { "ś", XK_sacute },
        { "ß", XK_ssharp },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //---
        { 0, XK_z }, //New overlay
        //---
        { "ž", XK_zcaron },
        { "ż", XK_zabovedot },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
        { 0, XK_n }, //New overlay
        //---
        { "ñ", XK_ntilde },
        { "ń", XK_nacute },
        { "ň", XK_ncaron },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //
        { 0, XK_t }, //New overlay
        //---
        { "ț", XK_tcedilla },
        { "ť", XK_tcaron },
        { "þ", XK_thorn },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //----
        { 0, XK_g }, //New overlay
        //---
        { "ĝ", XK_gcircumflex },
        { "ğ", XK_gbreve },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //
        { 0, XK_h }, //New overlay
        //---
        { "ĥ", XK_hcircumflex },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //
        { 0, XK_j }, //New overlay
        //---
        { "ĵ", XK_jcircumflex },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
        { 0, XK_l }, //New overlay
        //---
        { "ł", XK_lstroke },
        { "ľ", XK_lcaron },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
        { 0, XK_r }, //New overlay
        //---
        { "ř", XK_rcaron },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
		//---
        { "🙂", 0x101f642 }, //emoji overlay
        //---
        { "😀", 0x101f600 },
        { "😁", 0x101f601 },
        { "😂", 0x101f602 },
        { "😃", 0x101f603 },
        { "😄", 0x101f604 },
        { "😅", 0x101f605 },
        { "😆", 0x101f606 },
        { "😇", 0x101f607 },
        { "😈", 0x101f608 },
        { "😉", 0x101f609 },
        { "😊", 0x101f60a },
        { "😋", 0x101f60b },
        { "😌", 0x101f60c },
        { "😍", 0x101f60d },
        { "😎", 0x101f60e },
        { "😏", 0x101f60f },
        { "😐", 0x101f610 },
        { "😒", 0x101f612 },
        { "😓", 0x101f613 },
        { "😛", 0x101f61b },
        { "😮", 0x101f62e },
        { "😟", 0x101f61f },
        { "😟", 0x101f620 },
        { "😢", 0x101f622 },
        { "😭", 0x101f62d },
        { "😳", 0x101f633 },
        { "😴", 0x101f634 },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
        //--
		{ "/?", XK_slash }, //punctuation overlay
		//--
		{ "1!", XK_1, 1 },
		{ "2@", XK_2, 1 },
		{ "3#", XK_3, 1 },
		{ "4$", XK_4, 1 },
		{ "5%", XK_5, 1 },
		{ "6^", XK_6, 1 },
		{ "7&", XK_7, 1 },
		{ "8*", XK_8, 1 },
		{ "9(", XK_9, 1 },
		{ "0)", XK_0, 1 },
		{ "'\"", XK_apostrophe, 1 },
		{ "`~", XK_grave, 1 },
		{ "-_", XK_minus, 1 },
		{ "=+", XK_plus, 1 },
		{ "[{", XK_bracketleft, 1 },
		{ "]}", XK_bracketright, 1 },
		{ ",<", XK_comma, 1 },
		{ ".>", XK_period, 1 },
		{ "/?", XK_slash, 1 },
		{ "\\|", XK_backslash, 1 },
		{ "¡", XK_exclamdown, 1 },
		{ "?", XK_questiondown, 1 },
		{ "°", XK_degree, 1 },
		{ "£", XK_sterling, 1 },
		{ "€", XK_EuroSign, 1 },
		{ "¥", XK_yen, 1 },
		{ ";:", XK_colon, 1 },
        { 0, XK_Cancel }, /* XK_Cancel signifies  overlay boundary */
};


static Key keys_symbols[KEYS] = {
  { "1!", XK_1, 1 },
  { "2@", XK_2, 1 },
  { "3#", XK_3, 1 },
@@ -80,7 +288,55 @@ static Key keys_symbols[40] = {

  { 0 }, /* New row */

  { "", XK_Shift_L|XK_bar, 1 },
  { "☺", 0x101f642, 1 },
  { "⇤", XK_Home, 1 },
  { "←", XK_Left, 1 },
  { "→", XK_Right, 1 },
  { "⇥", XK_End, 1 },
  { "⇊", XK_Next, 1 },
  { ";:", XK_colon, 1 },
  { "Tab", XK_Tab, 1 },
  { "⌫Bksp", XK_BackSpace, 2 },

  { 0 }, /* New row */
  { "↺", XK_Cancel, 1},
  { "Shft", XK_Shift_L, 1 },
  { "↓", 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] = {
  { "F1", XK_F1, 1 },
  { "F2", XK_F2, 1 },
  { "F3", XK_F3, 1 },
  { "F4", XK_F4, 1 },
  { "F5", XK_F5, 1 },
  { "F6", XK_F6, 1 },
  { "F7", XK_F7, 1 },
  { "F8", XK_F8, 1 },
  { "F9", XK_F9, 1 },
  { "F10", XK_F10, 1 },

  { 0 }, /* New row */

  { "▶", XF86XK_AudioPlay, 1 },
  { "●", XF86XK_AudioRecord, 1 },
  { "■", XF86XK_AudioStop, 1 },
  { "◂◂", XF86XK_AudioPrev, 1 },
  { "▸▸", XF86XK_AudioNext, 1 },
  { "♫M", XF86XK_AudioMute, 1 },
  { "♫-", XF86XK_AudioLowerVolume, 1 },
  { "♫+", XF86XK_AudioRaiseVolume, 1 },
  { "☀-", XF86XK_MonBrightnessDown, 1 },
  { "☀+", XF86XK_MonBrightnessUp, 1 },

  { 0 }, /* New row */

  { "Del", XK_Delete, 1 },
  { "⇤", XK_Home, 1 },
  { "←", XK_Left, 1 },
  { "→", XK_Right, 1 },
@@ -88,22 +344,37 @@ static Key keys_symbols[40] = {
  { "⇊", XK_Next, 1 },
  { "⇈", XK_Prior, 1 },
  { "Tab", XK_Tab, 1 },
  { "⇍ Bksp", XK_BackSpace, 2 },
  { "⌫Bksp", XK_BackSpace, 2 },

  { 0 }, /* New row */
  { "↺", XK_Cancel, 1},
  { "Shft", XK_Shift_L, 1 },
  /*{ "L", XK_Left, 1 },*/
  { "↓", 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 LAYERS 3
static Key* layers[LAYERS] = {
    keys_en,
    keys_symbols,
    keys_functions,
};


#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 },
};


Buttonmod buttonmods[] = {
        { XK_Shift_L, Button2 },
        { XK_Alt_L, Button3 },
diff --git a/svkbd.c b/svkbd.c
index 95c0de0..6bfdc3d 100644
--- a/svkbd.c
+++ b/svkbd.c
@@ -8,6 +8,8 @@
#include <string.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include <X11/keysymdef.h>
#include <X11/XF86keysym.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -18,16 +20,19 @@
#include <X11/extensions/Xinerama.h>
#endif
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>

#include "drw.h"
#include "util.h"



/* macros */
#define LENGTH(x)       (sizeof x / sizeof x[0])
#define TEXTW(X)        (drw_fontset_getwidth(drw, (X)))
#define STRINGTOKEYSYM(X)			(XStringToKeySym(X))

/* enums */
enum { SchemeNorm, SchemePress, SchemeHighlight, SchemeLast };
@@ -62,11 +67,18 @@ 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();
static void run(void);
static void setup(void);
static void togglelayer();
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 unpress(Key *k, KeySym mod);
static void updatekeys();

@@ -87,11 +99,20 @@ static Window root, win;
static Clr* scheme[SchemeLast];
static Bool running = True, isdock = False;
static KeySym pressedmod = 0;
static struct timeval pressbegin;
static int currentlayer = 0;
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 KeySym ispressingkeysym;

Bool ispressing = False;
Bool baselayer = True;
Bool sigtermd = False;

/* configuration, allows nested code to access above variables */
@@ -284,43 +305,126 @@ findkey(int x, int y) {
}


int
hasoverlay(KeySym keysym) {
	int begin, i;
	begin = 0;
	for(i = 0; i < OVERLAYS; i++) {
		if(overlay[i].keysym == XK_Cancel) {
			begin = i+1;
		} else if(overlay[i].keysym == keysym) {
			return begin+1;
		}
	}
	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) {
		hideoverlay();
	}
	unpress(NULL, 0);
}

void record_press_begin(KeySym ks) {
	//record the begin of the press, don't simulate the actual keypress yet
	gettimeofday(&pressbegin, NULL);
	ispressingkeysym = ks;
}

void
press(Key *k, KeySym mod) {
	int i;
	int overlayidx = -1;
	k->pressed = !k->pressed;

	if(!IsModifierKey(k->keysym)) {
		for(i = 0; i < LENGTH(keys); i++) {
			if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
				XTestFakeKeyEvent(dpy,
					XKeysymToKeycode(dpy, keys[i].keysym),
					True, 0);
			}
		}
		pressedmod = mod;
		if(pressedmod) {
			XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, mod),
					True, 0);
	if (debug) { printf("Begin press: %ld\n", k->keysym); fflush(stdout); }
	pressbegin.tv_sec = 0;
	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);
		}
		XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, k->keysym), True, 0);
	} else if(!IsModifierKey(k->keysym)) {
		if (currentoverlay == -1)
			overlayidx = hasoverlay(k->keysym);
		if (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);
			}
		} else {
			if (debug) { printf("Simulating press: %ld\n", k->keysym); fflush(stdout); }
			for(i = 0; i < LENGTH(keys); i++) {
				if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
					simulate_keypress(keys[i].keysym);
				}
			}
			pressedmod = mod;
			if(pressedmod) {
				simulate_keypress(mod);
			}
			simulate_keypress(k->keysym);

		for(i = 0; i < LENGTH(keys); i++) {
			if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
				XTestFakeKeyEvent(dpy,
					XKeysymToKeycode(dpy, keys[i].keysym),
					False, 0);
			for(i = 0; i < LENGTH(keys); i++) {
				if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
					simulate_keyrelease(keys[i].keysym);
				}
			}
		}
	}
	drawkey(k);
}





int tmp_remap(KeySym keysym) {
	XChangeKeyboardMapping(dpy, tmp_keycode, 1, &keysym, 1);
	XSync(dpy, False);
	return tmp_keycode;
}

void
simulate_keypress(KeySym keysym) {
	KeyCode code = XKeysymToKeycode(dpy, keysym);
	if (code == 0)
		code = tmp_remap(keysym);
	XTestFakeKeyEvent(dpy, code, True, 0);
}

void
simulate_keyrelease(KeySym keysym) {
	KeyCode code = XKeysymToKeycode(dpy, keysym);
	if (code == 0)
		code = tmp_remap(keysym);
	XTestFakeKeyEvent(dpy, code, False, 0);
}


double get_press_duration() {
	struct timeval now;
	gettimeofday(&now, NULL);
	return (double) ((now.tv_sec * 1000000L + now.tv_usec) - (pressbegin.tv_sec * 1000000L + pressbegin.tv_usec)) / (double) 1000000L;
}

void
unpress(Key *k, KeySym mod) {
	int i;
@@ -328,7 +432,7 @@ unpress(Key *k, KeySym mod) {
	if(k != NULL) {
		switch(k->keysym) {
		case XK_Cancel:
			togglelayer();
			cyclelayer();
			break;
		case XK_Break:
		  running = False;
@@ -337,11 +441,42 @@ unpress(Key *k, KeySym mod) {
		}
	}


	if ((pressbegin.tv_sec || pressbegin.tv_usec) && 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); }
				//simulate the press event, as we postponed it earlier in press()
				for(i = 0; i < LENGTH(keys); i++) {
					if(keys[i].pressed && IsModifierKey(keys[i].keysym)) {
						simulate_keypress(keys[i].keysym);
					}
				}
				pressedmod = mod;
				if(pressedmod) {
					simulate_keypress(mod);
				}
				simulate_keypress(k->keysym);
				pressbegin.tv_sec = 0;
				pressbegin.tv_usec = 0;
			} else {
				return;
			}
		}
	}

	if (debug) {
		if (k) {
			printf("Simulation of release: %ld\n", k->keysym); fflush(stdout);
		} else {
			printf("Simulation of release (all keys)"); fflush(stdout);
		}
	}


	for(i = 0; i < LENGTH(keys); i++) {
		if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
			XTestFakeKeyEvent(dpy,
				XKeysymToKeycode(dpy, keys[i].keysym),
				False, 0);
			simulate_keyrelease(keys[i].keysym);
			keys[i].pressed = 0;
			drawkey(&keys[i]);
			break;
@@ -349,22 +484,26 @@ unpress(Key *k, KeySym mod) {
	}
	if(i != LENGTH(keys)) {
		if(pressedmod) {
			XTestFakeKeyEvent(dpy,
				XKeysymToKeycode(dpy, pressedmod),
				False, 0);
			simulate_keyrelease(mod);
		}
		pressedmod = 0;

		for(i = 0; i < LENGTH(keys); i++) {
			if(keys[i].pressed) {
				XTestFakeKeyEvent(dpy,
					XKeysymToKeycode(dpy,
						keys[i].keysym), False, 0);
				simulate_keyrelease(keys[i].keysym);
				keys[i].pressed = 0;
				drawkey(&keys[i]);
			}
		}
	}

	if (currentoverlay != -1) {
		if (releaseprotect) {
			releaseprotect = 0;
		} else {
			hideoverlay();
		}
	}
}

void
@@ -373,11 +512,14 @@ run(void) {
	int xfd;
	fd_set fds;
	struct timeval tv;
	double duration = 0.0;
	int cyclemodidx;


	xfd = ConnectionNumber(dpy);
	tv.tv_usec = 0;
	tv.tv_sec = 2;
	tv.tv_sec = 1;


	//XSync(dpy, False);
	XFlush(dpy);
@@ -392,7 +534,25 @@ run(void) {
					(handler[ev.type])(&ev); /* call handler */
				}
			}
		} else {
			if (ispressing && ispressingkeysym) {
				duration = get_press_duration();
				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));
					}
					pressbegin.tv_sec = 0;
					pressbegin.tv_usec = 0;
					ispressingkeysym = 0;
				}
			}
		}
		usleep(100000L);
	}
}

@@ -425,10 +585,34 @@ setup(void) {
		sw = DisplayWidth(dpy, screen);
		sh = DisplayHeight(dpy, screen);
	}
    drw = drw_create(dpy, screen, root, sw, sh);
	drw = drw_create(dpy, screen, root, sw, sh);
	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
		die("no fonts could be loaded.");
    drw_setscheme(drw, scheme[SchemeNorm]);
	drw_setscheme(drw, scheme[SchemeNorm]);

	//find an unused keycode to use as a temporary keycode (derived from source: https://stackoverflow.com/questions/44313966/c-xtest-emitting-key-presses-for-every-unicode-character)
	KeySym *keysyms = NULL;
	int keysyms_per_keycode = 0;
	int keycode_low, keycode_high;
	Bool key_is_empty;
	int symindex;
	XDisplayKeycodes(dpy, &keycode_low, &keycode_high);
	keysyms = XGetKeyboardMapping(dpy, keycode_low, keycode_high - keycode_low, &keysyms_per_keycode);
	for(i = keycode_low; i <= keycode_high; i++) {
		key_is_empty = True;
		for(j = 0; j < keysyms_per_keycode; j++) {
			symindex = (i - keycode_low) * keysyms_per_keycode + j;
			if(keysyms[symindex] != 0) {
				key_is_empty = False;
			} else {
				break;
			}
		}
		if (key_is_empty) {
			tmp_keycode = i;
			break;
		}
	}

	/* init appearance */
	for (j = 0; j < SchemeLast; j++)
@@ -464,9 +648,9 @@ setup(void) {
	wa.border_pixel = scheme[SchemeNorm][ColFg].pixel;
	wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
	win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0,
			    CopyFromParent, CopyFromParent, CopyFromParent,
			    CWOverrideRedirect | CWBorderPixel |
			    CWBackingPixel, &wa);
			CopyFromParent, CopyFromParent, CopyFromParent,
			CWOverrideRedirect | CWBorderPixel |
			CWBackingPixel, &wa);
	XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask|
			ButtonPressMask|ExposureMask|LeaveWindowMask|
			PointerMotionMask);
@@ -488,6 +672,7 @@ setup(void) {
	XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh,
			ch);

	XFree(keysyms);
	XFree(ch);
	XFree(wmh);
	XFree(str.value);
@@ -531,18 +716,84 @@ updatekeys() {

void
usage(char *argv0) {
	fprintf(stderr, "usage: %s [-hdv] [-g geometry]\n", argv0);
	fprintf(stderr, "usage: %s [-hdvD] [-g geometry] [-fn font]\n", argv0);
	exit(1);
}

void
togglelayer() {
	memcpy(&keys, baselayer ? &keys_symbols : &keys_en, sizeof(keys_en));
cyclelayer() {
	currentlayer++;
	if (currentlayer >= LAYERS)
		currentlayer = 0;
	if (debug) { printf("Cycling to layer %d\n", currentlayer); fflush(stdout); }
	memcpy(&keys, layers[currentlayer], sizeof(keys_en));
	updatekeys();
	drawkeyboard();
}

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]);
		}
	}
	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);
}

void
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++) {
		if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
			keys[i].pressed = 0;
			drawkey(&keys[i]);
			break;
		}
	}

	for (i = idx, j=0; i < OVERLAYS; i++, j++) {
		if (overlay[i].keysym == XK_Cancel) {
			break;
		}
		while (keys[j].keysym == 0) j++;
		keys[j].label = overlay[i].label;
		keys[j].keysym = overlay[i].keysym;
	}
	currentoverlay = idx;
	overlaykeysym = ispressingkeysym;
	releaseprotect = 1;
	updatekeys();
	drawkeyboard();
	baselayer = !baselayer;
	XSync(dpy, False);
}

void
hideoverlay() {
	if (debug) { printf("Hiding overlay %d\n", currentoverlay); fflush(stdout); }
	currentoverlay = -1;
	overlaykeysym = 0;
	currentlayer = -1;
	cyclelayer();
}


void
sigterm(int sig)
{
@@ -582,6 +833,10 @@ main(int argc, char *argv[]) {
			if(bitm & YNegative && wy == 0)
				wy = -1;
			i++;
		} else if (!strcmp(argv[i], "-fn")) { /* font or font set */
			fonts[0] = argv[++i];
		} else if(!strcmp(argv[i], "-D")) {
			debug = 1;
		} else if(!strcmp(argv[i], "-h")) {
			usage(argv[0]);
		}
@@ -597,4 +852,3 @@ main(int argc, char *argv[]) {
	XCloseDisplay(dpy);
	return 0;
}

-- 
2.27.0

[PATCH v7 2/5] added an extra key column (alt now on keyboard by default), added cyrillic keymap, added a toggle to enable/disable overlays, added quick toggle buttons to toggle primary two layers. Added some configurability using command line parameters and environment variables. Export this patch

---
 README.md     |  34 ++++++++--
 layout.sxmo.h | 168 ++++++++++++++++++++++++++++++++++++++++----------
 svkbd.c       | 164 +++++++++++++++++++++++++++++++-----------------
 3 files changed, 272 insertions(+), 94 deletions(-)

diff --git a/README.md b/README.md
index 71ebd1a..69124fa 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
SVKBD
=====

This is a simple virtual keyboard, intended to be used in environments,
where no keyboard is available.

@@ -9,8 +10,11 @@ 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-sxmo`, which is svkbd using an versatile
layout with multiple layers and overlays, and optimised for mobile devices.
It was designed for [Simple X Mobile](https://sr.ht/~mil/Sxmo).

You can create svkbd for additional layouts by doing:

	% make svkbd-$layout

@@ -20,25 +24,43 @@ This will take the file `layout.$layout.h` and create `svkbd-$layout`.
Usage
-----

	% svkbd-en
	% svkbd-sxmo

This will open svkbd at the bottom of the screen, showing the default
English layout.

	% svkbd-en -d
	% svkbd-sxmo -d

This tells svkbd-en to announce itself being a dock window, which then
This tells svkbd-sxmo 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-sxmo -g 400x200+1+1

This will start svkbd-en with a size of 400x200 and at the upper left
window corner.

You can enable layers on the fly 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.

The virtual keyboard comes 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.

Repository
----------

SXMO version:

	git clone https://git.sr.ht/~mil/sxmo-svkbd

Original upstream:

	git clone http://git.suckless.org/svkbd

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 6bfdc3d..4a51e44 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;

@@ -119,6 +119,8 @@ Bool sigtermd = False;
#include "config.h"
#include "layout.h"

static Key* layers[LAYERS];

void
motionnotify(XEvent *e)
{
@@ -207,6 +209,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);
		}
@@ -215,6 +226,7 @@ cleanup(void) {
	for (i = 0; i < SchemeLast; i++)
		free(scheme[i]);
	drw_sync(drw);
	drw_free(drw);
	XSync(dpy, False);
	drw_free(drw);
	XDestroyWindow(dpy, win);
@@ -269,7 +281,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);
@@ -319,17 +337,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) {
@@ -355,16 +362,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);
@@ -434,6 +435,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:
@@ -442,7 +449,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); }
@@ -469,7 +476,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);
		}
	}

@@ -497,7 +504,7 @@ unpress(Key *k, KeySym mod) {
		}
	}

	if (currentoverlay != -1) {
	if (enableoverlays && currentoverlay != -1) {
		if (releaseprotect) {
			releaseprotect = 0;
		} else {
@@ -513,7 +520,6 @@ run(void) {
	fd_set fds;
	struct timeval tv;
	double duration = 0.0;
	int cyclemodidx;


	xfd = ConnectionNumber(dpy);
@@ -525,6 +531,7 @@ run(void) {
	XFlush(dpy);

	while (running) {
		usleep(100000L);
		FD_ZERO(&fds);
		FD_SET(xfd, &fds);
		if (select(xfd + 1, &fds, NULL, NULL, &tv)) {
@@ -540,19 +547,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);
	}
}

@@ -716,14 +717,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));
@@ -732,29 +739,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); }
@@ -799,15 +796,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);
		strcpy(layer_names_list, layers_env);
	}


	for (i = 1; argv[i]; i++) {
		if(!strcmp(argv[i], "-v")) {
			die("svkbd-"VERSION", © 2006-2020 svkbd engineers,"
@@ -839,9 +879,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);
			strcpy(layer_names_list, argv[i+1]);
		}
	}

	init_layers(layer_names_list);

	if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
		fprintf(stderr, "warning: no locale support\n");
	if(!(dpy = XOpenDisplay(0)))
@@ -850,5 +899,6 @@ main(int argc, char *argv[]) {
	run();
	cleanup();
	XCloseDisplay(dpy);
	if (layer_names_list != NULL) free(layer_names_list);
	return 0;
}
-- 
2.27.0

[PATCH v7 3/5] 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. Export this patch

---
 README.md     |   7 ++++
 config.def.h  |   1 +
 layout.sxmo.h |  39 +++++++++++++++++-
 svkbd.c       | 107 +++++++++++++++++++++++++++++++++++++-------------
 4 files changed, 125 insertions(+), 29 deletions(-)

diff --git a/README.md b/README.md
index 69124fa..2704601 100644
--- a/README.md
+++ b/README.md
@@ -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
----------

diff --git a/config.def.h b/config.def.h
index fe404b4..4f8a346 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 4a51e44..f9fc1f2 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;

@@ -127,7 +130,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
@@ -218,7 +221,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);
		}
	}
@@ -250,18 +253,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]);
	}
@@ -312,7 +331,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) {
@@ -372,7 +391,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);
				}
@@ -383,7 +402,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);
				}
@@ -454,7 +473,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);
					}
@@ -481,7 +500,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;
@@ -489,13 +508,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;
@@ -631,7 +650,7 @@ setup(void) {
	if(!ww)
		ww = sw;
	if(!wh)
		wh = sh * rows / 16;
		wh = sh * rows / heightfactor;

	if(!wx)
		wx = 0;
@@ -642,7 +661,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;
@@ -699,10 +718,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;
@@ -717,23 +736,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();
}
@@ -746,7 +772,7 @@ togglelayer() {
		currentlayer = 1;
	}
	if (debug) { printf("Toggling layer %d\n", currentlayer); fflush(stdout); }
	memcpy(&keys, layers[currentlayer], sizeof(keys_en));
	setlayer();
	updatekeys();
	drawkeyboard();
}
@@ -757,7 +783,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]);
@@ -801,21 +827,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;
				}
@@ -828,17 +865,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");
@@ -846,8 +885,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,"
@@ -885,11 +927,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");
-- 
2.27.0

[PATCH v7 4/5] adding some minor error catching Export this patch

---
 svkbd.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/svkbd.c b/svkbd.c
index f9fc1f2..2ecd8d1 100644
--- a/svkbd.c
+++ b/svkbd.c
@@ -883,6 +883,7 @@ main(int argc, char *argv[]) {
	const char* layers_env = getenv("SVKBD_LAYERS");
	if (layers_env != NULL) {
		layer_names_list = malloc(128);
		if (!layer_names_list) die("memory allocation error\n");
		strcpy(layer_names_list, layers_env);
	}
	const char* heightfactor_s = getenv("SVKBD_HEIGHTFACTOR");
@@ -926,7 +927,10 @@ main(int argc, char *argv[]) {
		} else if(!strcmp(argv[i], "-l")) {
			if(i >= argc - 1)
				continue;
			if (layer_names_list == NULL) layer_names_list = malloc(128);
			if (layer_names_list == NULL) {
				layer_names_list = malloc(128);
				if (!layer_names_list) die("memory allocation error\n");
			}
			strcpy(layer_names_list, argv[++i]);
		} else if(!strcmp(argv[i], "-s")) {
			if(i >= argc - 1)
@@ -942,6 +946,8 @@ main(int argc, char *argv[]) {
		}
	}

	if (heightfactor <= 0) die("height factor must be a positive integer\n");

	init_layers(layer_names_list, initial_layer_name);

	if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
@@ -952,6 +958,6 @@ main(int argc, char *argv[]) {
	run();
	cleanup();
	XCloseDisplay(dpy);
	if (layer_names_list != NULL) free(layer_names_list);
	free(layer_names_list);
	return 0;
}
-- 
2.27.0

[PATCH v7 5/5] fixed the select() logic in run() to handle sigterm situations Export this patch

---
 svkbd.c | 40 ++++++++++++++++++----------------------
 1 file changed, 18 insertions(+), 22 deletions(-)

diff --git a/svkbd.c b/svkbd.c
index 2ecd8d1..8edcaea 100644
--- a/svkbd.c
+++ b/svkbd.c
@@ -205,27 +205,6 @@ void
cleanup(void) {
	int i;

	// E.g. Generally in scripts we call SIGTERM on svkbd in which case
	//      if the user is holding for example the enter key (to execute
	//      the kill or script that does the kill), that causes an issue
	//      since then X doesn't know the keyup is never coming.. (since
	//      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 < numkeys; i++) {
			XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
		}
	}

	for (i = 0; i < SchemeLast; i++)
		free(scheme[i]);
	drw_sync(drw);
@@ -539,6 +518,7 @@ run(void) {
	fd_set fds;
	struct timeval tv;
	double duration = 0.0;
	int i, r;


	xfd = ConnectionNumber(dpy);
@@ -553,7 +533,8 @@ run(void) {
		usleep(100000L);
		FD_ZERO(&fds);
		FD_SET(xfd, &fds);
		if (select(xfd + 1, &fds, NULL, NULL, &tv)) {
		r = select(xfd + 1, &fds, NULL, NULL, &tv);
		if (r) {
			while (XPending(dpy)) {
				XNextEvent(dpy, &ev);
				if(handler[ev.type]) {
@@ -561,6 +542,7 @@ run(void) {
				}
			}
		} else {
			//time-out expired without anything interesting happening, check for long-presses
			if (ispressing && ispressingkeysym) {
				duration = get_press_duration();
				if (debug == 2) { printf("%f\n", duration); fflush(stdout); }
@@ -573,6 +555,20 @@ run(void) {
				}
			}
		}
        if (r == -1 || sigtermd) {
            // an error occurred  or we received a signal
            // E.g. Generally in scripts we want to call SIGTERM on svkbd in which case
            //      if the user is holding for example the enter key (to execute
            //      the kill or script that does the kill), that causes an issue
            //      since then X doesn't know the keyup is never coming.. (since
            //      process will be dead before finger lifts - in that case we
            //      just trigger out fake up presses for all keys
            if (debug) { printf("signal received, releasing all keys"); fflush(stdout); }
            for (i = 0; i < numkeys; i++) {
                XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
            }
            running = False;
        }
	}
}

-- 
2.27.0