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

This commit is contained in:
Maarten van Gompel 2020-08-02 15:46:14 +02:00 committed by Hiltjo Posthuma
parent b488ae6410
commit 48994f125e
3 changed files with 583 additions and 57 deletions

View File

@ -1,11 +1,12 @@
static const Bool wmborder = True; static const Bool wmborder = True;
static int fontsize = 16; static int fontsize = 20;
static double overlay_delay = 1.0;
static const char *fonts[] = { static const char *fonts[] = {
"monospace:size=16" "DejaVu Sans:bold:size=20"
}; };
static const char *colors[SchemeLast][2] = { static const char *colors[SchemeLast][2] = {
/* fg bg */ /* fg bg */
[SchemeNorm] = { "#58a7c6", "#14313d" }, [SchemeNorm] = { "#ffffff", "#14313d" },
[SchemePress] = { "#ffffff", "#005577" }, [SchemePress] = { "#ffffff", "#000000" },
[SchemeHighlight] = { "#58a7c6", "#005577" }, [SchemeHighlight] = { "#58a7c6", "#005577" },
}; };

View File

@ -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_q, 1 },
{ 0, XK_w, 1 }, { 0, XK_w, 1 },
{ 0, XK_e, 1 }, { 0, XK_e, 1 },
@ -23,7 +24,7 @@ static Key keys_en[40] = {
{ 0, XK_j, 1 }, { 0, XK_j, 1 },
{ 0, XK_k, 1 }, { 0, XK_k, 1 },
{ 0, XK_l, 1 }, { 0, XK_l, 1 },
{ ";:", XK_colon, 1 }, { "/?", XK_slash, 1 },
/*{ "'", XK_apostrophe, 2 },*/ /*{ "'", XK_apostrophe, 2 },*/
{ 0 }, /* New row */ { 0 }, /* New row */
@ -37,7 +38,7 @@ static Key keys_en[40] = {
{ 0, XK_m, 1 }, { 0, XK_m, 1 },
/*{ "/?", XK_slash, 1 },*/ /*{ "/?", XK_slash, 1 },*/
{ "Tab", XK_Tab, 1 }, { "Tab", XK_Tab, 1 },
{ "Bksp", XK_BackSpace, 2 }, { "Bksp", XK_BackSpace, 2 },
{ 0 }, /* New row */ { 0 }, /* New row */
{ "", XK_Cancel, 1}, { "", XK_Cancel, 1},
@ -53,7 +54,214 @@ static Key keys_en[40] = {
{ "↲ Enter", XK_Return, 2 }, { "↲ 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 }, { "1!", XK_1, 1 },
{ "2@", XK_2, 1 }, { "2@", XK_2, 1 },
{ "3#", XK_3, 1 }, { "3#", XK_3, 1 },
@ -80,7 +288,55 @@ static Key keys_symbols[40] = {
{ 0 }, /* New row */ { 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_Home, 1 },
{ "", XK_Left, 1 }, { "", XK_Left, 1 },
{ "", XK_Right, 1 }, { "", XK_Right, 1 },
@ -88,22 +344,37 @@ static Key keys_symbols[40] = {
{ "", XK_Next, 1 }, { "", XK_Next, 1 },
{ "", XK_Prior, 1 }, { "", XK_Prior, 1 },
{ "Tab", XK_Tab, 1 }, { "Tab", XK_Tab, 1 },
{ "Bksp", XK_BackSpace, 2 }, { "Bksp", XK_BackSpace, 2 },
{ 0 }, /* New row */ { 0 }, /* New row */
{ "", XK_Cancel, 1}, { "", XK_Cancel, 1},
{ "Shft", XK_Shift_L, 1 }, { "Shft", XK_Shift_L, 1 },
/*{ "L", XK_Left, 1 },*/
{ "", XK_Down, 1 }, { "", XK_Down, 1 },
{ "", XK_Up, 1 }, { "", XK_Up, 1 },
/*{ "R", XK_Right, 1 },*/
{ "", XK_space, 2 }, { "", XK_space, 2 },
{ "Esc", XK_Escape, 1 }, { "Esc", XK_Escape, 1 },
{ "Ctrl", XK_Control_L, 1 }, { "Ctrl", XK_Control_L, 1 },
/*{ "Alt", XK_Alt_L, 1 },*/
{ "↲ Enter", XK_Return, 2 }, { "↲ 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[] = { Buttonmod buttonmods[] = {
{ XK_Shift_L, Button2 }, { XK_Shift_L, Button2 },
{ XK_Alt_L, Button3 }, { XK_Alt_L, Button3 },

340
svkbd.c
View File

@ -8,6 +8,8 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/keysymdef.h>
#include <X11/XF86keysym.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
@ -18,16 +20,19 @@
#include <X11/extensions/Xinerama.h> #include <X11/extensions/Xinerama.h>
#endif #endif
#include <signal.h> #include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/time.h>
#include "drw.h" #include "drw.h"
#include "util.h" #include "util.h"
/* macros */ /* macros */
#define LENGTH(x) (sizeof x / sizeof x[0]) #define LENGTH(x) (sizeof x / sizeof x[0])
#define TEXTW(X) (drw_fontset_getwidth(drw, (X))) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)))
#define STRINGTOKEYSYM(X) (XStringToKeySym(X))
/* enums */ /* enums */
enum { SchemeNorm, SchemePress, SchemeHighlight, SchemeLast }; enum { SchemeNorm, SchemePress, SchemeHighlight, SchemeLast };
@ -62,11 +67,18 @@ static void drawkeyboard(void);
static void drawkey(Key *k); static void drawkey(Key *k);
static void expose(XEvent *e); static void expose(XEvent *e);
static Key *findkey(int x, int y); static Key *findkey(int x, int y);
static int iscyclemod(KeySym keysym);
static void leavenotify(XEvent *e); static void leavenotify(XEvent *e);
static void press(Key *k, KeySym mod); static void press(Key *k, KeySym mod);
static double get_press_duration();
static void run(void); static void run(void);
static void setup(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 unpress(Key *k, KeySym mod);
static void updatekeys(); static void updatekeys();
@ -87,11 +99,20 @@ static Window root, win;
static Clr* scheme[SchemeLast]; static Clr* scheme[SchemeLast];
static Bool running = True, isdock = False; static Bool running = True, isdock = False;
static KeySym pressedmod = 0; 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 int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0;
static char *name = "svkbd"; static char *name = "svkbd";
static int debug = 0;
static KeySym ispressingkeysym;
Bool ispressing = False; Bool ispressing = False;
Bool baselayer = True;
Bool sigtermd = False; Bool sigtermd = False;
/* configuration, allows nested code to access above variables */ /* 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 void
leavenotify(XEvent *e) { leavenotify(XEvent *e) {
if (currentoverlay != -1) {
hideoverlay();
}
unpress(NULL, 0); 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 void
press(Key *k, KeySym mod) { press(Key *k, KeySym mod) {
int i; int i;
int overlayidx = -1;
k->pressed = !k->pressed; k->pressed = !k->pressed;
if(!IsModifierKey(k->keysym)) { if (debug) { printf("Begin press: %ld\n", k->keysym); fflush(stdout); }
for(i = 0; i < LENGTH(keys); i++) { pressbegin.tv_sec = 0;
if(keys[i].pressed && IsModifierKey(keys[i].keysym)) { pressbegin.tv_usec = 0;
XTestFakeKeyEvent(dpy, ispressingkeysym = 0;
XKeysymToKeycode(dpy, keys[i].keysym),
True, 0);
}
}
pressedmod = mod;
if(pressedmod) {
XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, mod),
True, 0);
}
XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, k->keysym), True, 0);
for(i = 0; i < LENGTH(keys); i++) { int cm = iscyclemod(k->keysym);
if(keys[i].pressed && IsModifierKey(keys[i].keysym)) { if (cm != -1) {
XTestFakeKeyEvent(dpy, if (!pressbegin.tv_sec && !pressbegin.tv_usec) {
XKeysymToKeycode(dpy, keys[i].keysym), //record the begin of the press, don't simulate the actual keypress yet
False, 0); record_press_begin(k->keysym);
}
} 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)) {
simulate_keyrelease(keys[i].keysym);
}
} }
} }
} }
drawkey(k); 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 void
unpress(Key *k, KeySym mod) { unpress(Key *k, KeySym mod) {
int i; int i;
@ -331,7 +435,7 @@ unpress(Key *k, KeySym mod) {
if(k != NULL) { if(k != NULL) {
switch(k->keysym) { switch(k->keysym) {
case XK_Cancel: case XK_Cancel:
togglelayer(); cyclelayer();
break; break;
case XK_Break: case XK_Break:
running = False; 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++) { for(i = 0; i < LENGTH(keys); i++) {
if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) { if(keys[i].pressed && !IsModifierKey(keys[i].keysym)) {
XTestFakeKeyEvent(dpy, simulate_keyrelease(keys[i].keysym);
XKeysymToKeycode(dpy, keys[i].keysym),
False, 0);
keys[i].pressed = 0; keys[i].pressed = 0;
drawkey(&keys[i]); drawkey(&keys[i]);
break; break;
@ -352,22 +487,26 @@ unpress(Key *k, KeySym mod) {
} }
if(i != LENGTH(keys)) { if(i != LENGTH(keys)) {
if(pressedmod) { if(pressedmod) {
XTestFakeKeyEvent(dpy, simulate_keyrelease(mod);
XKeysymToKeycode(dpy, pressedmod),
False, 0);
} }
pressedmod = 0; pressedmod = 0;
for(i = 0; i < LENGTH(keys); i++) { for(i = 0; i < LENGTH(keys); i++) {
if(keys[i].pressed) { if(keys[i].pressed) {
XTestFakeKeyEvent(dpy, simulate_keyrelease(keys[i].keysym);
XKeysymToKeycode(dpy,
keys[i].keysym), False, 0);
keys[i].pressed = 0; keys[i].pressed = 0;
drawkey(&keys[i]); drawkey(&keys[i]);
} }
} }
} }
if (currentoverlay != -1) {
if (releaseprotect) {
releaseprotect = 0;
} else {
hideoverlay();
}
}
} }
void void
@ -376,11 +515,14 @@ run(void) {
int xfd; int xfd;
fd_set fds; fd_set fds;
struct timeval tv; struct timeval tv;
double duration = 0.0;
int cyclemodidx;
xfd = ConnectionNumber(dpy); xfd = ConnectionNumber(dpy);
tv.tv_usec = 0; tv.tv_usec = 0;
tv.tv_sec = 2; tv.tv_sec = 1;
//XSync(dpy, False); //XSync(dpy, False);
XFlush(dpy); XFlush(dpy);
@ -395,7 +537,25 @@ run(void) {
(handler[ev.type])(&ev); /* call handler */ (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); sw = DisplayWidth(dpy, screen);
sh = DisplayHeight(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))) if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
die("no fonts could be loaded."); 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 */ /* init appearance */
for (j = 0; j < SchemeLast; j++) for (j = 0; j < SchemeLast; j++)
@ -467,9 +651,9 @@ setup(void) {
wa.border_pixel = scheme[SchemeNorm][ColFg].pixel; wa.border_pixel = scheme[SchemeNorm][ColFg].pixel;
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0, win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0,
CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBorderPixel | CWOverrideRedirect | CWBorderPixel |
CWBackingPixel, &wa); CWBackingPixel, &wa);
XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask| XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask|
ButtonPressMask|ExposureMask|LeaveWindowMask| ButtonPressMask|ExposureMask|LeaveWindowMask|
PointerMotionMask); PointerMotionMask);
@ -491,6 +675,7 @@ setup(void) {
XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh, XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh,
ch); ch);
XFree(keysyms);
XFree(ch); XFree(ch);
XFree(wmh); XFree(wmh);
XFree(str.value); XFree(str.value);
@ -534,18 +719,84 @@ updatekeys() {
void void
usage(char *argv0) { 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); exit(1);
} }
void void
togglelayer() { cyclelayer() {
memcpy(&keys, baselayer ? &keys_symbols : &keys_en, sizeof(keys_en)); 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(); updatekeys();
drawkeyboard(); drawkeyboard();
baselayer = !baselayer;
} }
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();
XSync(dpy, False);
}
void
hideoverlay() {
if (debug) { printf("Hiding overlay %d\n", currentoverlay); fflush(stdout); }
currentoverlay = -1;
overlaykeysym = 0;
currentlayer = -1;
cyclelayer();
}
void void
sigterm(int sig) sigterm(int sig)
{ {
@ -585,6 +836,10 @@ main(int argc, char *argv[]) {
if(bitm & YNegative && wy == 0) if(bitm & YNegative && wy == 0)
wy = -1; wy = -1;
i++; 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")) { } else if(!strcmp(argv[i], "-h")) {
usage(argv[0]); usage(argv[0]);
} }
@ -600,4 +855,3 @@ main(int argc, char *argv[]) {
XCloseDisplay(dpy); XCloseDisplay(dpy);
return 0; return 0;
} }