commit 48994f125e2fdaee15f0724e4f97c24443f9eb96
Author:     Maarten van Gompel <proy...@anaproy.nl>
AuthorDate: Sun Aug 2 15:46:14 2020 +0200
Commit:     Hiltjo Posthuma <hil...@codemadness.org>
CommitDate: Sun Aug 2 18:00:31 2020 +0200

    Added overlays (appearing on long press), multiple layer support (rather 
than just a toggle) with new layers, style changes

diff --git a/config.def.h b/config.def.h
index 0d4172d..42d0c38 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,11 +1,12 @@
 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 e3f70b0..9f09334 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 */
@@ -287,43 +308,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;
@@ -331,7 +435,7 @@ unpress(Key *k, KeySym mod) {
        if(k != NULL) {
                switch(k->keysym) {
                case XK_Cancel:
-                       togglelayer();
+                       cyclelayer();
                        break;
                case XK_Break:
                  running = False;
@@ -340,11 +444,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;
@@ -352,22 +487,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
@@ -376,11 +515,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);
@@ -395,7 +537,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);
        }
 }
 
@@ -428,10 +588,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++)
@@ -467,9 +651,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);
@@ -491,6 +675,7 @@ setup(void) {
        XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh,
                        ch);
 
+       XFree(keysyms);
        XFree(ch);
        XFree(wmh);
        XFree(str.value);
@@ -534,18 +719,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)
 {
@@ -585,6 +836,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]);
                }
@@ -600,4 +855,3 @@ main(int argc, char *argv[]) {
        XCloseDisplay(dpy);
        return 0;
 }
-

Reply via email to