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:
parent
48994f125e
commit
99935775af
31
README
31
README
@ -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
|
||||||
|
|
||||||
|
168
layout.sxmo.h
168
layout.sxmo.h
@ -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 },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
162
svkbd.c
162
svkbd.c
@ -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);
|
|
||||||
if (cyclemodidx != -1) {
|
|
||||||
cyclemod();
|
|
||||||
} else {
|
|
||||||
showoverlay(hasoverlay(ispressingkeysym));
|
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,28 +742,18 @@ 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]);
|
|
||||||
}
|
}
|
||||||
|
if (debug) { printf("Toggling layer %d\n", currentlayer); fflush(stdout); }
|
||||||
|
memcpy(&keys, layers[currentlayer], sizeof(keys_en));
|
||||||
|
updatekeys();
|
||||||
|
drawkeyboard();
|
||||||
}
|
}
|
||||||
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
|
void
|
||||||
showoverlay(int idx) {
|
showoverlay(int idx) {
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user