svkbd

simple virtual keyboard
git clone git://git.suckless.org/svkbd
Log | Files | Refs | README | LICENSE

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:
Mconfig.def.h | 9+++++----
Mlayout.sxmo.h | 291++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msvkbd.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; } -