Added overlays (appearing on long press), multiple layer support (rather than j… | |
git clone git://git.suckless.org/svkbd | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 48994f125e2fdaee15f0724e4f97c24443f9eb96 | |
parent b488ae6410567d64a48eb676f7038c68f3eb0cc2 | |
Author: Maarten van Gompel <[email protected]> | |
Date: Sun, 2 Aug 2020 15:46:14 +0200 | |
Added overlays (appearing on long press), multiple layer support (rather than j… | |
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 a… | |
+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 ac… | |
+ record_press_begin(k->keysym); | |
} | |
- XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, k->keysym), True,… | |
+ } 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 simulat… | |
+ record_press_begin(k->keysym); | |
+ } | |
+ } else { | |
+ if (debug) { printf("Simulating press: %ld\n", k->keys… | |
+ for(i = 0; i < LENGTH(keys); i++) { | |
+ if(keys[i].pressed && IsModifierKey(keys[i].ke… | |
+ 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].ke… | |
+ 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.t… | |
+} | |
+ | |
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 == isp… | |
+ if (currentoverlay == -1) { | |
+ if (get_press_duration() < overlay_delay) { | |
+ if (debug) { printf("Delayed simulation of pre… | |
+ //simulate the press event, as we postponed it… | |
+ for(i = 0; i < LENGTH(keys); i++) { | |
+ if(keys[i].pressed && IsModifierKey(ke… | |
+ simulate_keypress(keys[i].keys… | |
+ } | |
+ } | |
+ 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); ffl… | |
+ } else { | |
+ printf("Simulation of release (all keys)"); fflush(std… | |
+ } | |
+ } | |
+ | |
+ | |
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 handl… | |
} | |
} | |
+ } else { | |
+ if (ispressing && ispressingkeysym) { | |
+ duration = get_press_duration(); | |
+ if (debug == 2) { printf("%f\n", duration); ff… | |
+ if (get_press_duration() >= overlay_delay) { | |
+ if (debug) { printf("press duration %f… | |
+ cyclemodidx = iscyclemod(ispressingkey… | |
+ if (cyclemodidx != -1) { | |
+ cyclemod(); | |
+ } else { | |
+ showoverlay(hasoverlay(ispress… | |
+ } | |
+ 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 s… | |
+ 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… | |
+ 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(std… | |
+ 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); fflu… | |
+ 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(std… | |
+ 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; | |
} | |
- |