Add minimalist C API and notes on integrating
This commit is contained in:
parent
c0e99da8f5
commit
d3f62fd08f
6 changed files with 349 additions and 1 deletions
31
README.md
31
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# squeek2lvgl
|
||||
|
||||
squeek2lvgl is a Python script that makes it possible to use [squeekboard] keyboard layouts with [LVGL]'s keyboard widget. To achieve this, [squeekboard]'s YAML syntax for layout definitions is converted into a C file (with an accompanying header) that can then be added to an [LVGL] project.
|
||||
squeek2lvgl is a Python script and an accompanying C library that make it possible to use [squeekboard] keyboard layouts with [LVGL]'s keyboard widget. To achieve this, [squeekboard]'s YAML syntax for layout definitions is converted into C files that, together with the library C files, can then be added to an [LVGL] project.
|
||||
|
||||
To access [squeekboard]'s layout files, squeek2lvgl shallowly clones the [squeekboard] git repository into a temporary directory and purges it before exiting.
|
||||
|
||||
|
|
@ -38,6 +38,33 @@ $ pipenv run python squeek2lvgl.py --input us.yaml --output .
|
|||
|
||||
When the process has finished, `sq2lv_layouts.h` and `sq2lv_layouts.c` will have been written into the current directory. Check the [examples] folder for further details about the generated files.
|
||||
|
||||
To facilitate usage, a minimalist C API is available in [sq2lv.h] and [sq2lv.c].
|
||||
|
||||
### Integrating into a project
|
||||
|
||||
Similar to [LVGL] squeek2lvgl should be added into a project as a git submodule. The generated C files assume that you have integrated [LVGL] as a submodule in the `./lvgl` folder. The C library files in turn assume that you have imported squeek2lvgl as a submodule one folder above the location of the generated files.
|
||||
|
||||
The following is an example directory hierarchy:
|
||||
|
||||
```
|
||||
$ tree .
|
||||
.
|
||||
├── lvgl # LVGL submodule
|
||||
│ ├── ...
|
||||
├── main.c # Project's own source files
|
||||
├── ...
|
||||
├── sq2lv_layouts.c # Layouts generated with squeek2lvgl
|
||||
├── sq2lv_layouts.h
|
||||
├── ...
|
||||
├── squeek2lvgl # squeek2lvgl submodule
|
||||
│ ├── sq2lv.c # squeek2lvgl library code
|
||||
│ ├── sq2lv.h
|
||||
│ ├── ...
|
||||
├── ...
|
||||
```
|
||||
|
||||
Using the directory structure above, you can then add `sq2lv_layouts.c` and `squeek2lvgl/sq2lv.c` into your build process just like the rest of your project's sources.
|
||||
|
||||
## License
|
||||
|
||||
squeek2lvgl is licensed under the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
|
@ -48,3 +75,5 @@ squeek2lvgl is licensed under the GNU General Public License as published by the
|
|||
[squeekboard]: https://gitlab.gnome.org/World/Phosh/squeekboard
|
||||
[squeekboard's US layout]: https://gitlab.gnome.org/World/Phosh/squeekboard/-/blob/master/data/keyboards/us.yaml
|
||||
[examples]: ./examples
|
||||
[sq2lv.h]: ./sq2lv.h
|
||||
[sq2lv.c]: ./sq2lv.c
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "lvgl/lvgl.h"
|
||||
|
||||
#define SQ2LV_SCANCODES_ENABLED 1
|
||||
|
||||
/* Layout IDs, values can be used as indexes into the sq2lv_layouts array */
|
||||
typedef enum {
|
||||
SQ2LV_LAYOUT_TERMINAL_US = 0
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "lvgl/lvgl.h"
|
||||
|
||||
#define SQ2LV_SCANCODES_ENABLED 0
|
||||
|
||||
/* Layout IDs, values can be used as indexes into the sq2lv_layouts array */
|
||||
typedef enum {
|
||||
SQ2LV_LAYOUT_US = 0
|
||||
|
|
|
|||
231
sq2lv.c
Normal file
231
sq2lv.c
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/**
|
||||
* Copyright 2021 Johannes Marbach
|
||||
*
|
||||
* This file is part of squeek2lvgl, hereafter referred to as the program.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "sq2lv.h"
|
||||
|
||||
|
||||
/**
|
||||
* Static variables
|
||||
*/
|
||||
|
||||
static sq2lv_layout_id_t current_layout_id = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Static prototypes
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a layer index to an LVGL keyboard mode.
|
||||
*
|
||||
* @param layer_index layer index to convert
|
||||
* @return corresponding LVGL keyboard mode
|
||||
*/
|
||||
static lv_keyboard_mode_t layer_index_to_keyboard_mode(int layer_index);
|
||||
|
||||
/**
|
||||
* Convert an LVGL keyboard mode to a layer index.
|
||||
*
|
||||
* @param keyboard_mode LVGL keyboard mode to convert
|
||||
* @return corresponding layer index
|
||||
*/
|
||||
static int keyboard_mode_to_layer_index(lv_keyboard_mode_t keyboard_mode);
|
||||
|
||||
/**
|
||||
* Get the layer index corresponding to a keyboard's current mode.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @return layer index
|
||||
*/
|
||||
static int get_layer_index(lv_obj_t *keyboard);
|
||||
|
||||
/**
|
||||
* Get the index of the destination layer for a layer switcher key in the current layout.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @param btn_id button index corresponding to the key
|
||||
* @return the destination layer's index or -1 if the key is not a layer switcher
|
||||
*/
|
||||
static int get_destination_layer_index_for_layer_switcher(lv_obj_t *keyboard, uint16_t btn_id);
|
||||
|
||||
|
||||
/**
|
||||
* Static functions
|
||||
*/
|
||||
|
||||
static lv_keyboard_mode_t layer_index_to_keyboard_mode(int layer_index) {
|
||||
switch (layer_index) {
|
||||
case 0:
|
||||
return LV_KEYBOARD_MODE_TEXT_LOWER;
|
||||
case 1:
|
||||
return LV_KEYBOARD_MODE_TEXT_UPPER;
|
||||
case 2:
|
||||
return LV_KEYBOARD_MODE_SPECIAL;
|
||||
case 3:
|
||||
return LV_KEYBOARD_MODE_NUMBER;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int keyboard_mode_to_layer_index(lv_keyboard_mode_t keyboard_mode) {
|
||||
switch (keyboard_mode) {
|
||||
case LV_KEYBOARD_MODE_TEXT_LOWER:
|
||||
return 0;
|
||||
case LV_KEYBOARD_MODE_TEXT_UPPER:
|
||||
return 1;
|
||||
case LV_KEYBOARD_MODE_SPECIAL:
|
||||
return 2;
|
||||
case LV_KEYBOARD_MODE_NUMBER:
|
||||
return 3;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_layer_index(lv_obj_t *keyboard) {
|
||||
return keyboard_mode_to_layer_index(lv_keyboard_get_mode(keyboard));
|
||||
}
|
||||
|
||||
static int get_destination_layer_index_for_layer_switcher(lv_obj_t *keyboard, uint16_t btn_id) {
|
||||
if (current_layout_id < 0 || current_layout_id >= sq2lv_num_layouts) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int layer_index = get_layer_index(keyboard);
|
||||
if (layer_index < 0 || layer_index >= sq2lv_layouts[current_layout_id].num_layers) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sq2lv_layouts[current_layout_id].layers[layer_index].num_switchers; ++i) {
|
||||
if (sq2lv_layouts[current_layout_id].layers[layer_index].switcher_idxs[i] == btn_id) {
|
||||
return sq2lv_layouts[current_layout_id].layers[layer_index].switcher_dests[i];
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
void sq2lv_switch_layout(lv_obj_t *keyboard, sq2lv_layout_id_t layout_id) {
|
||||
if (layout_id < 0 || layout_id >= sq2lv_num_layouts) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assign layers */
|
||||
for (int i = 0; i < sq2lv_layouts[layout_id].num_layers; ++i) {
|
||||
lv_keyboard_set_map(keyboard, layer_index_to_keyboard_mode(i),
|
||||
sq2lv_layouts[layout_id].layers[i].keycaps,
|
||||
sq2lv_layouts[layout_id].layers[i].attributes);
|
||||
}
|
||||
|
||||
/* Switch to default layer if current layer doesn't exist in new layout */
|
||||
int layer_index = get_layer_index(keyboard);
|
||||
if (layer_index < 0 || layer_index >= sq2lv_layouts[layout_id].num_layers) {
|
||||
lv_keyboard_set_mode(keyboard, layer_index_to_keyboard_mode(0));
|
||||
}
|
||||
|
||||
current_layout_id = layout_id;
|
||||
}
|
||||
|
||||
bool sq2lv_is_layer_switcher(lv_obj_t *keyboard, uint16_t btn_id) {
|
||||
return get_destination_layer_index_for_layer_switcher(keyboard, btn_id) >= 0;
|
||||
}
|
||||
|
||||
bool sq2lv_switch_layer(lv_obj_t *keyboard, uint16_t btn_id) {
|
||||
int destination_layer_index = get_destination_layer_index_for_layer_switcher(keyboard, btn_id);
|
||||
if (destination_layer_index < 0 || destination_layer_index >= sq2lv_layouts[current_layout_id].num_layers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lv_keyboard_set_mode(keyboard, layer_index_to_keyboard_mode(destination_layer_index));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sq2lv_is_modifier(lv_obj_t *keyboard, uint16_t btn_id) {
|
||||
if (current_layout_id < 0 || current_layout_id >= sq2lv_num_layouts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int layer_index = get_layer_index(keyboard);
|
||||
if (layer_index < 0 || layer_index >= sq2lv_layouts[current_layout_id].num_layers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sq2lv_layouts[current_layout_id].layers[layer_index].num_modifiers; ++i) {
|
||||
if (sq2lv_layouts[current_layout_id].layers[layer_index].modifier_idxs[i] == btn_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const int * const sq2lv_get_modifier_indexes(lv_obj_t *keyboard, int *num_modifiers) {
|
||||
if (current_layout_id < 0 || current_layout_id >= sq2lv_num_layouts) {
|
||||
*num_modifiers = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int layer_index = get_layer_index(keyboard);
|
||||
if (layer_index < 0 || layer_index >= sq2lv_layouts[current_layout_id].num_layers) {
|
||||
*num_modifiers = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*num_modifiers = sq2lv_layouts[current_layout_id].layers[layer_index].num_modifiers;
|
||||
if (*num_modifiers == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(sq2lv_layouts[current_layout_id].layers[layer_index].modifier_idxs[0]);
|
||||
}
|
||||
|
||||
#if SQ2LV_SCANCODES_ENABLED
|
||||
const int * const sq2lv_get_scancodes(lv_obj_t *keyboard, uint16_t btn_id, int *num_scancodes) {
|
||||
if (current_layout_id < 0 || current_layout_id >= sq2lv_num_layouts) {
|
||||
*num_scancodes = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int layer_index = get_layer_index(keyboard);
|
||||
if (layer_index < 0 || layer_index >= sq2lv_layouts[current_layout_id].num_layers) {
|
||||
*num_scancodes = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (btn_id >= sq2lv_layouts[current_layout_id].layers[layer_index].num_keys) {
|
||||
*num_scancodes = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*num_scancodes = sq2lv_layouts[current_layout_id].layers[layer_index].scancode_nums[btn_id];
|
||||
if (*num_scancodes == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int index = sq2lv_layouts[current_layout_id].layers[layer_index].scancode_idxs[btn_id];
|
||||
return &(sq2lv_layouts[current_layout_id].layers[layer_index].scancodes[index]);
|
||||
}
|
||||
#endif /* SQ2LV_SCANCODES_ENABLED */
|
||||
82
sq2lv.h
Normal file
82
sq2lv.h
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Copyright 2021 Johannes Marbach
|
||||
*
|
||||
* This file is part of squeek2lvgl, hereafter referred to as the program.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SQ2LV_H
|
||||
#define SQ2LV_H
|
||||
|
||||
#include "../sq2lv_layouts.h"
|
||||
|
||||
/**
|
||||
* Apply a layout to a keyboard.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @param layout_id layout ID
|
||||
*/
|
||||
void sq2lv_switch_layout(lv_obj_t *keyboard, sq2lv_layout_id_t layout_id);
|
||||
|
||||
/**
|
||||
* Check if a key acts as a layer switcher in the current layer.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @param btn_id button index corresponding to the key
|
||||
* @return true if a layer switch would happen if the key is pressed, false otherwise
|
||||
*/
|
||||
bool sq2lv_is_layer_switcher(lv_obj_t *keyboard, uint16_t btn_id);
|
||||
|
||||
/**
|
||||
* Attempt to perform a layer switch after pressing a key in the current layer.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @param btn_id button index corresponding to the pressed key
|
||||
* @return true if a layer switch was performed, false otherwise
|
||||
*/
|
||||
bool sq2lv_switch_layer(lv_obj_t *keyboard, uint16_t btn_id);
|
||||
|
||||
/**
|
||||
* Check if a key is a modifier in the current layer.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @param btn_id button index corresponding to the key
|
||||
* @return true if the key is a modifier, false otherwise
|
||||
*/
|
||||
bool sq2lv_is_modifier(lv_obj_t *keyboard, uint16_t btn_id);
|
||||
|
||||
/**
|
||||
* Get the button indexes for all modifier keys in the current layer.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @param num_modifiers pointer to an integer into which the number of modifiers will be written
|
||||
* @return pointer to the array of button indexes corresponding to modifier keys
|
||||
*/
|
||||
const int * const sq2lv_get_modifier_indexes(lv_obj_t *keyboard, int *num_modifiers);
|
||||
|
||||
#if SQ2LV_SCANCODES_ENABLED
|
||||
/**
|
||||
* Get scancodes associated with a key in the current layer.
|
||||
*
|
||||
* @param keyboard keyboard widget
|
||||
* @param btn_id button index corresponding to the key
|
||||
* @param num_scancodes pointer to an integer into which the number of scancodes will be written
|
||||
* @return pointer into an array of scancodes at the appropriate index
|
||||
*/
|
||||
const int * const sq2lv_get_scancodes(lv_obj_t *keyboard, uint16_t btn_id, int *num_scancodes);
|
||||
#endif /* SQ2LV_SCANCODES_ENABLED */
|
||||
|
||||
#endif /* SQ2LV_H */
|
||||
|
|
@ -622,6 +622,8 @@ if __name__ == '__main__':
|
|||
h_builder = SourceFileBuilder()
|
||||
h_builder.add_include('lvgl/lvgl.h')
|
||||
h_builder.add_line()
|
||||
h_builder.add_line(f'#define SQ2LV_SCANCODES_ENABLED {1 if args.generate_scancodes else 0}')
|
||||
h_builder.add_line()
|
||||
|
||||
layouts = []
|
||||
unique_scancodes = {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue