added an extra key column (alt now on keyboard by default), added cyrillic keymap, added a toggle to enable/disable overlays, added quick toggle buttons to toggle primary two layers. Added some configurability using command line parameters and environment variables.

This commit is contained in:
Maarten van Gompel 2020-08-02 15:46:15 +02:00 committed by Hiltjo Posthuma
parent 48994f125e
commit 99935775af
3 changed files with 267 additions and 96 deletions

31
README
View File

@ -1,5 +1,6 @@
SVKBD SVKBD
===== =====
This is a simple virtual keyboard, intended to be used in environments, This is a simple virtual keyboard, intended to be used in environments,
where no keyboard is available. where no keyboard is available.
@ -9,8 +10,11 @@ Installation
$ make $ make
$ make install $ make install
This will create by default `svkbd-en`, which is svkbd using an English This will create by default `svkbd-sxmo`, which is svkbd using an versatile
keyboard layout. You can create svkbd for additional layouts by doing: layout with multiple layers and overlays, and optimised for mobile devices.
It was designed for [Simple X Mobile](https://sr.ht/~mil/Sxmo).
You can create svkbd for additional layouts by doing:
$ make LAYOUT=$layout $ make LAYOUT=$layout
@ -20,25 +24,36 @@ This will take the file `layout.$layout.h` and create `svkbd-$layout`.
Usage Usage
----- -----
$ svkbd-en % svkbd-sxmo
This will open svkbd at the bottom of the screen, showing the default This will open svkbd at the bottom of the screen, showing the default
English layout. English layout.
$ svkbd-en -d % svkbd-sxmo -d
This tells svkbd-en to announce itself being a dock window, which then This tells svkbd-sxmo to announce itself being a dock window, which then
is managed differently between different window managers. If using dwm is managed differently between different window managers. If using dwm
and the dock patch, then this will make svkbd being managed by dwm and and the dock patch, then this will make svkbd being managed by dwm and
some space of the screen being reserved for it. some space of the screen being reserved for it.
$ svkbd-en -g 400x200+1+1 % svkbd-sxmo -g 400x200+1+1
This will start svkbd-en with a size of 400x200 and at the upper left This will start svkbd-en with a size of 400x200 and at the upper left
window corner. window corner.
You can enable layers on the fly through either the ``-l`` flag or through the ``SVKBD_LAYERS`` environment variable.
They both take a comma separated list of layer names (as defined in your ``layout.*.h``). Use the ``↺`` button in the
bottom-left to cycle through all the layers.
The virtual keyboard comes with overlays that will show when certain keys are hold pressed for a longer time. For
example, a long press on the ``a`` key will enable an overview showing all kinds of diacritic combinations for ``a``.
Overlay functionality interferes with the ability to hold a key and have it outputted repeatedly. You can disable
overlay functionality with the ``-O`` flag or by setting the environment variable ``SVKBD_ENABLEOVERLAYS=0``. There is
also a key on the function layer of the keyboard itself to enable/disable this behaviour on the fly. Its label shows
``≅`` when the overlay functionality is enabled and ``≇`` when not.
Repository Repository
---------- ----------
git clone https://git.suckless.org/svkbd git clone http://git.suckless.org/svkbd

View File

@ -1,7 +1,8 @@
#define KEYS 40 #define KEYS 43
static Key keys[KEYS] = { NULL }; static Key keys[KEYS] = { NULL };
static Key keys_en[KEYS] = { static Key keys_en[KEYS] = {
{ "Esc", XK_Escape, 1 },
{ 0, XK_q, 1 }, { 0, XK_q, 1 },
{ 0, XK_w, 1 }, { 0, XK_w, 1 },
{ 0, XK_e, 1 }, { 0, XK_e, 1 },
@ -15,6 +16,7 @@ static Key keys_en[KEYS] = {
{ 0 }, /* New row */ { 0 }, /* New row */
{ "'\"", XK_apostrophe, 1 },
{ 0, XK_a, 1 }, { 0, XK_a, 1 },
{ 0, XK_s, 1 }, { 0, XK_s, 1 },
{ 0, XK_d, 1 }, { 0, XK_d, 1 },
@ -25,10 +27,10 @@ static Key keys_en[KEYS] = {
{ 0, XK_k, 1 }, { 0, XK_k, 1 },
{ 0, XK_l, 1 }, { 0, XK_l, 1 },
{ "/?", XK_slash, 1 }, { "/?", XK_slash, 1 },
/*{ "'", XK_apostrophe, 2 },*/
{ 0 }, /* New row */ { 0 }, /* New row */
{ "123", XK_Mode_switch, 1 },
{ 0, XK_z, 1 }, { 0, XK_z, 1 },
{ 0, XK_x, 1 }, { 0, XK_x, 1 },
{ 0, XK_c, 1 }, { 0, XK_c, 1 },
@ -36,25 +38,21 @@ static Key keys_en[KEYS] = {
{ 0, XK_b, 1 }, { 0, XK_b, 1 },
{ 0, XK_n, 1 }, { 0, XK_n, 1 },
{ 0, XK_m, 1 }, { 0, XK_m, 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},
{ "Shft", XK_Shift_L, 1 }, { "Shift", XK_Shift_L, 2 },
/*{ "L", XK_Left, 1 },*/ { "Ctrl", XK_Control_L, 1 },
{ "Alt", XK_Alt_L, 1 },
{ "", XK_space, 2 },
{ "", XK_Down, 1 }, { "", XK_Down, 1 },
{ "", XK_Up, 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 }, { "↲ Enter", XK_Return, 2 },
}; };
#define OVERLAYS 165 #define OVERLAYS 197
static Key overlay[OVERLAYS] = { static Key overlay[OVERLAYS] = {
{ 0, XK_a }, //Overlay for a { 0, XK_a }, //Overlay for a
//--- //---
@ -195,6 +193,58 @@ static Key overlay[OVERLAYS] = {
{ 0, XK_r }, //New overlay { 0, XK_r }, //New overlay
//--- //---
{ "ř", XK_rcaron }, { "ř", XK_rcaron },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_softsign }, //New overlay
//---
{ "ъ", XK_Cyrillic_hardsign },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_ie }, //New overlay
//---
{ "ё", XK_Cyrillic_io },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_e }, //New overlay
//---
{ "Є", XK_Ukrainian_ie },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_i }, //New overlay
//---
{ "і", XK_Ukrainian_i },
{ "ї", XK_Ukrainian_yi },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_u }, //New overlay
//---
{ "ў", XK_Byelorussian_shortu },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_shorti }, //New overlay
//---
{ "ј", XK_Cyrillic_je },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_el }, //New overlay
//---
{ "љ", XK_Cyrillic_lje },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_en }, //New overlay
//---
{ "њ", XK_Cyrillic_nje },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_tse }, //New overlay
//---
{ "џ", XK_Cyrillic_dzhe },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//---
{ 0, XK_Cyrillic_che }, //New overlay
//---
{ "ћ", XK_Serbian_tshe },
{ "ђ", XK_Serbian_dje },
{ 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */ { 0, XK_Cancel }, /* XK_Cancel signifies overlay boundary */
//--- //---
{ "🙂", 0x101f642 }, //emoji overlay { "🙂", 0x101f642 }, //emoji overlay
@ -262,6 +312,7 @@ static Key overlay[OVERLAYS] = {
static Key keys_symbols[KEYS] = { static Key keys_symbols[KEYS] = {
{ "Esc", XK_Escape, 1 },
{ "1!", XK_1, 1 }, { "1!", XK_1, 1 },
{ "2@", XK_2, 1 }, { "2@", XK_2, 1 },
{ "3#", XK_3, 1 }, { "3#", XK_3, 1 },
@ -285,31 +336,34 @@ static Key keys_symbols[KEYS] = {
{ ".>", XK_period, 1 }, { ".>", XK_period, 1 },
{ "/?", XK_slash, 1 }, { "/?", XK_slash, 1 },
{ "\\|", XK_backslash, 1 }, { "\\|", XK_backslash, 1 },
{ ";:", XK_colon, 1 },
{ 0 }, /* New row */ { 0 }, /* New row */
{ "abc", XK_Mode_switch, 1 },
{ "", 0x101f642, 1 }, { "", 0x101f642, 1 },
{ "", XK_Home, 1 }, { "", XK_Home, 1 },
{ "", XK_Left, 1 }, { "", XK_Left, 1 },
{ "", XK_Right, 1 }, { "", XK_Right, 1 },
{ "", XK_End, 1 }, { "", XK_End, 1 },
{ "", XK_Next, 1 }, { "", XK_Next, 1 },
{ ";:", XK_colon, 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 }, { "Shift", XK_Shift_L, 2 },
{ "Ctrl", XK_Control_L, 1 },
{ "Alt", XK_Alt_L, 1 },
{ "", XK_space, 2 },
{ "", XK_Down, 1 }, { "", XK_Down, 1 },
{ "", XK_Up, 1 }, { "", XK_Up, 1 },
{ "", XK_space, 2 },
{ "Esc", XK_Escape, 1 },
{ "Ctrl", XK_Control_L, 1 },
{ "↲ Enter", XK_Return, 2 }, { "↲ Enter", XK_Return, 2 },
}; };
static Key keys_functions[KEYS] = { static Key keys_functions[KEYS] = {
{ "Esc", XK_Escape, 1 },
{ "F1", XK_F1, 1 }, { "F1", XK_F1, 1 },
{ "F2", XK_F2, 1 }, { "F2", XK_F2, 1 },
{ "F3", XK_F3, 1 }, { "F3", XK_F3, 1 },
@ -323,6 +377,7 @@ static Key keys_functions[KEYS] = {
{ 0 }, /* New row */ { 0 }, /* New row */
{ "", XK_KP_Insert, 1 },
{ "", XF86XK_AudioPlay, 1 }, { "", XF86XK_AudioPlay, 1 },
{ "", XF86XK_AudioRecord, 1 }, { "", XF86XK_AudioRecord, 1 },
{ "", XF86XK_AudioStop, 1 }, { "", XF86XK_AudioStop, 1 },
@ -336,6 +391,7 @@ static Key keys_functions[KEYS] = {
{ 0 }, /* New row */ { 0 }, /* New row */
{ "abc", XK_Mode_switch, 1 },
{ "Del", XK_Delete, 1 }, { "Del", XK_Delete, 1 },
{ "", XK_Home, 1 }, { "", XK_Home, 1 },
{ "", XK_Left, 1 }, { "", XK_Left, 1 },
@ -348,30 +404,80 @@ static Key keys_functions[KEYS] = {
{ 0 }, /* New row */ { 0 }, /* New row */
{ "", XK_Cancel, 1}, { "", XK_Cancel, 1},
{ "Shft", XK_Shift_L, 1 }, { "Shift", XK_Shift_L, 2 },
{ "Ctrl", XK_Control_L, 1 },
{ "Alt", XK_Alt_L, 1 },
{ "", XK_space, 2 },
{ "", XK_Down, 1 }, { "", XK_Down, 1 },
{ "", XK_Up, 1 }, { "", XK_Up, 1 },
{ "", XK_space, 2 },
{ "Esc", XK_Escape, 1 },
{ "Ctrl", XK_Control_L, 1 },
{ "↲ Enter", XK_Return, 2 }, { "↲ Enter", XK_Return, 2 },
}; };
#define LAYERS 3 static Key keys_ru[KEYS] = {
static Key* layers[LAYERS] = { { "и", XK_Cyrillic_shorti, 1 },
{ "ц", XK_Cyrillic_tse, 1 },
{ "у", XK_Cyrillic_u, 1 },
{ "к", XK_Cyrillic_ka, 1 },
{ "е", XK_Cyrillic_ie, 1 },
{ "н", XK_Cyrillic_en, 1 },
{ "г", XK_Cyrillic_ghe, 1 },
{ "ш", XK_Cyrillic_sha, 1 },
{ "щ", XK_Cyrillic_shcha, 1 },
{ "з", XK_Cyrillic_ze, 1 },
{ "х", XK_Cyrillic_ha, 1 },
{ 0 }, /* New row */
{ "ф", XK_Cyrillic_ef, 1 },
{ "ы", XK_Cyrillic_yeru, 1 },
{ "в", XK_Cyrillic_ve, 1 },
{ "а", XK_Cyrillic_a, 1 },
{ "п", XK_Cyrillic_pe, 1 },
{ "о", XK_Cyrillic_o, 1 },
{ "л", XK_Cyrillic_el, 1 },
{ "д", XK_Cyrillic_de, 1 },
{ "ж", XK_Cyrillic_zhe, 1 },
{ "э", XK_Cyrillic_e, 1 },
{ "ю", XK_Cyrillic_yu, 1 },
{ 0 }, /* New row */
{ "123", XK_Mode_switch, 1 },
{ "я", XK_Cyrillic_ya, 1 },
{ "ч", XK_Cyrillic_che, 1 },
{ "с", XK_Cyrillic_es, 1 },
{ "м", XK_Cyrillic_em, 1 },
{ "и", XK_Cyrillic_i, 1 },
{ "т", XK_Cyrillic_te, 1 },
{ "ь", XK_Cyrillic_softsign, 1 },
{ "б", XK_Cyrillic_be, 1 },
{ "⌫Bksp", XK_BackSpace, 2 },
{ 0 }, /* New row */
{ "", XK_Cancel, 1},
{ "Shift", XK_Shift_L, 2 },
{ "Ctrl", XK_Control_L, 1 },
{ "Alt", XK_Alt_L, 1 },
{ "", XK_space, 2 },
{ "", XK_Down, 1 },
{ "", XK_Up, 1 },
{ "↲ Enter", XK_Return, 2 },
};
#define LAYERS 4
static char* layer_names[LAYERS] = {
"en",
"symbols",
"functions",
"ru",
};
static Key* available_layers[LAYERS] = {
keys_en, keys_en,
keys_symbols, keys_symbols,
keys_functions, keys_functions,
}; keys_ru
#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 },
}; };

164
svkbd.c
View File

@ -67,7 +67,6 @@ 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 double get_press_duration();
@ -76,9 +75,9 @@ static void setup(void);
static void simulate_keypress(KeySym keysym); static void simulate_keypress(KeySym keysym);
static void simulate_keyrelease(KeySym keysym); static void simulate_keyrelease(KeySym keysym);
static void showoverlay(int idx); static void showoverlay(int idx);
static void cyclemod();
static void hideoverlay(); static void hideoverlay();
static void cyclelayer(); static void cyclelayer();
static void togglelayer();
static void unpress(Key *k, KeySym mod); static void unpress(Key *k, KeySym mod);
static void updatekeys(); static void updatekeys();
@ -101,14 +100,15 @@ static Bool running = True, isdock = False;
static KeySym pressedmod = 0; static KeySym pressedmod = 0;
static struct timeval pressbegin; static struct timeval pressbegin;
static int currentlayer = 0; static int currentlayer = 0;
static int enableoverlays = 1;
static int currentoverlay = -1; // -1 = no overlay static int currentoverlay = -1; // -1 = no overlay
static int currentcyclemod = 0;
static KeySym overlaykeysym = 0; //keysym for which the overlay is presented 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 releaseprotect = 0; //set to 1 after overlay is shown, protecting against immediate release
static int tmp_keycode = 1; 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 int debug = 0;
static int numlayers = 0;
static KeySym ispressingkeysym; static KeySym ispressingkeysym;
@ -122,6 +122,8 @@ Bool sigtermd = False;
#endif #endif
#include LAYOUT #include LAYOUT
static Key* layers[LAYERS];
void void
motionnotify(XEvent *e) motionnotify(XEvent *e)
{ {
@ -210,6 +212,15 @@ cleanup(void) {
// process will be dead before finger lifts - in that case we // process will be dead before finger lifts - in that case we
// just trigger out fake up presses for all keys // just trigger out fake up presses for all keys
if (sigtermd) { if (sigtermd) {
//handle last pending events
XEvent ev;
while (XPending(dpy)) {
XNextEvent(dpy, &ev);
if(handler[ev.type]) {
(handler[ev.type])(&ev); /* call handler */
}
}
if (debug) { printf("Cleanup: simulating key release\n"); fflush(stdout); }
for (i = 0; i < LENGTH(keys); i++) { for (i = 0; i < LENGTH(keys); i++) {
XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0); XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0);
} }
@ -218,6 +229,7 @@ cleanup(void) {
for (i = 0; i < SchemeLast; i++) for (i = 0; i < SchemeLast; i++)
free(scheme[i]); free(scheme[i]);
drw_sync(drw); drw_sync(drw);
drw_free(drw);
XSync(dpy, False); XSync(dpy, False);
drw_free(drw); drw_free(drw);
XDestroyWindow(dpy, win); XDestroyWindow(dpy, win);
@ -272,7 +284,13 @@ drawkey(Key *k) {
drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1); drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1);
drw_rect(drw, k->x, k->y, k->w, k->h, 0, 0); drw_rect(drw, k->x, k->y, k->w, k->h, 0, 0);
if(k->label) { if (k->keysym == XK_KP_Insert) {
if (enableoverlays) {
l = "";
} else {
l = "";
}
} else if(k->label) {
l = k->label; l = k->label;
} else { } else {
l = XKeysymToString(k->keysym); l = XKeysymToString(k->keysym);
@ -322,17 +340,6 @@ hasoverlay(KeySym keysym) {
return -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) { if (currentoverlay != -1) {
@ -358,16 +365,10 @@ press(Key *k, KeySym mod) {
pressbegin.tv_usec = 0; pressbegin.tv_usec = 0;
ispressingkeysym = 0; ispressingkeysym = 0;
int cm = iscyclemod(k->keysym); if(!IsModifierKey(k->keysym)) {
if (cm != -1) { if (enableoverlays && currentoverlay == -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(!IsModifierKey(k->keysym)) {
if (currentoverlay == -1)
overlayidx = hasoverlay(k->keysym); overlayidx = hasoverlay(k->keysym);
if (overlayidx != -1) { if (enableoverlays && overlayidx != -1) {
if (!pressbegin.tv_sec && !pressbegin.tv_usec) { if (!pressbegin.tv_sec && !pressbegin.tv_usec) {
//record the begin of the press, don't simulate the actual keypress yet //record the begin of the press, don't simulate the actual keypress yet
record_press_begin(k->keysym); record_press_begin(k->keysym);
@ -437,6 +438,12 @@ unpress(Key *k, KeySym mod) {
case XK_Cancel: case XK_Cancel:
cyclelayer(); cyclelayer();
break; break;
case XK_script_switch:
togglelayer();
break;
case XK_KP_Insert:
enableoverlays = !enableoverlays;
break;
case XK_Break: case XK_Break:
running = False; running = False;
default: default:
@ -445,7 +452,7 @@ unpress(Key *k, KeySym mod) {
} }
if ((pressbegin.tv_sec || pressbegin.tv_usec) && k && k->keysym == ispressingkeysym) { if ((pressbegin.tv_sec || pressbegin.tv_usec) && enableoverlays && k && k->keysym == ispressingkeysym) {
if (currentoverlay == -1) { if (currentoverlay == -1) {
if (get_press_duration() < overlay_delay) { if (get_press_duration() < overlay_delay) {
if (debug) { printf("Delayed simulation of press after release: %ld\n", k->keysym); fflush(stdout); } if (debug) { printf("Delayed simulation of press after release: %ld\n", k->keysym); fflush(stdout); }
@ -472,7 +479,7 @@ unpress(Key *k, KeySym mod) {
if (k) { if (k) {
printf("Simulation of release: %ld\n", k->keysym); fflush(stdout); printf("Simulation of release: %ld\n", k->keysym); fflush(stdout);
} else { } else {
printf("Simulation of release (all keys)"); fflush(stdout); printf("Simulation of release (all keys)\n"); fflush(stdout);
} }
} }
@ -500,7 +507,7 @@ unpress(Key *k, KeySym mod) {
} }
} }
if (currentoverlay != -1) { if (enableoverlays && currentoverlay != -1) {
if (releaseprotect) { if (releaseprotect) {
releaseprotect = 0; releaseprotect = 0;
} else { } else {
@ -516,7 +523,6 @@ run(void) {
fd_set fds; fd_set fds;
struct timeval tv; struct timeval tv;
double duration = 0.0; double duration = 0.0;
int cyclemodidx;
xfd = ConnectionNumber(dpy); xfd = ConnectionNumber(dpy);
@ -528,6 +534,7 @@ run(void) {
XFlush(dpy); XFlush(dpy);
while (running) { while (running) {
usleep(100000L);
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(xfd, &fds); FD_SET(xfd, &fds);
if (select(xfd + 1, &fds, NULL, NULL, &tv)) { if (select(xfd + 1, &fds, NULL, NULL, &tv)) {
@ -543,19 +550,13 @@ run(void) {
if (debug == 2) { printf("%f\n", duration); fflush(stdout); } if (debug == 2) { printf("%f\n", duration); fflush(stdout); }
if (get_press_duration() >= overlay_delay) { if (get_press_duration() >= overlay_delay) {
if (debug) { printf("press duration %f\n", duration); fflush(stdout); } if (debug) { printf("press duration %f\n", duration); fflush(stdout); }
cyclemodidx = iscyclemod(ispressingkeysym); showoverlay(hasoverlay(ispressingkeysym));
if (cyclemodidx != -1) {
cyclemod();
} else {
showoverlay(hasoverlay(ispressingkeysym));
}
pressbegin.tv_sec = 0; pressbegin.tv_sec = 0;
pressbegin.tv_usec = 0; pressbegin.tv_usec = 0;
ispressingkeysym = 0; ispressingkeysym = 0;
} }
} }
} }
usleep(100000L);
} }
} }
@ -719,14 +720,20 @@ updatekeys() {
void void
usage(char *argv0) { usage(char *argv0) {
fprintf(stderr, "usage: %s [-hdvD] [-g geometry] [-fn font]\n", argv0); fprintf(stderr, "usage: %s [-hdvDOl] [-g geometry] [-fn font]\n", argv0);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -d - Set Dock Window Type\n");
fprintf(stderr, " -D - Enable debug\n");
fprintf(stderr, " -O - Disable overlays\n");
fprintf(stderr, " -l - Comma separated list of layers to enable\n");
fprintf(stderr, " -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n");
exit(1); exit(1);
} }
void void
cyclelayer() { cyclelayer() {
currentlayer++; currentlayer++;
if (currentlayer >= LAYERS) if (currentlayer >= numlayers)
currentlayer = 0; currentlayer = 0;
if (debug) { printf("Cycling to layer %d\n", currentlayer); fflush(stdout); } if (debug) { printf("Cycling to layer %d\n", currentlayer); fflush(stdout); }
memcpy(&keys, layers[currentlayer], sizeof(keys_en)); memcpy(&keys, layers[currentlayer], sizeof(keys_en));
@ -735,29 +742,19 @@ cyclelayer() {
} }
void void
cyclemod() { togglelayer() {
int i; if (currentlayer > 0) {
//unpress all pressed keys currentlayer = 0;
for(i = 0; i < LENGTH(keys); i++) { } else if (numlayers > 1) {
if(keys[i].pressed) { currentlayer = 1;
keys[i].pressed = 0;
drawkey(&keys[i]);
}
} }
pressedmod = 0; if (debug) { printf("Toggling layer %d\n", currentlayer); fflush(stdout); }
pressbegin.tv_sec = 0; memcpy(&keys, layers[currentlayer], sizeof(keys_en));
pressbegin.tv_usec = 0; updatekeys();
ispressingkeysym = 0; drawkeyboard();
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 void
showoverlay(int idx) { showoverlay(int idx) {
if (debug) { printf("Showing overlay %d\n", idx); fflush(stdout); } if (debug) { printf("Showing overlay %d\n", idx); fflush(stdout); }
@ -802,15 +799,58 @@ sigterm(int sig)
{ {
running = False; running = False;
sigtermd = True; sigtermd = True;
if (debug) { printf("Sigterm received\n"); fflush(stdout); }
}
void
init_layers(char * layer_names_list) {
if (layer_names_list == NULL) {
numlayers = LAYERS;
memcpy(&layers, &available_layers, sizeof(available_layers));
} else {
char * s;
int j;
s = strtok(layer_names_list, ",");
while (s != NULL) {
if (numlayers+1 > LAYERS) die("too many layers specified");
int found = 0;
for (j = 0; j < LAYERS; j++) {
if (strcmp(layer_names[j], s) == 0) {
layers[numlayers] = available_layers[j];
printf("Adding layer %s\n", s);
found = 1;
break;
}
}
if (!found) {
fprintf(stderr, "Undefined layer: %s\n", s);
exit(3);
}
numlayers++;
s = strtok(NULL,",");
}
}
} }
int int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
int i, xr, yr, bitm; int i, xr, yr, bitm;
unsigned int wr, hr; unsigned int wr, hr;
char * layer_names_list = NULL;
memcpy(&keys, &keys_en, sizeof(keys_en)); memcpy(&keys, &keys_en, sizeof(keys_en));
signal(SIGTERM, sigterm); signal(SIGTERM, sigterm);
const char* enableoverlays_env = getenv("SVKBD_ENABLEOVERLAYS");
if (enableoverlays_env != NULL) enableoverlays = atoi(enableoverlays_env);
const char* layers_env = getenv("SVKBD_LAYERS");
if (layers_env != NULL) {
layer_names_list = malloc(128);
strcpy(layer_names_list, layers_env);
}
for (i = 1; argv[i]; i++) { for (i = 1; argv[i]; i++) {
if(!strcmp(argv[i], "-v")) { if(!strcmp(argv[i], "-v")) {
die("svkbd-"VERSION", © 2006-2020 svkbd engineers," die("svkbd-"VERSION", © 2006-2020 svkbd engineers,"
@ -842,9 +882,18 @@ main(int argc, char *argv[]) {
debug = 1; debug = 1;
} else if(!strcmp(argv[i], "-h")) { } else if(!strcmp(argv[i], "-h")) {
usage(argv[0]); usage(argv[0]);
} else if(!strcmp(argv[i], "-O")) {
enableoverlays = 0;
} else if(!strcmp(argv[i], "-l")) {
if(i >= argc - 1)
continue;
if (layer_names_list == NULL) layer_names_list = malloc(128);
strcpy(layer_names_list, argv[i+1]);
} }
} }
init_layers(layer_names_list);
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fprintf(stderr, "warning: no locale support\n"); fprintf(stderr, "warning: no locale support\n");
if(!(dpy = XOpenDisplay(0))) if(!(dpy = XOpenDisplay(0)))
@ -853,5 +902,6 @@ main(int argc, char *argv[]) {
run(); run();
cleanup(); cleanup();
XCloseDisplay(dpy); XCloseDisplay(dpy);
if (layer_names_list != NULL) free(layer_names_list);
return 0; return 0;
} }