commit 48994f125e2fdaee15f0724e4f97c24443f9eb96
parent b488ae6410567d64a48eb676f7038c68f3eb0cc2
Author: Maarten van Gompel <proycon@anaproy.nl>
Date: Sun, 2 Aug 2020 15:46:14 +0200
Added overlays (appearing on long press), multiple layer support (rather than just a toggle) with new layers, style changes
Diffstat:
M | config.def.h | | | 9 | +++++---- |
M | layout.sxmo.h | | | 291 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
M | svkbd.c | | | 338 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
3 files changed, 582 insertions(+), 56 deletions(-)
diff --git 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
@@ -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
@@ -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;
}
-