Add minimalist C API and notes on integrating

This commit is contained in:
Johannes Marbach 2021-09-18 22:00:03 +02:00
parent c0e99da8f5
commit d3f62fd08f
6 changed files with 349 additions and 1 deletions

View file

@ -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

View file

@ -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

View file

@ -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
View 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
View 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 */

View file

@ -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 = {}