Add support for dynamic layout switching (en / de)

This commit is contained in:
Johannes Marbach
2021-09-06 10:26:59 +02:00
parent 4e25a65c2d
commit 14de16a506
6 changed files with 9239 additions and 8 deletions

View File

@@ -10,7 +10,7 @@ BIN = unl0kr
#Collect the files to compile
MAINSRC = ./main.c ./libinput_device_discovery.c ./libinput_multi.c ./libinput_xkb.c
MAINSRC = ./main.c ./libinput_device_discovery.c ./libinput_multi.c ./libinput_xkb.c ./layouts.c ./montserrat_extended_32.c
include $(LVGL_DIR)/lvgl/lvgl.mk
include $(LVGL_DIR)/lv_drivers/lv_drivers.mk

View File

@@ -21,6 +21,7 @@ The biggest obstacle is input processing. [lv_drivers] provides an evdev interfa
- On-screen keyboard control via one or more hardware keyboard (including support for multiple layouts using XKB)
- Works great on my laptop keyboard but occasionally drops keys on my Ergodox EZ)
- On-screen keyboard control via touchscreen (tested on PinePhone)
- Switching on-screen keyboard layout at runtime (layouts still to be refined, currently only supports US English and German)
## To do
@@ -38,7 +39,7 @@ Upstreamed contributions so far:
- [Add full keyboard support to libinput/evdev driver] (⏳ in review)
- [Automatic device discovery via libinput] (⏳ in review)
# Operation
# Development
## Dependencies
@@ -47,7 +48,7 @@ Upstreamed contributions so far:
- [libinput]
- [libxkbcommon]
## Testing
## Building & running
For development and testing you can run the app in a VT. `sudo` is needed to access input device files.
@@ -57,6 +58,14 @@ $ sudo chvt 2
$ sudo ./unl0kr
```
## Changing fonts
Fonts need to be converted to C arrays before they can be used with [lvgl]. This is most conveniently done using the official [online font converter]. Useful unicode ranges for the conversion are 0x0020-0x007F (basic Latin) and 0x00A0-0x00FF (Latin-1 supplement). For the various `LV_SYMBOL_...` glyphs, make sure to also add [Font Awesome] with the following code points:
```
0xF001, 0xF008, 0xF00B, 0xF00C, 0xF00D, 0xF011, 0xF013, 0xF015, 0xF019, 0xF01C, 0xF021, 0xF026, 0xF027, 0xF028, 0xF03E, 0xF0E0, 0xF304, 0xF043, 0xF048, 0xF04B, 0xF04C, 0xF04D, 0xF051, 0xF052, 0xF053, 0xF054, 0xF067, 0xF068, 0xF06E, 0xF070, 0xF071, 0xF074, 0xF077, 0xF078, 0xF079, 0xF07B, 0xF093, 0xF095, 0xF0C4, 0xF0C5, 0xF0C7, 0xF0C9, 0xF0E7, 0xF0EA, 0xF0F3, 0xF11C, 0xF124, 0xF158, 0xF1EB, 0xF240, 0xF241, 0xF242, 0xF243, 0xF244, 0xF287, 0xF293, 0xF2ED, 0xF55A, 0xF7C2, 0xF8A2
```
# Acknowledgements
The [lv_port_linux_frame_buffer] project served as a starting point for the codebase. The mouse cursor image was taken from [lv_sim_emscripten].
@@ -71,6 +80,8 @@ Unl0kr is licensed under the GNU General Public License as published by the Free
[lv_sim_emscripten]: https://github.com/lvgl/lv_sim_emscripten/blob/master/mouse_cursor_icon.c]
[libinput]: https://gitlab.freedesktop.org/libinput/libinput
[libxkbcommon]: https://github.com/xkbcommon/libxkbcommon
[online font converter]: https://lvgl.io/tools/fontconverter
[Font Awesome]: https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff
[Add support for pointer devices to libinput driver]: https://github.com/lvgl/lv_drivers/pull/150
[Add support for keypads to libinput driver]: https://github.com/lvgl/lv_drivers/pull/152
[Don't compile example assets when disabled in lv_conf.h]: https://github.com/lvgl/lvgl/pull/2523

152
layouts.c Normal file
View File

@@ -0,0 +1,152 @@
#include "layouts.h"
/**
* English (US)
**/
#define NAME_ENGLISH_US "English (US)"
/* Lowercase layer */
#define KEYS_LOWER_ENGLISH_US { \
"1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n", \
"ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n", \
"_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n", \
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" \
}
#define ATTRIBUTES_LOWER_ENGLISH_US { \
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_BTNMATRIX_CTRL_CHECKED | 7, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, LV_BTNMATRIX_CTRL_CHECKED | 7, \
LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 \
}
/* Uppercase layer */
#define KEYS_UPPER_ENGLISH_US { \
"1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n", \
"abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n", \
"_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n", \
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" \
}
#define ATTRIBUTES_UPPER_ENGLISH_US { \
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, LV_BTNMATRIX_CTRL_CHECKED | 7, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, LV_BTNMATRIX_CTRL_CHECKED | 7, \
LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, LV_BTNMATRIX_CTRL_CHECKED | 1, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 \
}
/* Symbol layer */
#define KEYS_SYMBOL_ENGLISH_US { \
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n", \
"abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n", \
"\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n", \
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" \
}
#define ATTRIBUTES_SYMBOL_ENGLISH_US { \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 2, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 \
}
/**
* German
**/
#define NAME_GERMAN "German"
/* Lowercase layer */
#define KEYS_LOWER_GERMAN { \
"q", "w", "e", "r", "t", "z", "u", "i", "o", "p", "ü", "\n", \
"a", "s", "d", "f", "g", "h", "j", "k", "l", "ö", "ä", "\n", \
"ABC", "_", "y", "x", "c", "v", "b", "n", "m", "_", LV_SYMBOL_BACKSPACE, "\n", \
"1#", LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" \
}
#define ATTRIBUTES_LOWER_GERMAN { \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_BTNMATRIX_CTRL_HIDDEN | 1, 2, 2, 2, 2, 2, 2, 2, LV_BTNMATRIX_CTRL_HIDDEN | 1, LV_BTNMATRIX_CTRL_CHECKED | 3, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 7, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 \
}
/* Uppercase layer */
#define KEYS_UPPER_GERMAN { \
"Q", "W", "E", "R", "T", "Z", "U", "I", "O", "P", "Ü", "\n", \
"A", "S", "D", "F", "G", "H", "J", "K", "L", "Ö", "Ä", "\n", \
"abc", "_", "Y", "X", "C", "V", "B", "N", "M", "_", LV_SYMBOL_BACKSPACE, "\n", \
"1#", LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" \
}
#define ATTRIBUTES_UPPER_GERMAN { \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_BTNMATRIX_CTRL_HIDDEN | 1, 2, 2, 2, 2, 2, 2, 2, LV_BTNMATRIX_CTRL_HIDDEN | 1, LV_BTNMATRIX_CTRL_CHECKED | 3, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 7, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 \
}
/* Symbol layer */
#define KEYS_SYMBOL_GERMAN { \
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n", \
"abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n", \
"\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n", \
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, "" \
}
#define ATTRIBUTES_SYMBOL_GERMAN { \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, LV_BTNMATRIX_CTRL_CHECKED | 2, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2 \
}
/**
* All layouts
**/
static const char * const names =
NAME_ENGLISH_US "\n"
NAME_GERMAN;
static const char ** const keys_lower[] = {
(const char * const [])KEYS_LOWER_ENGLISH_US,
(const char * const [])KEYS_LOWER_GERMAN
};
static const lv_btnmatrix_ctrl_t * const attributes_lower[] = {
(lv_btnmatrix_ctrl_t[])ATTRIBUTES_LOWER_ENGLISH_US,
(lv_btnmatrix_ctrl_t[])ATTRIBUTES_LOWER_GERMAN
};
static const char ** const keys_upper[] = {
(const char * const [])KEYS_UPPER_ENGLISH_US,
(const char * const [])KEYS_UPPER_GERMAN
};
static const lv_btnmatrix_ctrl_t *attributes_upper[] = {
(lv_btnmatrix_ctrl_t[])ATTRIBUTES_UPPER_ENGLISH_US,
(lv_btnmatrix_ctrl_t[])ATTRIBUTES_UPPER_GERMAN
};
static const char ** const keys_symbol[] = {
(const char * const [])KEYS_SYMBOL_ENGLISH_US,
(const char * const [])KEYS_SYMBOL_GERMAN
};
static const lv_btnmatrix_ctrl_t *attributes_symbol[] = {
(lv_btnmatrix_ctrl_t[])ATTRIBUTES_SYMBOL_ENGLISH_US,
(lv_btnmatrix_ctrl_t[])ATTRIBUTES_SYMBOL_GERMAN
};
/**
* Functions
**/
char *get_layout_names(void) {
return names;
}
void apply_layout(lv_obj_t *keyboard, layout_t layout) {
if (layout < 0 || layout >= NUM_LAYOUTS) {
return;
}
lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_TEXT_LOWER, keys_lower[layout], attributes_lower[layout]);
lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_TEXT_UPPER, keys_upper[layout], attributes_upper[layout]);
lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_SPECIAL, keys_symbol[layout], attributes_symbol[layout]);
}

16
layouts.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef LAYOUTS_H
#define LAYOUTS_H
#include "lvgl/lvgl.h"
typedef enum {
LAYOUT_ENGLISH_US = 0,
LAYOUT_GERMAN = 1
} layout_t;
#define NUM_LAYOUTS 2
char *get_layout_names(void);
void apply_layout(lv_obj_t *keyboard, layout_t layout);
#endif /* LAYOUTS_H */

31
main.c
View File

@@ -9,6 +9,7 @@
#include "libinput_device_discovery.h"
#include "libinput_multi.h"
#include "libinput_xkb.h"
#include "layouts.h"
// Mouse cursor image (from https://github.com/lvgl/lv_sim_emscripten/blob/master/mouse_cursor_icon.c)
@@ -115,7 +116,11 @@ lv_img_dsc_t mouse_cursor_icon = {
.data = mouse_cursor_icon_map,
};
// Keyboard event callbacks
// Global widgets
lv_obj_t *keyboard = NULL;
// Event callbacks
void keyboard_event_ready_cb(lv_event_t *e);
@@ -132,6 +137,13 @@ void keyboard_event_cancel_cb(lv_event_t *e) {
abort();
}
void keymap_dropdown_event_cb(lv_event_t * e) {
if(lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) {
return;
}
apply_layout(keyboard, lv_dropdown_get_selected(lv_event_get_target(e)));
}
// Main
int main(void)
@@ -220,6 +232,9 @@ int main(void)
// Build the UI...
// Register fonts
LV_FONT_DECLARE(montserrat_extended_32);
// Figure out a few numbers for sizing and positioning
int row_height = ver_res / 6;
if (4 * row_height > ver_res / 2) {
@@ -228,13 +243,13 @@ int main(void)
const int keyboard_height = 4 * row_height;
// Keyboard
lv_obj_t *keyboard = lv_keyboard_create(lv_scr_act());
keyboard = lv_keyboard_create(lv_scr_act());
lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_TEXT_LOWER);
lv_obj_set_pos(keyboard, 0, 0);
lv_obj_set_size(keyboard, hor_res, keyboard_height);
lv_style_t style_keyboard;
lv_style_init(&style_keyboard);
lv_style_set_text_font(&style_keyboard, &lv_font_montserrat_32);
lv_style_set_text_font(&style_keyboard, &montserrat_extended_32);
lv_obj_add_style(keyboard, &style_keyboard, 0);
// Label
@@ -245,7 +260,7 @@ int main(void)
lv_obj_align(spangroup, LV_ALIGN_CENTER, 0, ver_res / 2 - keyboard_height);
static lv_style_t style_spangroup;
lv_style_init(&style_spangroup);
lv_style_set_text_font(&style_spangroup, &lv_font_montserrat_32);
lv_style_set_text_font(&style_spangroup, &montserrat_extended_32);
lv_obj_add_style(spangroup, &style_spangroup, 0);
lv_span_t *span1 = lv_spangroup_new_span(spangroup);
lv_span_set_text(span1, "Password required to unlock ");
@@ -263,7 +278,7 @@ int main(void)
lv_obj_add_state(textarea, LV_STATE_FOCUSED);
lv_style_t style_textarea;
lv_style_init(&style_textarea);
lv_style_set_text_font(&style_textarea, &lv_font_montserrat_32);
lv_style_set_text_font(&style_textarea, &montserrat_extended_32);
lv_obj_add_style(textarea, &style_textarea, 0);
// Route keyboard input into textarea
@@ -279,6 +294,12 @@ int main(void)
lv_obj_add_event_cb(keyboard, keyboard_event_cancel_cb, LV_EVENT_CANCEL, NULL);
lv_obj_add_event_cb(keyboard, keyboard_event_ready_cb, LV_EVENT_READY, NULL);
// Keymap dropdown
lv_obj_t *dropdown = lv_dropdown_create(lv_scr_act());
lv_dropdown_set_options(dropdown, get_layout_names());
lv_obj_align(dropdown, LV_ALIGN_TOP_RIGHT, -20, 20);
lv_obj_add_event_cb(dropdown, keymap_dropdown_event_cb, LV_EVENT_ALL, NULL);
// Run lvgl in tickless mode
while(1) {
lv_task_handler();

9031
montserrat_extended_32.c Normal file

File diff suppressed because it is too large Load Diff