├── Kconfig ├── .gitignore ├── docs ├── images │ └── screen.jpg ├── 3d_files │ ├── nn_rear_cap.stl │ └── nn_rear_cap_w_reset.stl └── nice_nano_wire_guide.md ├── boards └── shields │ └── dongle_screen │ ├── dongle_screen.overlay │ ├── Kconfig.shield │ ├── include │ └── fonts.h │ ├── dongle_screen.conf │ ├── src │ ├── custom_status_screen.h │ ├── brightness.h │ ├── widgets │ │ ├── mod_status.h │ │ ├── layer_status.h │ │ ├── battery_status.h │ │ ├── wpm_status.h │ │ ├── output_status.h │ │ ├── layer_status.c │ │ ├── mod_status.c │ │ ├── wpm_status.c │ │ ├── output_status.c │ │ └── battery_status.c │ ├── screen_rotate_init.c │ ├── custom_status_screen.c │ ├── fonts │ │ ├── NerdFonts_Regular_20.c │ │ └── NerdFonts_Regular_40.c │ └── brightness.c │ ├── CMakeLists.txt │ ├── boards │ ├── nice_nano_v2.overlay │ └── seeeduino_xiao_ble.overlay │ └── Kconfig.defconfig ├── zephyr └── module.yml ├── .vscode └── settings.json ├── drivers └── display │ ├── CMakeLists.txt │ ├── display_st7789v.h │ └── display_st7789v.c ├── modules └── lvgl │ ├── CMakeLists.txt │ └── lvgl.c ├── config └── west.yml ├── CMakeLists.txt ├── LICENSE └── README.md /Kconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /docs/images/screen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janpfischer/zmk-dongle-screen/HEAD/docs/images/screen.jpg -------------------------------------------------------------------------------- /boards/shields/dongle_screen/dongle_screen.overlay: -------------------------------------------------------------------------------- 1 | / { 2 | chosen { 3 | zephyr,display = &st7789; 4 | }; 5 | }; -------------------------------------------------------------------------------- /docs/3d_files/nn_rear_cap.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janpfischer/zmk-dongle-screen/HEAD/docs/3d_files/nn_rear_cap.stl -------------------------------------------------------------------------------- /boards/shields/dongle_screen/Kconfig.shield: -------------------------------------------------------------------------------- 1 | config SHIELD_DONGLE_SCREEN 2 | def_bool $(shields_list_contains,dongle_screen) -------------------------------------------------------------------------------- /docs/3d_files/nn_rear_cap_w_reset.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janpfischer/zmk-dongle-screen/HEAD/docs/3d_files/nn_rear_cap_w_reset.stl -------------------------------------------------------------------------------- /zephyr/module.yml: -------------------------------------------------------------------------------- 1 | name: dongle_screen 2 | build: 3 | cmake: . 4 | kconfig: Kconfig 5 | settings: 6 | board_root: . 7 | depends: 8 | - lvgl -------------------------------------------------------------------------------- /boards/shields/dongle_screen/include/fonts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | LV_FONT_DECLARE(NerdFonts_Regular_20); 6 | LV_FONT_DECLARE(NerdFonts_Regular_40); -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "custom_status_screen.h": "c", 4 | "layer_status.h": "c", 5 | "battery_status.h": "c", 6 | "locale": "c" 7 | } 8 | } -------------------------------------------------------------------------------- /boards/shields/dongle_screen/dongle_screen.conf: -------------------------------------------------------------------------------- 1 | CONFIG_ZMK_DISPLAY=y 2 | CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y 3 | CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY=n 4 | CONFIG_ZMK_DISPLAY_STATUS_SCREEN_CUSTOM=y 5 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/custom_status_screen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2024 The ZMK Contributors 4 | * SPDX-License-Identifier: MIT 5 | * 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | 12 | lv_obj_t *zmk_display_status_screen(); -------------------------------------------------------------------------------- /drivers/display/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | zephyr_library_amend() 2 | set_source_files_properties( 3 | ${ZEPHYR_BASE}/drivers/display/display_st7789v.c 4 | TARGET_DIRECTORY ${lib_name} 5 | PROPERTIES HEADER_FILE_ONLY ON) 6 | zephyr_library_sources(display_st7789v.c) -------------------------------------------------------------------------------- /modules/lvgl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Zephyr) 2 | zephyr_library_amend() 3 | set_source_files_properties( 4 | ${ZEPHYR_BASE}/modules/lvgl/lvgl.c 5 | TARGET_DIRECTORY ${lib_name} 6 | PROPERTIES HEADER_FILE_ONLY ON) 7 | zephyr_library_sources(lvgl.c) -------------------------------------------------------------------------------- /config/west.yml: -------------------------------------------------------------------------------- 1 | manifest: 2 | remotes: 3 | - name: zmkfirmware 4 | url-base: https://github.com/zmkfirmware 5 | projects: 6 | - name: zmk 7 | remote: zmkfirmware 8 | revision: main 9 | import: app/west.yml 10 | self: 11 | path: config -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | zephyr_include_directories(include) 2 | zephyr_library() 3 | 4 | if(CONFIG_SHIELD_DONGLE_SCREEN) 5 | 6 | add_subdirectory(${ZEPHYR_CURRENT_MODULE_DIR}/drivers/display) 7 | add_subdirectory(${ZEPHYR_CURRENT_MODULE_DIR}/modules/lvgl) 8 | 9 | 10 | endif() -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/brightness.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #pragma once 8 | 9 | /** 10 | * @brief Wake the screen when a peripheral reconnects 11 | * Called by battery widget when it detects a peripheral reconnection 12 | */ 13 | void brightness_wake_screen_on_reconnect(void); -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/mod_status.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct zmk_widget_mod_status 7 | { 8 | sys_snode_t node; 9 | lv_obj_t *obj; 10 | lv_obj_t *label; 11 | }; 12 | 13 | int zmk_widget_mod_status_init(struct zmk_widget_mod_status *widget, lv_obj_t *parent); 14 | lv_obj_t *zmk_widget_mod_status_obj(struct zmk_widget_mod_status *widget); -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/layer_status.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | struct zmk_widget_layer_status { 13 | sys_snode_t node; 14 | lv_obj_t *obj; 15 | }; 16 | 17 | int zmk_widget_layer_status_init(struct zmk_widget_layer_status *widget, lv_obj_t *parent); 18 | lv_obj_t *zmk_widget_layer_status_obj(struct zmk_widget_layer_status *widget); -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/battery_status.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | struct zmk_widget_dongle_battery_status { 13 | sys_snode_t node; 14 | lv_obj_t *obj; 15 | }; 16 | 17 | int zmk_widget_dongle_battery_status_init(struct zmk_widget_dongle_battery_status *widget, lv_obj_t *parent); 18 | lv_obj_t *zmk_widget_dongle_battery_status_obj(struct zmk_widget_dongle_battery_status *widget); -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/wpm_status.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | struct zmk_widget_wpm_status 13 | { 14 | lv_obj_t *obj; 15 | lv_obj_t *wpm_label; 16 | lv_obj_t *font_test; 17 | sys_snode_t node; 18 | }; 19 | 20 | int zmk_widget_wpm_status_init(struct zmk_widget_wpm_status *widget, lv_obj_t *parent); 21 | lv_obj_t *zmk_widget_wpm_status_obj(struct zmk_widget_wpm_status *widget); -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/output_status.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | // output_status.h 13 | struct zmk_widget_output_status 14 | { 15 | lv_obj_t *obj; 16 | lv_obj_t *transport_label; 17 | lv_obj_t *ble_label; 18 | sys_snode_t node; 19 | }; 20 | 21 | int zmk_widget_output_status_init(struct zmk_widget_output_status *widget, lv_obj_t *parent); 22 | lv_obj_t *zmk_widget_output_status_obj(struct zmk_widget_output_status *widget); -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/screen_rotate_init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 6 | int disp_set_orientation(void) 7 | { 8 | // Set the orientation 9 | const struct device *display = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); 10 | if (!device_is_ready(display)) 11 | { 12 | return -EIO; 13 | } 14 | 15 | #ifdef CONFIG_DONGLE_SCREEN_HORIZONTAL 16 | #ifdef CONFIG_DONGLE_SCREEN_FLIPPED 17 | int ret = display_set_orientation(display, DISPLAY_ORIENTATION_ROTATED_90); 18 | #else 19 | int ret = display_set_orientation(display, DISPLAY_ORIENTATION_ROTATED_270); 20 | #endif 21 | #else 22 | #ifdef CONFIG_DONGLE_SCREEN_FLIPPED 23 | int ret = display_set_orientation(display, DISPLAY_ORIENTATION_NORMAL); 24 | #else 25 | int ret = display_set_orientation(display, DISPLAY_ORIENTATION_ROTATED_180); 26 | #endif 27 | #endif 28 | 29 | if (ret < 0) 30 | { 31 | return ret; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | SYS_INIT(disp_set_orientation, APPLICATION, 60); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 janpfischer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(CONFIG_SHIELD_DONGLE_SCREEN) 2 | zephyr_library() 3 | zephyr_library_sources(${ZEPHYR_BASE}/misc/empty_file.c) 4 | zephyr_library_include_directories(${ZEPHYR_LVGL_MODULE_DIR}) 5 | zephyr_library_include_directories(${ZEPHYR_BASE}/lib/gui/lvgl/) 6 | zephyr_library_include_directories(${ZEPHYR_BASE}/drivers) 7 | zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) 8 | zephyr_library_include_directories(${ZEPHYR_CURRENT_MODULE_DIR}/include) 9 | zephyr_library_include_directories(${ZEPHYR_CURRENT_CMAKE_DIR}/include) 10 | zephyr_library_include_directories(include) 11 | zephyr_library_sources(src/brightness.c) 12 | zephyr_library_sources(src/custom_status_screen.c) 13 | zephyr_library_sources(src/screen_rotate_init.c) 14 | zephyr_library_sources(src/widgets/output_status.c) 15 | zephyr_library_sources(src/widgets/battery_status.c) 16 | zephyr_library_sources(src/widgets/layer_status.c) 17 | zephyr_library_sources(src/widgets/wpm_status.c) 18 | zephyr_library_sources(src/widgets/mod_status.c) 19 | file(GLOB font_sources src/fonts/*.c) 20 | zephyr_library_sources(${font_sources}) 21 | endif() -------------------------------------------------------------------------------- /docs/nice_nano_wire_guide.md: -------------------------------------------------------------------------------- 1 | ## Wiring Guide for nice!nano and ProMicro/SuperMini nRF52840 2 | 3 | | 1.69" Display | nice!nano Pin | 4 | |-------------------------------|-----------| 5 | | VCC | VCC | 6 | | GND | GND | 7 | | LCD_DIN | 115 | 8 | | LCD_CLK | 113 | 9 | | LCD_CS | 106 | 10 | | LCD_DC | 104 | 11 | | LCD_RST | 011 | 12 | | LCD_BL | 010 | 13 | | TP_SDA | No Connect | 14 | | TP_SCL | No Connect | 15 | | TP_RST | No Connect | 16 | | TP_IRQ | No Connect | 17 | 18 | | APDS9960 Light Sensor | nice!nano Pin | 19 | |-------------------------------|-----------| 20 | | VIN | VCC | 21 | | 3Vo | No Connect | 22 | | GND | GND | 23 | | SCL | 020 | 24 | | SDA | 017 | 25 | | INT | 100 | 26 | 27 | ## Installation 28 | 29 | Follow README installation and replace `seeeduino_xiao_ble` with `nice_nano_v2` for step 3 in your `build.yaml`. 30 | 31 | [3D print rear cap](/docs/3d_files/) modified and tested with SuperMini nRF52840. 32 | - Two versions, with and without reset button. 33 | - 6x6x6mm tactile button used for reset. 34 | - No supports needed. 35 | - Rear cap only. Print the display mount and main body from [Prospector](https://github.com/carrefinho/prospector/tree/main/case). 36 | 37 | nice!nano fit untested, might be tight. Would like feedback. -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/layer_status.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #include 8 | #include 9 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); 19 | 20 | struct layer_status_state 21 | { 22 | uint8_t index; 23 | const char *label; 24 | }; 25 | 26 | static void set_layer_symbol(lv_obj_t *label, struct layer_status_state state) 27 | { 28 | if (state.label == NULL) 29 | { 30 | char text[7] = {}; 31 | 32 | sprintf(text, "%i", state.index); 33 | 34 | lv_label_set_text(label, text); 35 | } 36 | else 37 | { 38 | char text[13] = {}; 39 | 40 | snprintf(text, sizeof(text), "%s", state.label); 41 | 42 | lv_label_set_text(label, text); 43 | } 44 | } 45 | 46 | static void layer_status_update_cb(struct layer_status_state state) 47 | { 48 | struct zmk_widget_layer_status *widget; 49 | SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_layer_symbol(widget->obj, state); } 50 | } 51 | 52 | static struct layer_status_state layer_status_get_state(const zmk_event_t *eh) 53 | { 54 | uint8_t index = zmk_keymap_highest_layer_active(); 55 | return (struct layer_status_state){ 56 | .index = index, 57 | .label = zmk_keymap_layer_name(index)}; 58 | } 59 | 60 | ZMK_DISPLAY_WIDGET_LISTENER(widget_layer_status, struct layer_status_state, layer_status_update_cb, 61 | layer_status_get_state) 62 | 63 | ZMK_SUBSCRIPTION(widget_layer_status, zmk_layer_state_changed); 64 | 65 | int zmk_widget_layer_status_init(struct zmk_widget_layer_status *widget, lv_obj_t *parent) 66 | { 67 | widget->obj = lv_label_create(parent); 68 | 69 | lv_obj_set_style_text_font(widget->obj, &lv_font_montserrat_40, 0); 70 | 71 | sys_slist_append(&widgets, &widget->node); 72 | 73 | widget_layer_status_init(); 74 | return 0; 75 | } 76 | 77 | lv_obj_t *zmk_widget_layer_status_obj(struct zmk_widget_layer_status *widget) 78 | { 79 | return widget->obj; 80 | } -------------------------------------------------------------------------------- /drivers/display/display_st7789v.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Marc Reilly 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | #ifndef ST7789V_DISPLAY_DRIVER_H__ 7 | #define ST7789V_DISPLAY_DRIVER_H__ 8 | 9 | #include 10 | 11 | #define ST7789V_CMD_NOP 0x00 12 | #define ST7789V_CMD_SW_RESET 0x01 13 | 14 | #define ST7789V_CMD_SLEEP_IN 0x10 15 | #define ST7789V_CMD_SLEEP_OUT 0x11 16 | #define ST7789V_CMD_INV_OFF 0x20 17 | #define ST7789V_CMD_INV_ON 0x21 18 | #define ST7789V_CMD_GAMSET 0x26 19 | #define ST7789V_CMD_DISP_OFF 0x28 20 | #define ST7789V_CMD_DISP_ON 0x29 21 | 22 | #define ST7789V_CMD_CASET 0x2a 23 | #define ST7789V_CMD_RASET 0x2b 24 | #define ST7789V_CMD_RAMWR 0x2c 25 | 26 | #define ST7789V_CMD_MADCTL 0x36 27 | #define ST7789V_MADCTL_MY_TOP_TO_BOTTOM 0x00 28 | #define ST7789V_MADCTL_MY_BOTTOM_TO_TOP 0x80 29 | #define ST7789V_MADCTL_MX_LEFT_TO_RIGHT 0x00 30 | #define ST7789V_MADCTL_MX_RIGHT_TO_LEFT 0x40 31 | #define ST7789V_MADCTL_MV_REVERSE_MODE 0x20 32 | #define ST7789V_MADCTL_MV_NORMAL_MODE 0x00 33 | #define ST7789V_MADCTL_ML 0x10 34 | #define ST7789V_MADCTL_RBG 0x00 35 | #define ST7789V_MADCTL_BGR 0x08 36 | #define ST7789V_MADCTL_MH_LEFT_TO_RIGHT 0x00 37 | #define ST7789V_MADCTL_MH_RIGHT_TO_LEFT 0x04 38 | 39 | #define ST7789V_CMD_COLMOD 0x3a 40 | #define ST7789V_COLMOD_RGB_65K (0x5 << 4) 41 | #define ST7789V_COLMOD_RGB_262K (0x6 << 4) 42 | #define ST7789V_COLMOD_FMT_12bit (3) 43 | #define ST7789V_COLMOD_FMT_16bit (5) 44 | #define ST7789V_COLMOD_FMT_18bit (6) 45 | 46 | #define ST7789V_CMD_RAMCTRL 0xb0 47 | #define ST7789V_CMD_RGBCTRL 0xb1 48 | #define ST7789V_CMD_PORCTRL 0xb2 49 | #define ST7789V_CMD_CMD2EN 0xdf 50 | #define ST7789V_CMD_DGMEN 0xba 51 | #define ST7789V_CMD_GCTRL 0xb7 52 | #define ST7789V_CMD_VCOMS 0xbb 53 | 54 | #define ST7789V_CMD_LCMCTRL 0xc0 55 | #define ST7789V_LCMCTRL_XMY 0x40 56 | #define ST7789V_LCMCTRL_XBGR 0x20 57 | #define ST7789V_LCMCTRL_XINV 0x10 58 | #define ST7789V_LCMCTRL_XMX 0x08 59 | #define ST7789V_LCMCTRL_XMH 0x04 60 | #define ST7789V_LCMCTRL_XMV 0x02 61 | 62 | #define ST7789V_CMD_VDVVRHEN 0xc2 63 | #define ST7789V_CMD_VRH 0xc3 64 | #define ST7789V_CMD_VDS 0xc4 65 | #define ST7789V_CMD_FRCTRL2 0xc6 66 | #define ST7789V_CMD_PWCTRL1 0xd0 67 | 68 | #define ST7789V_CMD_PVGAMCTRL 0xe0 69 | #define ST7789V_CMD_NVGAMCTRL 0xe1 70 | 71 | #define ST7789V_CMD_NONE 0xff 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/mod_status.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "mod_status.h" 6 | #include // <-- Wichtig für LV_FONT_DECLARE 7 | 8 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 9 | 10 | static void update_mod_status(struct zmk_widget_mod_status *widget) 11 | { 12 | uint8_t mods = zmk_hid_get_keyboard_report()->body.modifiers; 13 | char text[32] = ""; 14 | int idx = 0; 15 | 16 | // Temporäre Puffer für Symbole 17 | char *syms[4]; 18 | int n = 0; 19 | 20 | if (mods & (MOD_LCTL | MOD_RCTL)) 21 | syms[n++] = "󰘴"; 22 | if (mods & (MOD_LSFT | MOD_RSFT)) 23 | syms[n++] = "󰘶"; // U+F0636 24 | if (mods & (MOD_LALT | MOD_RALT)) 25 | syms[n++] = "󰘵"; // U+F0635 26 | if (mods & (MOD_LGUI | MOD_RGUI)) 27 | // set next syms according to CONFIG_DONGLE_SCREEN_SYSTEM (0,1,2) 28 | #if CONFIG_DONGLE_SCREEN_SYSTEM_ICON == 1 29 | syms[n++] = "󰌽"; // U+DF3D 30 | #elif CONFIG_DONGLE_SCREEN_SYSTEM_ICON == 2 31 | syms[n++] = ""; // U+E62A 32 | #else 33 | syms[n++] = "󰘳"; // U+F0633 34 | #endif 35 | 36 | for (int i = 0; i < n; ++i) 37 | { 38 | if (i > 0) 39 | idx += snprintf(&text[idx], sizeof(text) - idx, " "); 40 | idx += snprintf(&text[idx], sizeof(text) - idx, "%s", syms[i]); 41 | } 42 | 43 | lv_label_set_text(widget->label, idx ? text : ""); 44 | } 45 | 46 | static void mod_status_timer_cb(struct k_timer *timer) 47 | { 48 | struct zmk_widget_mod_status *widget = k_timer_user_data_get(timer); 49 | update_mod_status(widget); 50 | } 51 | 52 | static struct k_timer mod_status_timer; 53 | 54 | int zmk_widget_mod_status_init(struct zmk_widget_mod_status *widget, lv_obj_t *parent) 55 | { 56 | widget->obj = lv_obj_create(parent); 57 | lv_obj_set_size(widget->obj, 180, 40); 58 | 59 | widget->label = lv_label_create(widget->obj); 60 | lv_obj_align(widget->label, LV_ALIGN_CENTER, 0, 0); 61 | lv_label_set_text(widget->label, "-"); 62 | lv_obj_set_style_text_font(widget->label, &NerdFonts_Regular_40, 0); // <-- NerdFont setzen 63 | 64 | k_timer_init(&mod_status_timer, mod_status_timer_cb, NULL); 65 | k_timer_user_data_set(&mod_status_timer, widget); 66 | k_timer_start(&mod_status_timer, K_MSEC(100), K_MSEC(100)); 67 | 68 | return 0; 69 | } 70 | 71 | lv_obj_t *zmk_widget_mod_status_obj(struct zmk_widget_mod_status *widget) 72 | { 73 | return widget->obj; 74 | } 75 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/wpm_status.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "wpm_status.h" 17 | #include 18 | 19 | static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); 20 | struct wpm_status_state 21 | { 22 | int wpm; 23 | }; 24 | 25 | static struct wpm_status_state get_state(const zmk_event_t *_eh) 26 | { 27 | const struct zmk_wpm_state_changed *ev = as_zmk_wpm_state_changed(_eh); 28 | 29 | return (struct wpm_status_state){ 30 | .wpm = ev ? ev->state : 0}; 31 | } 32 | 33 | static void set_wpm(struct zmk_widget_wpm_status *widget, struct wpm_status_state state) 34 | { 35 | 36 | char wpm_text[12]; 37 | snprintf(wpm_text, sizeof(wpm_text), "%i", state.wpm); 38 | lv_label_set_text(widget->wpm_label, wpm_text); 39 | } 40 | 41 | static void wpm_status_update_cb(struct wpm_status_state state) 42 | { 43 | struct zmk_widget_wpm_status *widget; 44 | SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) 45 | { 46 | set_wpm(widget, state); 47 | } 48 | } 49 | 50 | ZMK_DISPLAY_WIDGET_LISTENER(widget_wpm_status, struct wpm_status_state, 51 | wpm_status_update_cb, get_state) 52 | ZMK_SUBSCRIPTION(widget_wpm_status, zmk_wpm_state_changed); 53 | 54 | // output_status.c 55 | int zmk_widget_wpm_status_init(struct zmk_widget_wpm_status *widget, lv_obj_t *parent) 56 | { 57 | widget->obj = lv_obj_create(parent); 58 | lv_obj_set_size(widget->obj, 240, 77); 59 | 60 | widget->wpm_label = lv_label_create(widget->obj); 61 | lv_obj_align(widget->wpm_label, LV_ALIGN_TOP_LEFT, 0, 0); 62 | 63 | // Only here as a sample 64 | // widget->font_test = lv_label_create(widget->obj); 65 | // lv_obj_set_style_text_font(widget->font_test, &NerdFonts_Regular_20, 0); 66 | // lv_obj_align(widget->font_test, LV_ALIGN_TOP_RIGHT, -80, 0); 67 | 68 | // Only here as a sample 69 | // lv_label_set_text(widget->font_test, "󰕓󰘳󰘵󰘶"); 70 | // TODO: Explizit als UTF-8 wert setzen? 71 | 72 | sys_slist_append(&widgets, &widget->node); 73 | 74 | widget_wpm_status_init(); 75 | return 0; 76 | } 77 | 78 | lv_obj_t *zmk_widget_wpm_status_obj(struct zmk_widget_wpm_status *widget) 79 | { 80 | return widget->obj; 81 | } 82 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/boards/nice_nano_v2.overlay: -------------------------------------------------------------------------------- 1 | / { 2 | pwmleds { 3 | compatible = "pwm-leds"; 4 | disp_bl: pwm_led_1 { 5 | pwms = <&pwm1 0 PWM_MSEC(1) PWM_POLARITY_NORMAL>; 6 | }; 7 | }; 8 | }; 9 | 10 | &spi2 { 11 | status = "disabled"; 12 | }; 13 | 14 | &pinctrl { 15 | spi3_default: spi3_default { 16 | group1 { 17 | psels = , 18 | , 19 | ; 20 | }; 21 | }; 22 | 23 | spi3_sleep: spi3_sleep { 24 | group1 { 25 | psels = , 26 | , 27 | ; 28 | low-power-enable; 29 | }; 30 | }; 31 | 32 | pwm1_default: pwm1_default { 33 | group1 { 34 | psels = ; 35 | nordic,invert; 36 | }; 37 | }; 38 | 39 | pwm1_sleep: pwm1_sleep { 40 | group1 { 41 | psels = ; 42 | low-power-enable; 43 | }; 44 | }; 45 | }; 46 | 47 | disp_spi: &spi3 { 48 | status = "okay"; 49 | pinctrl-0 = <&spi3_default>; 50 | pinctrl-1 = <&spi3_sleep>; 51 | pinctrl-names = "default", "sleep"; 52 | cs-gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; 53 | 54 | st7789: st7789v@0 { 55 | compatible = "sitronix,st7789v"; 56 | spi-max-frequency = <31000000>; 57 | reg = <0>; 58 | cmd-data-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; 59 | reset-gpios = <&gpio0 11 GPIO_ACTIVE_LOW>; 60 | width = <240>; 61 | height = <280>; 62 | x-offset = <0>; 63 | y-offset = <20>; 64 | vcom = <0x19>; 65 | gctrl = <0x35>; 66 | vrhs = <0x12>; 67 | vdvs = <0x20>; 68 | mdac = <0x00>; 69 | gamma = <0x01>; 70 | colmod = <0x05>; 71 | lcm = <0x2c>; 72 | porch-param = [ 0c 0c 00 33 33 ]; 73 | cmd2en-param = [ 5a 69 02 01 ]; 74 | pwctrl1-param = [ a4 a1 ]; 75 | pvgam-param = [ D0 04 0D 11 13 2B 3F 54 4C 18 0D 0B 1F 23 ]; 76 | nvgam-param = [ D0 04 0C 11 13 2C 3F 44 51 2F 1F 1F 20 23 ]; 77 | ram-param = [ 00 F0 ]; 78 | rgb-param = [ CD 08 14 ]; 79 | }; 80 | }; 81 | 82 | &i2c0 { 83 | status = "okay"; 84 | 85 | apds9960: apds9960@39 { 86 | compatible = "avago,apds9960"; 87 | status = "okay"; 88 | reg = <0x39>; 89 | int-gpios = <&gpio1 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; 90 | }; 91 | }; 92 | 93 | &pwm1 { 94 | status = "okay"; 95 | pinctrl-0 = <&pwm1_default>; 96 | pinctrl-1 = <&pwm1_sleep>; 97 | pinctrl-names = "default", "sleep"; 98 | }; -------------------------------------------------------------------------------- /boards/shields/dongle_screen/boards/seeeduino_xiao_ble.overlay: -------------------------------------------------------------------------------- 1 | / { 2 | pwmleds { 3 | compatible = "pwm-leds"; 4 | disp_bl: pwm_led_1 { 5 | pwms = <&pwm1 0 PWM_MSEC(1) PWM_POLARITY_NORMAL>; 6 | }; 7 | }; 8 | }; 9 | 10 | &spi2 { 11 | status = "disabled"; 12 | }; 13 | 14 | &pinctrl { 15 | spi3_default: spi3_default { 16 | group1 { 17 | psels = , 18 | , 19 | ; 20 | }; 21 | }; 22 | 23 | spi3_sleep: spi3_sleep { 24 | group1 { 25 | psels = , 26 | , 27 | ; 28 | low-power-enable; 29 | }; 30 | }; 31 | 32 | pwm1_default: pwm1_default { 33 | group1 { 34 | psels = ; 35 | nordic,invert; 36 | }; 37 | }; 38 | 39 | pwm1_sleep: pwm1_sleep { 40 | group1 { 41 | psels = ; 42 | low-power-enable; 43 | }; 44 | }; 45 | }; 46 | 47 | disp_spi: &spi3 { 48 | status = "okay"; 49 | pinctrl-0 = <&spi3_default>; 50 | pinctrl-1 = <&spi3_sleep>; 51 | pinctrl-names = "default", "sleep"; 52 | cs-gpios = <&xiao_d 9 GPIO_ACTIVE_LOW>; 53 | 54 | st7789: st7789v@0 { 55 | compatible = "sitronix,st7789v"; 56 | spi-max-frequency = <31000000>; 57 | reg = <0>; 58 | cmd-data-gpios = <&xiao_d 7 GPIO_ACTIVE_LOW>; 59 | reset-gpios = <&xiao_d 3 GPIO_ACTIVE_LOW>; 60 | width = <240>; 61 | height = <280>; 62 | x-offset = <0>; 63 | y-offset = <20>; 64 | vcom = <0x19>; 65 | gctrl = <0x35>; 66 | vrhs = <0x12>; 67 | vdvs = <0x20>; 68 | mdac = <0x00>; 69 | gamma = <0x01>; 70 | colmod = <0x05>; 71 | lcm = <0x2c>; 72 | porch-param = [ 0c 0c 00 33 33 ]; 73 | cmd2en-param = [ 5a 69 02 01 ]; 74 | pwctrl1-param = [ a4 a1 ]; 75 | pvgam-param = [ D0 04 0D 11 13 2B 3F 54 4C 18 0D 0B 1F 23 ]; 76 | nvgam-param = [ D0 04 0C 11 13 2C 3F 44 51 2F 1F 1F 20 23 ]; 77 | ram-param = [ 00 F0 ]; 78 | rgb-param = [ CD 08 14 ]; 79 | }; 80 | }; 81 | 82 | &i2c0 { 83 | status = "okay"; 84 | 85 | apds9960: apds9960@39 { 86 | compatible = "avago,apds9960"; 87 | status = "okay"; 88 | reg = <0x39>; 89 | int-gpios = <&xiao_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; 90 | }; 91 | }; 92 | 93 | &pwm1 { 94 | status = "okay"; 95 | pinctrl-0 = <&pwm1_default>; 96 | pinctrl-1 = <&pwm1_sleep>; 97 | pinctrl-names = "default", "sleep"; 98 | }; 99 | 100 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/custom_status_screen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #include "custom_status_screen.h" 8 | 9 | #if CONFIG_DONGLE_SCREEN_OUTPUT_ACTIVE 10 | #include "widgets/output_status.h" 11 | static struct zmk_widget_output_status output_status_widget; 12 | #endif 13 | 14 | #if CONFIG_DONGLE_SCREEN_LAYER_ACTIVE 15 | #include "widgets/layer_status.h" 16 | static struct zmk_widget_layer_status layer_status_widget; 17 | #endif 18 | 19 | #if CONFIG_DONGLE_SCREEN_BATTERY_ACTIVE 20 | #include "widgets/battery_status.h" 21 | static struct zmk_widget_dongle_battery_status dongle_battery_status_widget; 22 | #endif 23 | 24 | #if CONFIG_DONGLE_SCREEN_WPM_ACTIVE 25 | #include "widgets/wpm_status.h" 26 | static struct zmk_widget_wpm_status wpm_status_widget; 27 | #endif 28 | 29 | #if CONFIG_DONGLE_SCREEN_MODIFIER_ACTIVE 30 | #include "widgets/mod_status.h" 31 | static struct zmk_widget_mod_status mod_widget; 32 | #endif 33 | 34 | #include 35 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 36 | 37 | lv_style_t global_style; 38 | 39 | lv_obj_t *zmk_display_status_screen() 40 | { 41 | lv_obj_t *screen; 42 | 43 | screen = lv_obj_create(NULL); 44 | lv_obj_set_style_bg_color(screen, lv_color_hex(0x000000), LV_PART_MAIN); 45 | lv_obj_set_style_bg_opa(screen, 255, LV_PART_MAIN); 46 | 47 | lv_style_init(&global_style); 48 | // lv_style_set_text_font(&global_style, &lv_font_unscii_8); // ToDo: Font is not recognized 49 | lv_style_set_text_color(&global_style, lv_color_white()); 50 | lv_style_set_text_letter_space(&global_style, 1); 51 | lv_style_set_text_line_space(&global_style, 1); 52 | lv_obj_add_style(screen, &global_style, LV_PART_MAIN); 53 | 54 | #if CONFIG_DONGLE_SCREEN_OUTPUT_ACTIVE 55 | zmk_widget_output_status_init(&output_status_widget, screen); 56 | lv_obj_align(zmk_widget_output_status_obj(&output_status_widget), LV_ALIGN_TOP_MID, 0, 10); 57 | #endif 58 | 59 | #if CONFIG_DONGLE_SCREEN_BATTERY_ACTIVE 60 | zmk_widget_dongle_battery_status_init(&dongle_battery_status_widget, screen); 61 | lv_obj_align(zmk_widget_dongle_battery_status_obj(&dongle_battery_status_widget), LV_ALIGN_BOTTOM_MID, 0, 0); 62 | #endif 63 | 64 | #if CONFIG_DONGLE_SCREEN_WPM_ACTIVE 65 | zmk_widget_wpm_status_init(&wpm_status_widget, screen); 66 | lv_obj_align(zmk_widget_wpm_status_obj(&wpm_status_widget), LV_ALIGN_TOP_LEFT, 20, 20); 67 | #endif 68 | 69 | #if CONFIG_DONGLE_SCREEN_LAYER_ACTIVE 70 | zmk_widget_layer_status_init(&layer_status_widget, screen); 71 | lv_obj_align(zmk_widget_layer_status_obj(&layer_status_widget), LV_ALIGN_CENTER, 0, 0); 72 | #endif 73 | 74 | #if CONFIG_DONGLE_SCREEN_MODIFIER_ACTIVE 75 | zmk_widget_mod_status_init(&mod_widget, screen); 76 | lv_obj_align(zmk_widget_mod_status_obj(&mod_widget), LV_ALIGN_CENTER, 0, 35); 77 | #endif 78 | 79 | return screen; 80 | } -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/output_status.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "output_status.h" 22 | 23 | static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); 24 | 25 | lv_point_t selection_line_points[] = {{0, 0}, {13, 0}}; // will be replaced with lv_point_precise_t 26 | 27 | struct output_status_state 28 | { 29 | struct zmk_endpoint_instance selected_endpoint; 30 | int active_profile_index; 31 | bool active_profile_connected; 32 | bool active_profile_bonded; 33 | bool usb_is_hid_ready; 34 | }; 35 | 36 | static struct output_status_state get_state(const zmk_event_t *_eh) 37 | { 38 | return (struct output_status_state){ 39 | .selected_endpoint = zmk_endpoints_selected(), // 0 = USB , 1 = BLE 40 | .active_profile_index = zmk_ble_active_profile_index(), // 0-3 BLE profiles 41 | .active_profile_connected = zmk_ble_active_profile_is_connected(), // 0 = not connected, 1 = connected 42 | .active_profile_bonded = !zmk_ble_active_profile_is_open(), // 0 = BLE not bonded, 1 = bonded 43 | .usb_is_hid_ready = zmk_usb_is_hid_ready()}; // 0 = not ready, 1 = ready 44 | } 45 | 46 | static void set_status_symbol(struct zmk_widget_output_status *widget, struct output_status_state state) 47 | { 48 | const char *ble_color = "ffffff"; 49 | const char *usb_color = "ffffff"; 50 | char transport_text[50] = {}; 51 | if (state.usb_is_hid_ready == 0) 52 | { 53 | usb_color = "ff0000"; 54 | } 55 | else 56 | { 57 | usb_color = "ffffff"; 58 | } 59 | 60 | if (state.active_profile_connected == 1) 61 | { 62 | ble_color = "00ff00"; 63 | } 64 | else if (state.active_profile_bonded == 1) 65 | { 66 | ble_color = "0000ff"; 67 | } 68 | else 69 | { 70 | ble_color = "ffffff"; 71 | } 72 | 73 | switch (state.selected_endpoint.transport) 74 | { 75 | case ZMK_TRANSPORT_USB: 76 | snprintf(transport_text, sizeof(transport_text), "> #%s USB#\n#%s BLE#", usb_color, ble_color); 77 | break; 78 | case ZMK_TRANSPORT_BLE: 79 | snprintf(transport_text, sizeof(transport_text), "#%s USB#\n> #%s BLE#", usb_color, ble_color); 80 | break; 81 | } 82 | 83 | lv_label_set_recolor(widget->transport_label, true); 84 | lv_obj_set_style_text_align(widget->transport_label, LV_TEXT_ALIGN_RIGHT, 0); 85 | lv_label_set_text(widget->transport_label, transport_text); 86 | 87 | char ble_text[12]; 88 | 89 | snprintf(ble_text, sizeof(ble_text), "%d", state.active_profile_index + 1); 90 | // lv_obj_set_style_text_align(widget->ble_label, LV_TEXT_ALIGN_RIGHT, 0); 91 | lv_label_set_text(widget->ble_label, ble_text); 92 | } 93 | 94 | static void output_status_update_cb(struct output_status_state state) 95 | { 96 | struct zmk_widget_output_status *widget; 97 | SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) 98 | { 99 | set_status_symbol(widget, state); 100 | } 101 | } 102 | 103 | ZMK_DISPLAY_WIDGET_LISTENER(widget_output_status, struct output_status_state, 104 | output_status_update_cb, get_state) 105 | ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_changed); 106 | ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed); 107 | ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed); 108 | 109 | // output_status.c 110 | int zmk_widget_output_status_init(struct zmk_widget_output_status *widget, lv_obj_t *parent) 111 | { 112 | widget->obj = lv_obj_create(parent); 113 | lv_obj_set_size(widget->obj, 240, 77); 114 | 115 | widget->transport_label = lv_label_create(widget->obj); 116 | lv_obj_align(widget->transport_label, LV_ALIGN_TOP_RIGHT, -10, 10); 117 | 118 | widget->ble_label = lv_label_create(widget->obj); 119 | lv_obj_align(widget->ble_label, LV_ALIGN_TOP_RIGHT, -10, 56); 120 | 121 | sys_slist_append(&widgets, &widget->node); 122 | 123 | widget_output_status_init(); 124 | return 0; 125 | } 126 | 127 | lv_obj_t *zmk_widget_output_status_obj(struct zmk_widget_output_status *widget) 128 | { 129 | return widget->obj; 130 | } 131 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/Kconfig.defconfig: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 The ZMK Contributors 2 | # SPDX-License-Identifier: MIT 3 | 4 | if SHIELD_DONGLE_SCREEN 5 | 6 | choice ZMK_DISPLAY_STATUS_SCREEN 7 | default ZMK_DISPLAY_STATUS_SCREEN_CUSTOM 8 | endchoice 9 | 10 | config ZMK_DISPLAY_STATUS_SCREEN_CUSTOM 11 | select LV_USE_LABEL 12 | select LV_USE_IMG 13 | select LV_USE_CANVAS 14 | select LV_USE_ANIMIMG 15 | select LV_USE_ANIMATION 16 | select LV_USE_LINE 17 | select LV_FONT_UNSCII_8 18 | select ZMK_WPM 19 | imply ZMK_HID_INDICATORS 20 | 21 | config ZMK_DONGLE_DISPLAY_DONGLE_BATTERY 22 | bool "Show also the battery level of the dongle" 23 | depends on BT && (!ZMK_SPLIT_BLE || ZMK_SPLIT_ROLE_CENTRAL) 24 | 25 | choice ZMK_DISPLAY_WORK_QUEUE 26 | default ZMK_DISPLAY_WORK_QUEUE_DEDICATED 27 | endchoice 28 | 29 | config ZMK_DISPLAY_DEDICATED_THREAD_STACK_SIZE 30 | default 4096 31 | 32 | choice ST7789V_PIXEL_FORMAT 33 | default ST7789V_RGB565 34 | endchoice 35 | 36 | config LV_Z_VDB_SIZE 37 | default 100 38 | 39 | config LV_Z_MEM_POOL_SIZE 40 | default 10000 41 | 42 | config LV_DPI_DEF 43 | default 261 44 | 45 | config LV_Z_BITS_PER_PIXEL 46 | default 16 47 | 48 | choice LV_COLOR_DEPTH 49 | default LV_COLOR_DEPTH_16 50 | endchoice 51 | 52 | config LV_COLOR_16_SWAP 53 | default y 54 | 55 | config LV_DISP_DEF_REFR_PERIOD 56 | default 20 57 | 58 | choice LV_FONT_DEFAULT 59 | default LV_FONT_DEFAULT_MONTSERRAT_20 60 | endchoice 61 | 62 | config LV_FONT_MONTSERRAT_40 63 | default y 64 | 65 | config PWM 66 | default y 67 | 68 | config LED 69 | default y 70 | 71 | config DONGLE_SCREEN_HORIZONTAL 72 | bool "Screen orientation" 73 | default y 74 | help 75 | Orientation of the screen. In default it is horizontal (laying on the side) 76 | 77 | config DONGLE_SCREEN_FLIPPED 78 | bool "Screen flip option" 79 | default n 80 | help 81 | Should the screen orientation should be flipped in horizontal or vertical orientation? 82 | 83 | config DONGLE_SCREEN_IDLE_TIMEOUT_S 84 | int "Screen idle timeout in seconds (0 = never off)" 85 | default 600 86 | help 87 | Time in seconds after which the screen turns off when idle. 0 = never off. 88 | 89 | config DONGLE_SCREEN_MAX_BRIGHTNESS 90 | int "Maximum screen brightness (1-100)" 91 | default 80 92 | range 1 100 93 | help 94 | This is the brightness which is used when the dongle is powered on. This is also the maximum brightness used by the dimmer. 95 | 96 | config DONGLE_SCREEN_MIN_BRIGHTNESS 97 | int "Minimum screen brightness (1-99)" 98 | default 1 99 | range 1 99 100 | help 101 | Minimum screen brightness (1-99). This is the brightness used as a minimum value for brightness adjustments with the modifier keys and the ambient light sensor. 102 | 103 | config DONGLE_SCREEN_DEFAULT_BRIGHTNESS 104 | int "Default screen brightness (0-100)" 105 | default DONGLE_SCREEN_MAX_BRIGHTNESS 106 | range DONGLE_SCREEN_MIN_BRIGHTNESS DONGLE_SCREEN_MAX_BRIGHTNESS 107 | help 108 | The initial brightness level for the screen backlight. 109 | This value is used at startup and when the screen is turned on. 110 | It is defaulted to the maximum brightness but can be overridden. 111 | 112 | config DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL 113 | bool "Control screen brightness via keyboard" 114 | default y 115 | help 116 | Allows controlling the screen brightness via keyboard (e.g. F23/F24). 117 | 118 | config DONGLE_SCREEN_BRIGHTNESS_UP_KEYCODE 119 | int "Keycode for increasing screen brightness" 120 | default 115 # KC_F24 121 | help 122 | Keycode that increases the screen brightness (default: F23). 123 | 124 | config DONGLE_SCREEN_BRIGHTNESS_DOWN_KEYCODE 125 | int "Keycode for decreasing screen brightness" 126 | default 114 # KC_F23 127 | help 128 | Keycode that decreases the screen brightness (default: F24). 129 | 130 | config DONGLE_SCREEN_TOGGLE_KEYCODE 131 | int "Keycode for toggle screen off/on" 132 | default 113 # KC_F22 133 | help 134 | Keycode that toggles the screen off and on (default: F22). 135 | 136 | config DONGLE_SCREEN_BRIGHTNESS_STEP 137 | int "Step for brightness adjustment with keyboard" 138 | default 10 139 | help 140 | How much brightness steps (range MIN_BRIGHTNESS to MAX_BRIGHTNESS) should be applied per keystroke 141 | 142 | config DONGLE_SCREEN_WPM_ACTIVE 143 | bool "WPM Widget active" 144 | default y 145 | help 146 | If the WPM Widget should be active or not 147 | 148 | config DONGLE_SCREEN_MODIFIER_ACTIVE 149 | bool "Modifier Widget active" 150 | default y 151 | help 152 | If the Modifier Widget should be active or not 153 | 154 | config DONGLE_SCREEN_LAYER_ACTIVE 155 | bool "Layer Widget active" 156 | default y 157 | help 158 | If the Layer Widget should be active or not 159 | 160 | config DONGLE_SCREEN_OUTPUT_ACTIVE 161 | bool "Output Widget active" 162 | default y 163 | help 164 | If the Output Widget should be active or not 165 | 166 | config DONGLE_SCREEN_BATTERY_ACTIVE 167 | bool "Battery Widget active" 168 | default y 169 | help 170 | If the Battery Widget should be active or not 171 | 172 | config DONGLE_SCREEN_AMBIENT_LIGHT 173 | bool "Enable automatic brightness via ambient light sensor" 174 | default n 175 | select SENSOR 176 | select APDS9960 177 | help 178 | If enabled, the ambient light sensor will be used to automatically adjust screen brightness. 179 | 180 | config DONGLE_SCREEN_AMBIENT_LIGHT_TEST 181 | bool "Enable automatic brightness testing" 182 | default n 183 | help 184 | If enabled, the ambient light sensor will be mocked to adjust screen brightness. 185 | 186 | config DONGLE_SCREEN_AMBIENT_LIGHT_EVALUATION_INTERVAL_MS 187 | int "The interval for ambient light evaluation (in milliseconds)" 188 | default 1000 189 | help 190 | The interval how often the ambient light level should be evaluated. 191 | 192 | config DONGLE_SCREEN_AMBIENT_LIGHT_MIN_RAW_VALUE 193 | int "The minimum raw value for until your ambient sensor repects readings." 194 | default 0 195 | range 0 1999 196 | help 197 | Depending on the position and if the sensor is behind transparent plastic or not the sensor readings can be vary. Behind plastic the default value is proven good. If your ambient light changes are not too reactive you might change this. 198 | 199 | config DONGLE_SCREEN_AMBIENT_LIGHT_MAX_RAW_VALUE 200 | int "The maximum raw value for until your ambient sensor repects readings." 201 | default 100 202 | range 100 3000 203 | help 204 | Depending on the position and if the sensor is behind transparent plastic or not the sensor readings can be vary. Behind plastic the default value is proven good. If your ambient light changes are not too reactive you might change this. 205 | 206 | config DONGLE_SCREEN_BRIGHTNESS_MODIFIER 207 | int "The modifier to start the application with." 208 | default 0 209 | range -99 99 210 | help 211 | The modifier to start the dongle with. Useful if you found a modifier comfortable for you. Espacially for ambient light. Otherwise no need to change. 212 | config DONGLE_SCREEN_SYSTEM_ICON 213 | int "The icon to display when the 'LGUI'/'RGUI' is pressed. (0: macOS, 1: Linux, 2: Windows)" 214 | default 0 215 | range 0 2 216 | help 217 | The icon to display when the 'LGUI'/'RGUI' is pressed. Can be used to better match the Mod Widget to the underlying system. 218 | (0: macOS, 1: Linux, 2: Windows) 219 | endif -------------------------------------------------------------------------------- /modules/lvgl/lvgl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2019 Jan Van Winkel 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "lvgl_display.h" 11 | #include "lvgl_common_input.h" 12 | #ifdef CONFIG_LV_Z_USE_FILESYSTEM 13 | #include "lvgl_fs.h" 14 | #endif 15 | #ifdef CONFIG_LV_Z_MEM_POOL_SYS_HEAP 16 | #include "lvgl_mem.h" 17 | #endif 18 | #include LV_MEM_CUSTOM_INCLUDE 19 | 20 | #define LOG_LEVEL CONFIG_LV_LOG_LEVEL 21 | #include 22 | LOG_MODULE_REGISTER(lvgl); 23 | 24 | static lv_disp_drv_t disp_drv; 25 | struct lvgl_disp_data disp_data = { 26 | .blanking_on = false, 27 | }; 28 | 29 | #define DISPLAY_NODE DT_CHOSEN(zephyr_display) 30 | 31 | #ifdef CONFIG_LV_Z_BUFFER_ALLOC_STATIC 32 | 33 | static lv_disp_draw_buf_t disp_buf; 34 | 35 | #define DISPLAY_WIDTH DT_PROP(DISPLAY_NODE, width) 36 | #define DISPLAY_HEIGHT DT_PROP(DISPLAY_NODE, height) 37 | 38 | #define BUFFER_SIZE \ 39 | (CONFIG_LV_Z_BITS_PER_PIXEL * \ 40 | ((CONFIG_LV_Z_VDB_SIZE * DISPLAY_WIDTH * DISPLAY_HEIGHT) / 100) / 8) 41 | 42 | #define NBR_PIXELS_IN_BUFFER (BUFFER_SIZE * 8 / CONFIG_LV_Z_BITS_PER_PIXEL) 43 | 44 | /* NOTE: depending on chosen color depth buffer may be accessed using uint8_t *, 45 | * uint16_t * or uint32_t *, therefore buffer needs to be aligned accordingly to 46 | * prevent unaligned memory accesses. 47 | */ 48 | static uint8_t buf0[BUFFER_SIZE] 49 | #ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION 50 | Z_GENERIC_SECTION(.lvgl_buf) 51 | #endif 52 | __aligned(CONFIG_LV_Z_VDB_ALIGN); 53 | 54 | #ifdef CONFIG_LV_Z_DOUBLE_VDB 55 | static uint8_t buf1[BUFFER_SIZE] 56 | #ifdef CONFIG_LV_Z_VBD_CUSTOM_SECTION 57 | Z_GENERIC_SECTION(.lvgl_buf) 58 | #endif 59 | __aligned(CONFIG_LV_Z_VDB_ALIGN); 60 | #endif /* CONFIG_LV_Z_DOUBLE_VDB */ 61 | 62 | #endif /* CONFIG_LV_Z_BUFFER_ALLOC_STATIC */ 63 | 64 | #if CONFIG_LV_LOG_LEVEL != 0 65 | /* 66 | * In LVGLv8 the signature of the logging callback has changes and it no longer 67 | * takes the log level as an integer argument. Instead, the log level is now 68 | * already part of the buffer passed to the logging callback. It's not optimal 69 | * but we need to live with it and parse the buffer manually to determine the 70 | * level and then truncate the string we actually pass to the logging framework. 71 | */ 72 | static void lvgl_log(const char *buf) 73 | { 74 | /* 75 | * This is ugly and should be done in a loop or something but as it 76 | * turned out, Z_LOG()'s first argument (that specifies the log level) 77 | * cannot be an l-value... 78 | * 79 | * We also assume lvgl is sane and always supplies the level string. 80 | */ 81 | switch (buf[1]) { 82 | case 'E': 83 | LOG_ERR("%s", buf + strlen("[Error] ")); 84 | break; 85 | case 'W': 86 | LOG_WRN("%s", buf + strlen("Warn] ")); 87 | break; 88 | case 'I': 89 | LOG_INF("%s", buf + strlen("[Info] ")); 90 | break; 91 | case 'T': 92 | LOG_DBG("%s", buf + strlen("[Trace] ")); 93 | break; 94 | } 95 | } 96 | #endif 97 | 98 | #ifdef CONFIG_LV_Z_BUFFER_ALLOC_STATIC 99 | 100 | static int lvgl_allocate_rendering_buffers(lv_disp_drv_t *disp_driver) 101 | { 102 | struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_driver->user_data; 103 | int err = 0; 104 | 105 | if (data->cap.x_resolution <= DISPLAY_WIDTH) { 106 | disp_driver->hor_res = data->cap.x_resolution; 107 | } else { 108 | LOG_ERR("Horizontal resolution is larger than maximum"); 109 | err = -ENOTSUP; 110 | } 111 | 112 | if (data->cap.y_resolution <= DISPLAY_HEIGHT) { 113 | disp_driver->ver_res = data->cap.y_resolution; 114 | } else { 115 | LOG_ERR("Vertical resolution is larger than maximum"); 116 | err = -ENOTSUP; 117 | } 118 | 119 | disp_driver->draw_buf = &disp_buf; 120 | #ifdef CONFIG_LV_Z_DOUBLE_VDB 121 | lv_disp_draw_buf_init(disp_driver->draw_buf, &buf0, &buf1, NBR_PIXELS_IN_BUFFER); 122 | #else 123 | lv_disp_draw_buf_init(disp_driver->draw_buf, &buf0, NULL, NBR_PIXELS_IN_BUFFER); 124 | #endif /* CONFIG_LV_Z_DOUBLE_VDB */ 125 | 126 | return err; 127 | } 128 | 129 | #else 130 | 131 | static int lvgl_allocate_rendering_buffers(lv_disp_drv_t *disp_driver) 132 | { 133 | void *buf0 = NULL; 134 | void *buf1 = NULL; 135 | uint16_t buf_nbr_pixels; 136 | uint32_t buf_size; 137 | struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_driver->user_data; 138 | 139 | disp_driver->hor_res = data->cap.x_resolution; 140 | disp_driver->ver_res = data->cap.y_resolution; 141 | 142 | buf_nbr_pixels = (CONFIG_LV_Z_VDB_SIZE * disp_driver->hor_res * disp_driver->ver_res) / 100; 143 | /* one horizontal line is the minimum buffer requirement for lvgl */ 144 | if (buf_nbr_pixels < disp_driver->hor_res) { 145 | buf_nbr_pixels = disp_driver->hor_res; 146 | } 147 | 148 | switch (data->cap.current_pixel_format) { 149 | case PIXEL_FORMAT_ARGB_8888: 150 | buf_size = 4 * buf_nbr_pixels; 151 | break; 152 | case PIXEL_FORMAT_RGB_888: 153 | buf_size = 3 * buf_nbr_pixels; 154 | break; 155 | case PIXEL_FORMAT_RGB_565: 156 | buf_size = 2 * buf_nbr_pixels; 157 | break; 158 | case PIXEL_FORMAT_MONO01: 159 | case PIXEL_FORMAT_MONO10: 160 | buf_size = buf_nbr_pixels / 8; 161 | buf_size += (buf_nbr_pixels % 8) == 0 ? 0 : 1; 162 | break; 163 | default: 164 | return -ENOTSUP; 165 | } 166 | 167 | buf0 = LV_MEM_CUSTOM_ALLOC(buf_size); 168 | if (buf0 == NULL) { 169 | LOG_ERR("Failed to allocate memory for rendering buffer"); 170 | return -ENOMEM; 171 | } 172 | 173 | #ifdef CONFIG_LV_Z_DOUBLE_VDB 174 | buf1 = LV_MEM_CUSTOM_ALLOC(buf_size); 175 | if (buf1 == NULL) { 176 | LV_MEM_CUSTOM_FREE(buf0); 177 | LOG_ERR("Failed to allocate memory for rendering buffer"); 178 | return -ENOMEM; 179 | } 180 | #endif 181 | 182 | disp_driver->draw_buf = LV_MEM_CUSTOM_ALLOC(sizeof(lv_disp_draw_buf_t)); 183 | if (disp_driver->draw_buf == NULL) { 184 | LV_MEM_CUSTOM_FREE(buf0); 185 | LV_MEM_CUSTOM_FREE(buf1); 186 | LOG_ERR("Failed to allocate memory to store rendering buffers"); 187 | return -ENOMEM; 188 | } 189 | 190 | lv_disp_draw_buf_init(disp_driver->draw_buf, buf0, buf1, buf_nbr_pixels); 191 | return 0; 192 | } 193 | #endif /* CONFIG_LV_Z_BUFFER_ALLOC_STATIC */ 194 | 195 | static int lvgl_init(void) 196 | { 197 | const struct device *display_dev = DEVICE_DT_GET(DISPLAY_NODE); 198 | 199 | int err = 0; 200 | 201 | if (!device_is_ready(display_dev)) { 202 | LOG_ERR("Display device not ready."); 203 | return -ENODEV; 204 | } 205 | 206 | #ifdef CONFIG_LV_Z_MEM_POOL_SYS_HEAP 207 | lvgl_heap_init(); 208 | #endif 209 | 210 | #if CONFIG_LV_LOG_LEVEL != 0 211 | lv_log_register_print_cb(lvgl_log); 212 | #endif 213 | 214 | lv_init(); 215 | 216 | #ifdef CONFIG_LV_Z_USE_FILESYSTEM 217 | lvgl_fs_init(); 218 | #endif 219 | 220 | disp_data.display_dev = display_dev; 221 | display_get_capabilities(display_dev, &disp_data.cap); 222 | 223 | lv_disp_drv_init(&disp_drv); 224 | disp_drv.user_data = (void *)&disp_data; 225 | 226 | switch (disp_data.cap.current_orientation) { 227 | case DISPLAY_ORIENTATION_NORMAL: 228 | disp_drv.rotated = LV_DISP_ROT_NONE; 229 | break; 230 | case DISPLAY_ORIENTATION_ROTATED_90: 231 | disp_drv.rotated = LV_DISP_ROT_90; 232 | break; 233 | case DISPLAY_ORIENTATION_ROTATED_180: 234 | disp_drv.rotated = LV_DISP_ROT_180; 235 | break; 236 | case DISPLAY_ORIENTATION_ROTATED_270: 237 | disp_drv.rotated = LV_DISP_ROT_270; 238 | break; 239 | default: 240 | LOG_ERR("Invalid display orientation"); 241 | return -EINVAL; 242 | } 243 | 244 | #ifdef CONFIG_LV_Z_FULL_REFRESH 245 | disp_drv.full_refresh = 1; 246 | #endif 247 | 248 | err = lvgl_allocate_rendering_buffers(&disp_drv); 249 | if (err != 0) { 250 | return err; 251 | } 252 | 253 | if (set_lvgl_rendering_cb(&disp_drv) != 0) { 254 | LOG_ERR("Display not supported."); 255 | return -ENOTSUP; 256 | } 257 | 258 | if (lv_disp_drv_register(&disp_drv) == NULL) { 259 | LOG_ERR("Failed to register display device."); 260 | return -EPERM; 261 | } 262 | 263 | err = lvgl_init_input_devices(); 264 | if (err < 0) { 265 | LOG_ERR("Failed to initialize input devices."); 266 | return err; 267 | } 268 | 269 | return 0; 270 | } 271 | 272 | SYS_INIT(lvgl_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); 273 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/widgets/battery_status.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 The ZMK Contributors 3 | * 4 | * SPDX-License-Identifier: MIT 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "battery_status.h" 22 | #include "../brightness.h" 23 | 24 | #if IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY) 25 | #define SOURCE_OFFSET 1 26 | #else 27 | #define SOURCE_OFFSET 0 28 | #endif 29 | 30 | static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); 31 | 32 | struct battery_state { 33 | uint8_t source; 34 | uint8_t level; 35 | bool usb_present; 36 | }; 37 | 38 | struct battery_object { 39 | lv_obj_t *symbol; 40 | lv_obj_t *label; 41 | } battery_objects[ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT + SOURCE_OFFSET]; 42 | 43 | static lv_color_t battery_image_buffer[ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT + SOURCE_OFFSET][102 * 5]; 44 | 45 | // Peripheral reconnection tracking 46 | // ZMK sends battery events with level < 1 when peripherals disconnect 47 | static int8_t last_battery_levels[ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT + SOURCE_OFFSET]; 48 | 49 | static void init_peripheral_tracking(void) { 50 | for (int i = 0; i < (ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT + SOURCE_OFFSET); i++) { 51 | last_battery_levels[i] = -1; // -1 indicates never seen before 52 | } 53 | } 54 | 55 | static bool is_peripheral_reconnecting(uint8_t source, uint8_t new_level) { 56 | if (source >= (ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT + SOURCE_OFFSET)) { 57 | return false; 58 | } 59 | 60 | int8_t previous_level = last_battery_levels[source]; 61 | 62 | // Reconnection detected if: 63 | // 1. Previous level was < 1 (disconnected/unknown) AND 64 | // 2. New level is >= 1 (valid battery level) 65 | bool reconnecting = (previous_level < 1) && (new_level >= 1); 66 | 67 | if (reconnecting) { 68 | LOG_INF("Peripheral %d reconnection: %d%% -> %d%% (was %s)", 69 | source, previous_level, new_level, 70 | previous_level == -1 ? "never seen" : "disconnected"); 71 | } 72 | 73 | return reconnecting; 74 | } 75 | 76 | static void draw_battery(lv_obj_t *canvas, uint8_t level, bool usb_present) { 77 | 78 | if (level < 1) 79 | { 80 | lv_canvas_fill_bg(canvas, lv_palette_main(LV_PALETTE_RED), LV_OPA_COVER); 81 | } else if (level <= 10) { 82 | lv_canvas_fill_bg(canvas, lv_palette_main(LV_PALETTE_YELLOW), LV_OPA_COVER); 83 | } else { 84 | lv_canvas_fill_bg(canvas, lv_color_white(), LV_OPA_COVER); 85 | } 86 | 87 | 88 | lv_draw_rect_dsc_t rect_fill_dsc; 89 | lv_draw_rect_dsc_init(&rect_fill_dsc); 90 | rect_fill_dsc.bg_color = lv_color_black(); 91 | 92 | 93 | 94 | lv_canvas_set_px(canvas, 0, 0, lv_color_black()); 95 | lv_canvas_set_px(canvas, 0, 4, lv_color_black()); 96 | lv_canvas_set_px(canvas, 101, 0, lv_color_black()); 97 | lv_canvas_set_px(canvas, 101, 4, lv_color_black()); 98 | 99 | if (level <= 99 && level > 0) 100 | { 101 | lv_canvas_draw_rect(canvas, level, 1, 100 - level, 3, &rect_fill_dsc); 102 | lv_canvas_set_px(canvas, 100, 1, lv_color_black()); 103 | lv_canvas_set_px(canvas, 100, 2, lv_color_black()); 104 | lv_canvas_set_px(canvas, 100, 3, lv_color_black()); 105 | } 106 | 107 | } 108 | 109 | static void set_battery_symbol(lv_obj_t *widget, struct battery_state state) { 110 | if (state.source >= ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT + SOURCE_OFFSET) { 111 | return; 112 | } 113 | 114 | // Check for reconnection using the existing battery level mechanism 115 | bool reconnecting = is_peripheral_reconnecting(state.source, state.level); 116 | 117 | // Update our tracking 118 | last_battery_levels[state.source] = state.level; 119 | 120 | 121 | // Wake screen on reconnection 122 | if (reconnecting) { 123 | #if CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S > 0 124 | LOG_INF("Peripheral %d reconnected (battery: %d%%), requesting screen wake", 125 | state.source, state.level); 126 | brightness_wake_screen_on_reconnect(); 127 | #else 128 | LOG_INF("Peripheral %d reconnected (battery: %d%%)", 129 | state.source, state.level); 130 | #endif 131 | } 132 | 133 | 134 | LOG_DBG("source: %d, level: %d, usb: %d", state.source, state.level, state.usb_present); 135 | lv_obj_t *symbol = battery_objects[state.source].symbol; 136 | lv_obj_t *label = battery_objects[state.source].label; 137 | 138 | draw_battery(symbol, state.level, state.usb_present); 139 | 140 | if (state.level > 0) { 141 | lv_obj_set_style_text_color(label, lv_color_white(), 0); 142 | lv_label_set_text_fmt(label, "%4u", state.level); 143 | } else { 144 | lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_RED), 0); 145 | lv_label_set_text(label, "X"); 146 | } 147 | 148 | if (state.level < 1) 149 | { 150 | lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_RED), 0); 151 | lv_label_set_text(label, "X"); 152 | } else if (state.level <= 10) { 153 | lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_YELLOW), 0); 154 | lv_label_set_text_fmt(label, "%4u", state.level); 155 | } else { 156 | lv_obj_set_style_text_color(label, lv_color_white(), 0); 157 | lv_label_set_text_fmt(label, "%4u", state.level); 158 | } 159 | 160 | 161 | 162 | lv_obj_clear_flag(symbol, LV_OBJ_FLAG_HIDDEN); 163 | lv_obj_move_foreground(symbol); 164 | lv_obj_clear_flag(label, LV_OBJ_FLAG_HIDDEN); 165 | lv_obj_move_foreground(label); 166 | 167 | } 168 | 169 | void battery_status_update_cb(struct battery_state state) { 170 | struct zmk_widget_dongle_battery_status *widget; 171 | SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_symbol(widget->obj, state); } 172 | } 173 | 174 | static struct battery_state peripheral_battery_status_get_state(const zmk_event_t *eh) { 175 | const struct zmk_peripheral_battery_state_changed *ev = as_zmk_peripheral_battery_state_changed(eh); 176 | return (struct battery_state){ 177 | .source = ev->source + SOURCE_OFFSET, 178 | .level = ev->state_of_charge, 179 | }; 180 | } 181 | 182 | static struct battery_state central_battery_status_get_state(const zmk_event_t *eh) { 183 | const struct zmk_battery_state_changed *ev = as_zmk_battery_state_changed(eh); 184 | return (struct battery_state) { 185 | .source = 0, 186 | .level = (ev != NULL) ? ev->state_of_charge : zmk_battery_state_of_charge(), 187 | #if IS_ENABLED(CONFIG_USB_DEVICE_STACK) 188 | .usb_present = zmk_usb_is_powered(), 189 | #endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ 190 | }; 191 | } 192 | 193 | static struct battery_state battery_status_get_state(const zmk_event_t *eh) { 194 | if (as_zmk_peripheral_battery_state_changed(eh) != NULL) { 195 | return peripheral_battery_status_get_state(eh); 196 | } else { 197 | return central_battery_status_get_state(eh); 198 | } 199 | } 200 | 201 | ZMK_DISPLAY_WIDGET_LISTENER(widget_dongle_battery_status, struct battery_state, 202 | battery_status_update_cb, battery_status_get_state) 203 | 204 | ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_peripheral_battery_state_changed); 205 | 206 | #if IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY) 207 | #if !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) 208 | 209 | ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_battery_state_changed); 210 | #if IS_ENABLED(CONFIG_USB_DEVICE_STACK) 211 | ZMK_SUBSCRIPTION(widget_dongle_battery_status, zmk_usb_conn_state_changed); 212 | #endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ 213 | #endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */ 214 | #endif /* IS_ENABLED(CONFIG_ZMK_DONGLE_DISPLAY_DONGLE_BATTERY) */ 215 | 216 | int zmk_widget_dongle_battery_status_init(struct zmk_widget_dongle_battery_status *widget, lv_obj_t *parent) { 217 | widget->obj = lv_obj_create(parent); 218 | 219 | lv_obj_set_size(widget->obj, 240, 40); 220 | 221 | for (int i = 0; i < ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT + SOURCE_OFFSET; i++) { 222 | lv_obj_t *image_canvas = lv_canvas_create(widget->obj); 223 | lv_obj_t *battery_label = lv_label_create(widget->obj); 224 | 225 | lv_canvas_set_buffer(image_canvas, battery_image_buffer[i], 102, 5, LV_IMG_CF_TRUE_COLOR); 226 | 227 | lv_obj_align(image_canvas, LV_ALIGN_BOTTOM_MID, -60 +(i * 120), -8); 228 | lv_obj_align(battery_label, LV_ALIGN_TOP_MID, -60 +(i * 120), 0); 229 | 230 | lv_obj_add_flag(image_canvas, LV_OBJ_FLAG_HIDDEN); 231 | lv_obj_add_flag(battery_label, LV_OBJ_FLAG_HIDDEN); 232 | 233 | battery_objects[i] = (struct battery_object){ 234 | .symbol = image_canvas, 235 | .label = battery_label, 236 | }; 237 | } 238 | 239 | sys_slist_append(&widgets, &widget->node); 240 | 241 | // Initialize peripheral tracking 242 | init_peripheral_tracking(); 243 | 244 | widget_dongle_battery_status_init(); 245 | 246 | return 0; 247 | } 248 | 249 | lv_obj_t *zmk_widget_dongle_battery_status_obj(struct zmk_widget_dongle_battery_status *widget) { 250 | return widget->obj; 251 | } -------------------------------------------------------------------------------- /drivers/display/display_st7789v.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Jan Van Winkel 3 | * Copyright (c) 2019 Nordic Semiconductor ASA 4 | * Copyright (c) 2019 Marc Reilly 5 | * Copyright (c) 2019 PHYTEC Messtechnik GmbH 6 | * Copyright (c) 2020 Endian Technologies AB 7 | * Copyright (c) 2022 Basalte bv 8 | * 9 | * SPDX-License-Identifier: Apache-2.0 10 | */ 11 | 12 | #define DT_DRV_COMPAT sitronix_st7789v 13 | 14 | #include "display_st7789v.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL 25 | #include 26 | LOG_MODULE_REGISTER(display_st7789v); 27 | 28 | struct st7789v_config { 29 | struct spi_dt_spec bus; 30 | struct gpio_dt_spec cmd_data_gpio; 31 | struct gpio_dt_spec reset_gpio; 32 | uint8_t vcom; 33 | uint8_t gctrl; 34 | bool vdv_vrh_enable; 35 | uint8_t vrh_value; 36 | uint8_t vdv_value; 37 | uint8_t mdac; 38 | uint8_t gamma; 39 | uint8_t colmod; 40 | uint8_t lcm; 41 | uint8_t porch_param[5]; 42 | uint8_t cmd2en_param[4]; 43 | uint8_t pwctrl1_param[2]; 44 | uint8_t pvgam_param[14]; 45 | uint8_t nvgam_param[14]; 46 | uint8_t ram_param[2]; 47 | uint8_t rgb_param[3]; 48 | uint16_t height; 49 | uint16_t width; 50 | }; 51 | 52 | struct st7789v_data { 53 | uint16_t x_offset; 54 | uint16_t y_offset; 55 | enum display_orientation orientation; 56 | }; 57 | 58 | #ifdef CONFIG_ST7789V_RGB565 59 | #define ST7789V_PIXEL_SIZE 2u 60 | #else 61 | #define ST7789V_PIXEL_SIZE 3u 62 | #endif 63 | 64 | static void st7789v_set_lcd_margins(const struct device *dev, uint16_t x_offset, uint16_t y_offset) 65 | { 66 | struct st7789v_data *data = dev->data; 67 | 68 | data->x_offset = x_offset; 69 | data->y_offset = y_offset; 70 | } 71 | 72 | static void st7789v_transmit(const struct device *dev, uint8_t cmd, uint8_t *tx_data, 73 | size_t tx_count) 74 | { 75 | const struct st7789v_config *config = dev->config; 76 | uint16_t data = cmd; 77 | 78 | struct spi_buf tx_buf = {.buf = &cmd, .len = 1}; 79 | struct spi_buf_set tx_bufs = {.buffers = &tx_buf, .count = 1}; 80 | 81 | if (config->cmd_data_gpio.port != NULL) { 82 | if (cmd != ST7789V_CMD_NONE) { 83 | gpio_pin_set_dt(&config->cmd_data_gpio, 1); 84 | spi_write_dt(&config->bus, &tx_bufs); 85 | } 86 | 87 | if (tx_data != NULL) { 88 | tx_buf.buf = tx_data; 89 | tx_buf.len = tx_count; 90 | gpio_pin_set_dt(&config->cmd_data_gpio, 0); 91 | spi_write_dt(&config->bus, &tx_bufs); 92 | } 93 | } else { 94 | tx_buf.buf = &data; 95 | tx_buf.len = 2; 96 | 97 | if (cmd != ST7789V_CMD_NONE) { 98 | spi_write_dt(&config->bus, &tx_bufs); 99 | } 100 | 101 | if (tx_data != NULL) { 102 | for (size_t index = 0; index < tx_count; ++index) { 103 | data = 0x0100 | tx_data[index]; 104 | spi_write_dt(&config->bus, &tx_bufs); 105 | } 106 | } 107 | } 108 | } 109 | 110 | static void st7789v_exit_sleep(const struct device *dev) 111 | { 112 | st7789v_transmit(dev, ST7789V_CMD_SLEEP_OUT, NULL, 0); 113 | k_sleep(K_MSEC(120)); 114 | } 115 | 116 | static void st7789v_reset_display(const struct device *dev) 117 | { 118 | LOG_DBG("Resetting display"); 119 | 120 | const struct st7789v_config *config = dev->config; 121 | if (config->reset_gpio.port != NULL) { 122 | k_sleep(K_MSEC(1)); 123 | gpio_pin_set_dt(&config->reset_gpio, 1); 124 | k_sleep(K_MSEC(6)); 125 | gpio_pin_set_dt(&config->reset_gpio, 0); 126 | k_sleep(K_MSEC(20)); 127 | } else { 128 | st7789v_transmit(dev, ST7789V_CMD_SW_RESET, NULL, 0); 129 | k_sleep(K_MSEC(5)); 130 | } 131 | } 132 | 133 | static int st7789v_blanking_on(const struct device *dev) 134 | { 135 | st7789v_transmit(dev, ST7789V_CMD_DISP_OFF, NULL, 0); 136 | return 0; 137 | } 138 | 139 | static int st7789v_blanking_off(const struct device *dev) 140 | { 141 | st7789v_transmit(dev, ST7789V_CMD_DISP_ON, NULL, 0); 142 | return 0; 143 | } 144 | 145 | static void st7789v_set_mem_area(const struct device *dev, const uint16_t x, const uint16_t y, 146 | const uint16_t w, const uint16_t h) 147 | { 148 | struct st7789v_data *data = dev->data; 149 | uint16_t spi_data[2]; 150 | 151 | uint16_t ram_x = x + data->x_offset; 152 | uint16_t ram_y = y + data->y_offset; 153 | 154 | spi_data[0] = sys_cpu_to_be16(ram_x); 155 | spi_data[1] = sys_cpu_to_be16(ram_x + w - 1); 156 | st7789v_transmit(dev, ST7789V_CMD_CASET, (uint8_t *)&spi_data[0], 4); 157 | 158 | spi_data[0] = sys_cpu_to_be16(ram_y); 159 | spi_data[1] = sys_cpu_to_be16(ram_y + h - 1); 160 | st7789v_transmit(dev, ST7789V_CMD_RASET, (uint8_t *)&spi_data[0], 4); 161 | } 162 | 163 | static int st7789v_write(const struct device *dev, const uint16_t x, const uint16_t y, 164 | const struct display_buffer_descriptor *desc, const void *buf) 165 | { 166 | const uint8_t *write_data_start = (uint8_t *)buf; 167 | uint16_t nbr_of_writes; 168 | uint16_t write_h; 169 | 170 | __ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width"); 171 | __ASSERT((desc->pitch * ST7789V_PIXEL_SIZE * desc->height) <= desc->buf_size, 172 | "Input buffer too small"); 173 | 174 | LOG_DBG("Writing %dx%d (w,h) @ %dx%d (x,y)", desc->width, desc->height, x, y); 175 | st7789v_set_mem_area(dev, x, y, desc->width, desc->height); 176 | 177 | if (desc->pitch > desc->width) { 178 | write_h = 1U; 179 | nbr_of_writes = desc->height; 180 | } else { 181 | write_h = desc->height; 182 | nbr_of_writes = 1U; 183 | } 184 | 185 | for (uint16_t write_cnt = 0U; write_cnt < nbr_of_writes; ++write_cnt) { 186 | st7789v_transmit(dev, write_cnt == 0U ? ST7789V_CMD_RAMWR : ST7789V_CMD_NONE, 187 | (void *)write_data_start, 188 | desc->width * ST7789V_PIXEL_SIZE * write_h); 189 | write_data_start += (desc->pitch * ST7789V_PIXEL_SIZE); 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | static void st7789v_get_capabilities(const struct device *dev, 196 | struct display_capabilities *capabilities) 197 | { 198 | const struct st7789v_config *config = dev->config; 199 | const struct st7789v_data *data = dev->data; 200 | 201 | memset(capabilities, 0, sizeof(struct display_capabilities)); 202 | capabilities->x_resolution = config->width; 203 | capabilities->y_resolution = config->height; 204 | 205 | #ifdef CONFIG_ST7789V_RGB565 206 | capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_565; 207 | capabilities->current_pixel_format = PIXEL_FORMAT_RGB_565; 208 | #else 209 | capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_888; 210 | capabilities->current_pixel_format = PIXEL_FORMAT_RGB_888; 211 | #endif 212 | capabilities->current_orientation = data->orientation; 213 | } 214 | 215 | static int st7789v_set_pixel_format(const struct device *dev, 216 | const enum display_pixel_format pixel_format) 217 | { 218 | #ifdef CONFIG_ST7789V_RGB565 219 | if (pixel_format == PIXEL_FORMAT_RGB_565) { 220 | #else 221 | if (pixel_format == PIXEL_FORMAT_RGB_888) { 222 | #endif 223 | return 0; 224 | } 225 | LOG_ERR("Pixel format change not implemented"); 226 | return -ENOTSUP; 227 | } 228 | 229 | static int st7789v_set_orientation(const struct device *dev, 230 | const enum display_orientation orientation) 231 | { 232 | const struct st7789v_config *config = dev->config; 233 | struct st7789v_data *data = dev->data; 234 | 235 | /* only modifying the MY, MX, MV bits, keep existing MDAC config */ 236 | uint8_t tx_data = config->mdac & (ST7789V_MADCTL_ML | ST7789V_MADCTL_BGR | 237 | ST7789V_MADCTL_MH_RIGHT_TO_LEFT); 238 | 239 | uint16_t x_offset = 0; 240 | uint16_t y_offset = 0; 241 | 242 | uint16_t row_offset = 0; 243 | uint16_t col_offset = 0; 244 | 245 | // if (config->width < 240) { 246 | /* 135x240 display */ 247 | row_offset = data->y_offset; 248 | col_offset = data->x_offset; 249 | // } else { 250 | // /* 240x320 and 240x240 displays */ 251 | // row_offset = (320 - config->height); 252 | // col_offset = (240 - config->width); 253 | // } 254 | 255 | switch (orientation) { 256 | case DISPLAY_ORIENTATION_NORMAL: 257 | tx_data |= ST7789V_MADCTL_MV_NORMAL_MODE; 258 | x_offset = data->x_offset; 259 | y_offset = data->y_offset; 260 | break; 261 | 262 | case DISPLAY_ORIENTATION_ROTATED_90: 263 | tx_data |= (ST7789V_MADCTL_MY_BOTTOM_TO_TOP | ST7789V_MADCTL_MV_REVERSE_MODE); 264 | x_offset = row_offset; 265 | y_offset = col_offset; 266 | break; 267 | 268 | case DISPLAY_ORIENTATION_ROTATED_180: 269 | tx_data |= (ST7789V_MADCTL_MY_BOTTOM_TO_TOP | ST7789V_MADCTL_MX_RIGHT_TO_LEFT); 270 | x_offset = col_offset; 271 | y_offset = row_offset; 272 | break; 273 | 274 | case DISPLAY_ORIENTATION_ROTATED_270: 275 | tx_data |= (ST7789V_MADCTL_MX_RIGHT_TO_LEFT | ST7789V_MADCTL_MV_REVERSE_MODE); 276 | x_offset = data->y_offset; 277 | y_offset = data->x_offset; 278 | break; 279 | 280 | default: 281 | LOG_ERR("Error changing display orientation"); 282 | return -ENOTSUP; 283 | } 284 | 285 | st7789v_set_lcd_margins(dev, x_offset, y_offset); 286 | st7789v_transmit(dev, ST7789V_CMD_MADCTL, &tx_data, 1U); 287 | data->orientation = orientation; 288 | LOG_INF("Changed orientation to: '%d'", data->orientation); 289 | 290 | return 0; 291 | } 292 | 293 | static void st7789v_lcd_init(const struct device *dev) 294 | { 295 | struct st7789v_data *data = dev->data; 296 | const struct st7789v_config *config = dev->config; 297 | uint8_t tmp; 298 | 299 | st7789v_set_lcd_margins(dev, data->x_offset, data->y_offset); 300 | 301 | st7789v_transmit(dev, ST7789V_CMD_CMD2EN, (uint8_t *)config->cmd2en_param, 302 | sizeof(config->cmd2en_param)); 303 | 304 | st7789v_transmit(dev, ST7789V_CMD_PORCTRL, (uint8_t *)config->porch_param, 305 | sizeof(config->porch_param)); 306 | 307 | /* Digital Gamma Enable, default disabled */ 308 | tmp = 0x00; 309 | st7789v_transmit(dev, ST7789V_CMD_DGMEN, &tmp, 1); 310 | 311 | /* Frame Rate Control in Normal Mode, default value */ 312 | tmp = 0x0f; 313 | st7789v_transmit(dev, ST7789V_CMD_FRCTRL2, &tmp, 1); 314 | 315 | tmp = config->gctrl; 316 | st7789v_transmit(dev, ST7789V_CMD_GCTRL, &tmp, 1); 317 | 318 | tmp = config->vcom; 319 | st7789v_transmit(dev, ST7789V_CMD_VCOMS, &tmp, 1); 320 | 321 | if (config->vdv_vrh_enable) { 322 | tmp = 0x01; 323 | st7789v_transmit(dev, ST7789V_CMD_VDVVRHEN, &tmp, 1); 324 | 325 | tmp = config->vrh_value; 326 | st7789v_transmit(dev, ST7789V_CMD_VRH, &tmp, 1); 327 | 328 | tmp = config->vdv_value; 329 | st7789v_transmit(dev, ST7789V_CMD_VDS, &tmp, 1); 330 | } 331 | 332 | st7789v_transmit(dev, ST7789V_CMD_PWCTRL1, (uint8_t *)config->pwctrl1_param, 333 | sizeof(config->pwctrl1_param)); 334 | 335 | /* Memory Data Access Control */ 336 | tmp = config->mdac; 337 | st7789v_transmit(dev, ST7789V_CMD_MADCTL, &tmp, 1); 338 | 339 | /* Interface Pixel Format */ 340 | tmp = config->colmod; 341 | st7789v_transmit(dev, ST7789V_CMD_COLMOD, &tmp, 1); 342 | 343 | tmp = config->lcm; 344 | st7789v_transmit(dev, ST7789V_CMD_LCMCTRL, &tmp, 1); 345 | 346 | tmp = config->gamma; 347 | st7789v_transmit(dev, ST7789V_CMD_GAMSET, &tmp, 1); 348 | 349 | st7789v_transmit(dev, ST7789V_CMD_INV_ON, NULL, 0); 350 | 351 | st7789v_transmit(dev, ST7789V_CMD_PVGAMCTRL, (uint8_t *)config->pvgam_param, 352 | sizeof(config->pvgam_param)); 353 | 354 | st7789v_transmit(dev, ST7789V_CMD_NVGAMCTRL, (uint8_t *)config->nvgam_param, 355 | sizeof(config->nvgam_param)); 356 | 357 | st7789v_transmit(dev, ST7789V_CMD_RAMCTRL, (uint8_t *)config->ram_param, 358 | sizeof(config->ram_param)); 359 | 360 | st7789v_transmit(dev, ST7789V_CMD_RGBCTRL, (uint8_t *)config->rgb_param, 361 | sizeof(config->rgb_param)); 362 | } 363 | 364 | static int st7789v_init(const struct device *dev) 365 | { 366 | const struct st7789v_config *config = dev->config; 367 | 368 | if (!spi_is_ready_dt(&config->bus)) { 369 | LOG_ERR("SPI device not ready"); 370 | return -ENODEV; 371 | } 372 | 373 | if (config->reset_gpio.port != NULL) { 374 | if (!gpio_is_ready_dt(&config->reset_gpio)) { 375 | LOG_ERR("Reset GPIO device not ready"); 376 | return -ENODEV; 377 | } 378 | 379 | if (gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE)) { 380 | LOG_ERR("Couldn't configure reset pin"); 381 | return -EIO; 382 | } 383 | } 384 | 385 | if (config->cmd_data_gpio.port != NULL) { 386 | if (!gpio_is_ready_dt(&config->cmd_data_gpio)) { 387 | LOG_ERR("CMD/DATA GPIO device not ready"); 388 | return -ENODEV; 389 | } 390 | 391 | if (gpio_pin_configure_dt(&config->cmd_data_gpio, GPIO_OUTPUT)) { 392 | LOG_ERR("Couldn't configure CMD/DATA pin"); 393 | return -EIO; 394 | } 395 | } 396 | 397 | st7789v_reset_display(dev); 398 | 399 | st7789v_blanking_on(dev); 400 | 401 | st7789v_lcd_init(dev); 402 | 403 | st7789v_exit_sleep(dev); 404 | 405 | return 0; 406 | } 407 | 408 | #ifdef CONFIG_PM_DEVICE 409 | static int st7789v_pm_action(const struct device *dev, enum pm_device_action action) 410 | { 411 | int ret = 0; 412 | 413 | switch (action) { 414 | case PM_DEVICE_ACTION_RESUME: 415 | st7789v_exit_sleep(dev); 416 | break; 417 | case PM_DEVICE_ACTION_SUSPEND: 418 | st7789v_transmit(dev, ST7789V_CMD_SLEEP_IN, NULL, 0); 419 | break; 420 | default: 421 | ret = -ENOTSUP; 422 | break; 423 | } 424 | 425 | return ret; 426 | } 427 | #endif /* CONFIG_PM_DEVICE */ 428 | 429 | static const struct display_driver_api st7789v_api = { 430 | .blanking_on = st7789v_blanking_on, 431 | .blanking_off = st7789v_blanking_off, 432 | .write = st7789v_write, 433 | .get_capabilities = st7789v_get_capabilities, 434 | .set_pixel_format = st7789v_set_pixel_format, 435 | .set_orientation = st7789v_set_orientation, 436 | }; 437 | 438 | #define ST7789V_WORD_SIZE(inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, cmd_data_gpios), (8), (9)) 439 | 440 | #define ST7789V_INIT(inst) \ 441 | static const struct st7789v_config st7789v_config_##inst = { \ 442 | .bus = SPI_DT_SPEC_INST_GET( \ 443 | inst, SPI_OP_MODE_MASTER | SPI_WORD_SET(ST7789V_WORD_SIZE(inst)), 0), \ 444 | .cmd_data_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, cmd_data_gpios, {}), \ 445 | .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {}), \ 446 | .vcom = DT_INST_PROP(inst, vcom), \ 447 | .gctrl = DT_INST_PROP(inst, gctrl), \ 448 | .vdv_vrh_enable = \ 449 | (DT_INST_NODE_HAS_PROP(inst, vrhs) && DT_INST_NODE_HAS_PROP(inst, vdvs)), \ 450 | .vrh_value = DT_INST_PROP_OR(inst, vrhs, 0), \ 451 | .vdv_value = DT_INST_PROP_OR(inst, vdvs, 0), \ 452 | .mdac = DT_INST_PROP(inst, mdac), \ 453 | .gamma = DT_INST_PROP(inst, gamma), \ 454 | .colmod = DT_INST_PROP(inst, colmod), \ 455 | .lcm = DT_INST_PROP(inst, lcm), \ 456 | .porch_param = DT_INST_PROP(inst, porch_param), \ 457 | .cmd2en_param = DT_INST_PROP(inst, cmd2en_param), \ 458 | .pwctrl1_param = DT_INST_PROP(inst, pwctrl1_param), \ 459 | .pvgam_param = DT_INST_PROP(inst, pvgam_param), \ 460 | .nvgam_param = DT_INST_PROP(inst, nvgam_param), \ 461 | .ram_param = DT_INST_PROP(inst, ram_param), \ 462 | .rgb_param = DT_INST_PROP(inst, rgb_param), \ 463 | .width = DT_INST_PROP(inst, width), \ 464 | .height = DT_INST_PROP(inst, height), \ 465 | }; \ 466 | \ 467 | static struct st7789v_data st7789v_data_##inst = { \ 468 | .x_offset = DT_INST_PROP(inst, x_offset), \ 469 | .y_offset = DT_INST_PROP(inst, y_offset), \ 470 | .orientation = DISPLAY_ORIENTATION_NORMAL, \ 471 | }; \ 472 | \ 473 | PM_DEVICE_DT_INST_DEFINE(inst, st7789v_pm_action); \ 474 | \ 475 | DEVICE_DT_INST_DEFINE(inst, &st7789v_init, PM_DEVICE_DT_INST_GET(inst), \ 476 | &st7789v_data_##inst, &st7789v_config_##inst, POST_KERNEL, \ 477 | CONFIG_DISPLAY_INIT_PRIORITY, &st7789v_api); 478 | 479 | DT_INST_FOREACH_STATUS_OKAY(ST7789V_INIT) 480 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/fonts/NerdFonts_Regular_20.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Size: 20 px 3 | * Bpp: 4 4 | * Opts: --bpp 4 --size 20 --no-compress --use-color-info --stride 1 --align 1 --font JetBrainsMonoNLNerdFontMono-Regular.ttf --symbols -^󰕓󰘳󰌽󰘴󰘵󰘶󰘲󰃚󰃛󰃜󰃝󰃞󰃟󰃠󰃡󰳲 --format lvgl -o NerdFonts_Regular_20.c 5 | ******************************************************************************/ 6 | 7 | #include "lvgl.h" 8 | 9 | #ifndef NERDFONTS_REGULAR_20 10 | #define NERDFONTS_REGULAR_20 1 11 | #endif 12 | 13 | #if NERDFONTS_REGULAR_20 14 | 15 | /*----------------- 16 | * BITMAPS 17 | *----------------*/ 18 | 19 | /*Store the image of the glyphs*/ 20 | static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 21 | /* U+002D "-" */ 22 | 0x19, 0x99, 0x99, 0x91, 0x3f, 0xff, 0xff, 0xf3, 23 | 24 | /* U+005E "^" */ 25 | 0x0, 0x0, 0x77, 0x0, 0x0, 0x0, 0x4, 0xff, 26 | 0x50, 0x0, 0x0, 0xc, 0xca, 0xc0, 0x0, 0x0, 27 | 0x4f, 0x43, 0xf4, 0x0, 0x0, 0xbd, 0x0, 0xcb, 28 | 0x0, 0x3, 0xf5, 0x0, 0x5f, 0x30, 0xb, 0xe0, 29 | 0x0, 0xe, 0xb0, 0x2f, 0x70, 0x0, 0x7, 0xf2, 30 | 31 | /* U+E62A "" */ 32 | 0x33, 0x33, 0x32, 0x23, 0x33, 0x33, 0xff, 0xff, 33 | 0xfc, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xcf, 34 | 0xff, 0xff, 0xff, 0xff, 0xfc, 0xcf, 0xff, 0xff, 35 | 0xff, 0xff, 0xfc, 0xcf, 0xff, 0xff, 0xff, 0xff, 36 | 0xfc, 0xcf, 0xff, 0xff, 0x99, 0x99, 0x97, 0x79, 37 | 0x99, 0x99, 0xff, 0xff, 0xfc, 0xcf, 0xff, 0xff, 38 | 0xff, 0xff, 0xfc, 0xcf, 0xff, 0xff, 0xff, 0xff, 39 | 0xfc, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xcf, 40 | 0xff, 0xff, 0xff, 0xff, 0xfc, 0xcf, 0xff, 0xff, 41 | 0xcc, 0xcc, 0xca, 0xac, 0xcc, 0xcc, 42 | 43 | /* U+E8E5 "" */ 44 | 0x44, 0x44, 0x43, 0x34, 0x44, 0x44, 0xff, 0xff, 45 | 0xfb, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xbf, 46 | 0xff, 0xff, 0xff, 0xff, 0xfb, 0xbf, 0xff, 0xff, 47 | 0xff, 0xff, 0xfb, 0xbf, 0xff, 0xff, 0xff, 0xff, 48 | 0xfb, 0xbf, 0xff, 0xff, 0x77, 0x77, 0x74, 0x47, 49 | 0x77, 0x77, 0xff, 0xff, 0xfa, 0xaf, 0xff, 0xff, 50 | 0xff, 0xff, 0xfb, 0xbf, 0xff, 0xff, 0xff, 0xff, 51 | 0xfb, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xbf, 52 | 0xff, 0xff, 0xff, 0xff, 0xfb, 0xbf, 0xff, 0xff, 53 | 0xbb, 0xbb, 0xb7, 0x7b, 0xbb, 0xbb, 54 | 55 | /* U+F179 "" */ 56 | 0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 57 | 0x0, 0x1b, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x9f, 58 | 0x80, 0x0, 0x0, 0x0, 0x0, 0xea, 0x0, 0x0, 59 | 0x2, 0xad, 0xb6, 0x6b, 0xed, 0x70, 0x2e, 0xff, 60 | 0xff, 0xff, 0xff, 0xf5, 0xaf, 0xff, 0xff, 0xff, 61 | 0xff, 0x90, 0xef, 0xff, 0xff, 0xff, 0xff, 0x20, 62 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0xef, 0xff, 63 | 0xff, 0xff, 0xff, 0x50, 0xaf, 0xff, 0xff, 0xff, 64 | 0xff, 0xe3, 0x4f, 0xff, 0xff, 0xff, 0xff, 0xfd, 65 | 0xc, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x3, 0xff, 66 | 0xff, 0xff, 0xff, 0xb0, 0x0, 0x4d, 0xc8, 0x6a, 67 | 0xea, 0x0, 68 | 69 | /* U+F293 "" */ 70 | 0x0, 0x0, 0x1, 0x10, 0x0, 0x0, 0x0, 0x2a, 71 | 0xff, 0xff, 0xb3, 0x0, 0x3, 0xff, 0xfb, 0xdf, 72 | 0xff, 0x50, 0xe, 0xff, 0xfa, 0x1d, 0xff, 0xe1, 73 | 0x5f, 0xff, 0xfa, 0x2, 0xef, 0xf6, 0xaf, 0xe8, 74 | 0xfa, 0x37, 0x2e, 0xfa, 0xdf, 0xd1, 0x69, 0x3c, 75 | 0xc, 0xfd, 0xef, 0xfd, 0x12, 0x11, 0xaf, 0xfe, 76 | 0xff, 0xff, 0xc1, 0x9, 0xff, 0xff, 0xff, 0xff, 77 | 0xc0, 0x6, 0xff, 0xff, 0xef, 0xfc, 0x2, 0x11, 78 | 0x7f, 0xfe, 0xcf, 0xc0, 0x7a, 0x3c, 0x9, 0xfd, 79 | 0x9f, 0xe9, 0xfa, 0x36, 0x2e, 0xfa, 0x4f, 0xff, 80 | 0xfa, 0x2, 0xef, 0xf5, 0xc, 0xff, 0xfa, 0x2e, 81 | 0xff, 0xe0, 0x2, 0xdf, 0xfd, 0xef, 0xff, 0x30, 82 | 0x0, 0x6, 0xbc, 0xdc, 0x82, 0x0, 83 | 84 | /* U+F00DA "󰃚" */ 85 | 0x0, 0x0, 0x1, 0x10, 0x0, 0x0, 0x0, 0x19, 86 | 0xef, 0xfe, 0x91, 0x0, 0x3, 0xef, 0xff, 0xff, 87 | 0xfe, 0x20, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xe0, 88 | 0x8f, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xdf, 0xff, 89 | 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 90 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 91 | 0xcf, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x6f, 0xff, 92 | 0xff, 0xff, 0xff, 0xf5, 0xc, 0xff, 0xff, 0xff, 93 | 0xff, 0xb0, 0x1, 0xbf, 0xff, 0xff, 0xfb, 0x0, 94 | 0x0, 0x4, 0xad, 0xda, 0x40, 0x0, 95 | 96 | /* U+F00DB "󰃛" */ 97 | 0x0, 0x1, 0x10, 0x0, 0x0, 0x0, 0x5b, 0xff, 98 | 0xff, 0xb4, 0x0, 0x0, 0x4f, 0xff, 0xff, 0xff, 99 | 0xb1, 0x0, 0x2, 0xff, 0xff, 0xff, 0xfd, 0x0, 100 | 0x0, 0x6f, 0xff, 0xff, 0xff, 0xa0, 0x0, 0xc, 101 | 0xff, 0xff, 0xff, 0xf3, 0x0, 0x6, 0xff, 0xff, 102 | 0xff, 0xf9, 0x0, 0x2, 0xff, 0xff, 0xff, 0xfd, 103 | 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 104 | 0xff, 0xff, 0xff, 0xfe, 0x0, 0x3, 0xff, 0xff, 105 | 0xff, 0xfc, 0x0, 0x7, 0xff, 0xff, 0xff, 0xf8, 106 | 0x0, 0xe, 0xff, 0xff, 0xff, 0xf1, 0x0, 0x9f, 107 | 0xff, 0xff, 0xff, 0x70, 0x6, 0xff, 0xff, 0xff, 108 | 0xf9, 0x0, 0x8f, 0xff, 0xff, 0xfe, 0x60, 0x0, 109 | 0x16, 0xbc, 0xca, 0x61, 0x0, 0x0, 110 | 111 | /* U+F00DC "󰃜" */ 112 | 0x15, 0x65, 0x30, 0x0, 0x0, 0x0, 0x29, 0xff, 113 | 0xff, 0x92, 0x0, 0x0, 0x0, 0x3e, 0xff, 0xff, 114 | 0x60, 0x0, 0x0, 0x2, 0xef, 0xff, 0xf8, 0x0, 115 | 0x0, 0x0, 0x3f, 0xff, 0xff, 0x50, 0x0, 0x0, 116 | 0x9, 0xff, 0xff, 0xe0, 0x0, 0x0, 0x2, 0xff, 117 | 0xff, 0xf5, 0x0, 0x0, 0x0, 0xdf, 0xff, 0xfa, 118 | 0x0, 0x0, 0x0, 0xaf, 0xff, 0xfd, 0x0, 0x0, 119 | 0x0, 0x9f, 0xff, 0xff, 0x0, 0x0, 0x0, 0x9f, 120 | 0xff, 0xfe, 0x0, 0x0, 0x0, 0xbf, 0xff, 0xfd, 121 | 0x0, 0x0, 0x0, 0xff, 0xff, 0xf8, 0x0, 0x0, 122 | 0x5, 0xff, 0xff, 0xf3, 0x0, 0x0, 0xd, 0xff, 123 | 0xff, 0xb0, 0x0, 0x0, 0x8f, 0xff, 0xfe, 0x10, 124 | 0x0, 0x6, 0xff, 0xff, 0xe3, 0x0, 0x1, 0x9f, 125 | 0xff, 0xfb, 0x10, 0x0, 0x4d, 0xff, 0xd9, 0x30, 126 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 127 | 128 | /* U+F00DD "󰃝" */ 129 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 130 | 0x0, 0x8d, 0x10, 0x0, 0x0, 0x0, 0x77, 0xaf, 131 | 0xfd, 0x77, 0x30, 0x0, 0x1f, 0xff, 0xba, 0xef, 132 | 0xf6, 0x0, 0x1, 0xff, 0xfa, 0x0, 0xaf, 0x60, 133 | 0x0, 0x8f, 0xff, 0xf6, 0x0, 0xed, 0x10, 0x8f, 134 | 0xff, 0xff, 0xa0, 0xb, 0xfc, 0x3, 0xef, 0xff, 135 | 0xf9, 0x0, 0xcf, 0x70, 0x3, 0xff, 0xff, 0x20, 136 | 0x3f, 0x90, 0x0, 0x1f, 0xff, 0x61, 0x5e, 0xf6, 137 | 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0x60, 0x0, 138 | 0x0, 0x3, 0xef, 0x70, 0x0, 0x0, 0x0, 0x0, 139 | 0x3, 0x60, 0x0, 0x0, 0x0, 140 | 141 | /* U+F00DE "󰃞" */ 142 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 143 | 0x0, 0x8d, 0x10, 0x0, 0x0, 0x0, 0x77, 0xaf, 144 | 0xfd, 0x77, 0x30, 0x0, 0x1f, 0xff, 0xba, 0xef, 145 | 0xf6, 0x0, 0x1, 0xfd, 0x20, 0x0, 0xaf, 0x60, 146 | 0x0, 0x8f, 0x40, 0x0, 0x0, 0xed, 0x10, 0x8f, 147 | 0xf0, 0x0, 0x0, 0xb, 0xfc, 0x3, 0xef, 0x10, 148 | 0x0, 0x0, 0xcf, 0x70, 0x3, 0xf8, 0x0, 0x0, 149 | 0x3f, 0x90, 0x0, 0x1f, 0xf8, 0x21, 0x5e, 0xf6, 150 | 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0x60, 0x0, 151 | 0x0, 0x3, 0xef, 0x70, 0x0, 0x0, 0x0, 0x0, 152 | 0x3, 0x60, 0x0, 0x0, 0x0, 153 | 154 | /* U+F00DF "󰃟" */ 155 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 156 | 0xb, 0xb0, 0x0, 0x0, 0x1, 0x77, 0xbf, 0xfb, 157 | 0x77, 0x10, 0x3, 0xff, 0xff, 0xaf, 0xff, 0x40, 158 | 0x3, 0xff, 0xff, 0x1, 0xcf, 0x40, 0xa, 0xff, 159 | 0xff, 0x0, 0x1f, 0xb0, 0xaf, 0xff, 0xff, 0x0, 160 | 0xd, 0xfa, 0x4f, 0xff, 0xff, 0x0, 0xe, 0xf5, 161 | 0x6, 0xff, 0xff, 0x0, 0x5f, 0x60, 0x3, 0xff, 162 | 0xff, 0x17, 0xff, 0x40, 0x3, 0xff, 0xff, 0xff, 163 | 0xff, 0x40, 0x0, 0x0, 0x5f, 0xf5, 0x0, 0x0, 164 | 0x0, 0x0, 0x4, 0x50, 0x0, 0x0, 165 | 166 | /* U+F00E0 "󰃠" */ 167 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 168 | 0x0, 0x8c, 0x10, 0x0, 0x0, 0x0, 0x77, 0xaf, 169 | 0xfd, 0x77, 0x30, 0x0, 0x1f, 0xff, 0xba, 0xef, 170 | 0xf6, 0x0, 0x1, 0xfd, 0x22, 0x40, 0xaf, 0x60, 171 | 0x0, 0x8f, 0x46, 0xff, 0xb0, 0xed, 0x10, 0x8f, 172 | 0xf0, 0xef, 0xff, 0x3b, 0xfc, 0x3, 0xef, 0x1c, 173 | 0xff, 0xf1, 0xcf, 0x70, 0x3, 0xf8, 0x2b, 0xd5, 174 | 0x3f, 0x90, 0x0, 0x1f, 0xf8, 0x21, 0x5e, 0xf6, 175 | 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0x60, 0x0, 176 | 0x0, 0x3, 0xef, 0x70, 0x0, 0x0, 0x0, 0x0, 177 | 0x3, 0x60, 0x0, 0x0, 0x0, 178 | 179 | /* U+F00E1 "󰃡" */ 180 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 181 | 0x0, 0x8c, 0x10, 0x0, 0x0, 0x0, 0x77, 0xaf, 182 | 0xfd, 0x77, 0x30, 0x0, 0x1f, 0xff, 0xff, 0xff, 183 | 0xf6, 0x0, 0x1, 0xff, 0xf8, 0x4f, 0xff, 0x60, 184 | 0x0, 0x8f, 0xff, 0x22, 0xdf, 0xfc, 0x10, 0x8f, 185 | 0xff, 0xc4, 0x97, 0xff, 0xfc, 0x3, 0xff, 0xf6, 186 | 0x23, 0x1f, 0xff, 0x70, 0x3, 0xff, 0x2e, 0xf4, 187 | 0xcf, 0x90, 0x0, 0x1f, 0xff, 0xff, 0xff, 0xf6, 188 | 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0x60, 0x0, 189 | 0x0, 0x3, 0xff, 0x70, 0x0, 0x0, 0x0, 0x0, 190 | 0x3, 0x70, 0x0, 0x0, 0x0, 191 | 192 | /* U+F033D "󰌽" */ 193 | 0x0, 0x0, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 194 | 0x0, 0x9, 0xff, 0xc0, 0x0, 0x0, 0x0, 0x0, 195 | 0x5f, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x0, 0x9f, 196 | 0xdc, 0xfc, 0x0, 0x0, 0x0, 0x0, 0xda, 0x0, 197 | 0x7f, 0x10, 0x0, 0x0, 0x7, 0xff, 0xdb, 0xff, 198 | 0x90, 0x0, 0x0, 0x5f, 0xff, 0xff, 0xff, 0xf6, 199 | 0x0, 0x1, 0xef, 0xff, 0xff, 0xff, 0xff, 0x20, 200 | 0x9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0xa, 201 | 0xaf, 0xff, 0xff, 0xff, 0xfb, 0xb0, 0x0, 0xd, 202 | 0xff, 0xff, 0xff, 0xf1, 0x0, 0x0, 0x3, 0xef, 203 | 0xff, 0xff, 0x40, 0x0, 0x0, 0x0, 0x18, 0xbb, 204 | 0x81, 0x0, 0x0, 205 | 206 | /* U+F0553 "󰕓" */ 207 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 208 | 0x6, 0x90, 0x0, 0x0, 0x0, 0x0, 0x2f, 0xf5, 209 | 0x0, 0x0, 0x0, 0x0, 0xdf, 0xff, 0x20, 0x0, 210 | 0x0, 0x1, 0x3c, 0xf3, 0x20, 0x0, 0x3, 0x20, 211 | 0xb, 0xe0, 0x1, 0x11, 0x9f, 0xf5, 0xb, 0xe0, 212 | 0x5f, 0xff, 0xff, 0xfa, 0xb, 0xe0, 0x5f, 0xff, 213 | 0xaf, 0xf6, 0xb, 0xe0, 0x5f, 0xff, 0xf, 0xc0, 214 | 0xb, 0xe0, 0x1a, 0xf6, 0xf, 0xb0, 0xb, 0xf0, 215 | 0x9, 0xf2, 0xd, 0xff, 0xff, 0xff, 0xff, 0xf0, 216 | 0x2, 0xaa, 0xae, 0xfa, 0xaa, 0x40, 0x0, 0x0, 217 | 0xb, 0xe0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xe0, 218 | 0x0, 0x0, 0x0, 0x0, 0x2e, 0xf4, 0x0, 0x0, 219 | 0x0, 0x0, 0xaf, 0xfd, 0x0, 0x0, 0x0, 0x0, 220 | 0x9f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x19, 0xa2, 221 | 0x0, 0x0, 222 | 223 | /* U+F0632 "󰘲" */ 224 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 225 | 0xa, 0xa0, 0x0, 0x0, 0x0, 0x0, 0xae, 0xea, 226 | 0x0, 0x0, 0x0, 0xa, 0xe1, 0x2e, 0xa0, 0x0, 227 | 0x0, 0xae, 0x10, 0x2, 0xea, 0x0, 0xa, 0xf7, 228 | 0x10, 0x1, 0x8f, 0xa0, 0x7d, 0xdf, 0x30, 0x3, 229 | 0xfd, 0xd7, 0x0, 0xf, 0x30, 0x3, 0xf0, 0x0, 230 | 0x0, 0xf, 0x30, 0x3, 0xf0, 0x0, 0x0, 0xf, 231 | 0xff, 0xff, 0xf0, 0x0, 0x0, 0x3, 0x33, 0x33, 232 | 0x30, 0x0, 0x0, 0x6, 0x66, 0x66, 0x60, 0x0, 233 | 0x0, 0xf, 0xdd, 0xdd, 0xf0, 0x0, 0x0, 0xf, 234 | 0x53, 0x35, 0xf0, 0x0, 0x0, 0xf, 0xff, 0xff, 235 | 0xf0, 0x0, 236 | 237 | /* U+F0633 "󰘳" */ 238 | 0x0, 0x20, 0x0, 0x0, 0x2, 0x0, 0x4f, 0xfd, 239 | 0x20, 0x2, 0xef, 0xe4, 0xd7, 0xa, 0xa0, 0xa, 240 | 0xa0, 0x7d, 0xe5, 0x6, 0xc0, 0xc, 0x60, 0x5e, 241 | 0x6f, 0xde, 0xfd, 0xdf, 0xed, 0xf6, 0x2, 0x59, 242 | 0xd5, 0x5d, 0x95, 0x20, 0x0, 0x6, 0xc0, 0xc, 243 | 0x60, 0x0, 0x0, 0x6, 0xc0, 0xc, 0x60, 0x0, 244 | 0x2c, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xca, 0x38, 245 | 0xd3, 0x3d, 0x83, 0xac, 0xf3, 0x7, 0xc0, 0xc, 246 | 0x60, 0x4f, 0xbc, 0x6e, 0x70, 0x7, 0xd6, 0xcb, 247 | 0x1a, 0xc8, 0x0, 0x0, 0x8c, 0x91, 248 | 249 | /* U+F0634 "󰘴" */ 250 | 0x0, 0x0, 0x4, 0x40, 0x0, 0x0, 0x0, 0x0, 251 | 0x4, 0xff, 0x40, 0x0, 0x0, 0x0, 0x4, 0xfd, 252 | 0xdf, 0x40, 0x0, 0x0, 0x4, 0xfd, 0x11, 0xdf, 253 | 0x40, 0x0, 0x4, 0xfd, 0x10, 0x1, 0xdf, 0x40, 254 | 0x3, 0xfd, 0x10, 0x0, 0x1, 0xdf, 0x40, 0xad, 255 | 0x10, 0x0, 0x0, 0x1, 0xdb, 0x0, 0x10, 0x0, 256 | 0x0, 0x0, 0x1, 0x0, 257 | 258 | /* U+F0635 "󰘵" */ 259 | 0x66, 0x66, 0x10, 0x4, 0x66, 0x66, 0xff, 0xff, 260 | 0x80, 0x9, 0xff, 0xff, 0x0, 0x7, 0xf1, 0x0, 261 | 0x0, 0x0, 0x0, 0x0, 0xe8, 0x0, 0x0, 0x0, 262 | 0x0, 0x0, 0x7f, 0x10, 0x0, 0x0, 0x0, 0x0, 263 | 0xe, 0x90, 0x0, 0x0, 0x0, 0x0, 0x7, 0xf2, 264 | 0x0, 0x0, 0x0, 0x0, 0x0, 0xe9, 0x0, 0x0, 265 | 0x0, 0x0, 0x0, 0x6f, 0x20, 0x0, 0x0, 0x0, 266 | 0x0, 0xe, 0xa3, 0x33, 0x0, 0x0, 0x0, 0x6, 267 | 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 268 | 269 | /* U+F0636 "󰘶" */ 270 | 0x0, 0x0, 0x7, 0x80, 0x0, 0x0, 0x0, 0x0, 271 | 0x7f, 0xf8, 0x0, 0x0, 0x0, 0x7, 0xf4, 0x4f, 272 | 0x80, 0x0, 0x0, 0x7f, 0x30, 0x4, 0xf8, 0x0, 273 | 0x7, 0xf6, 0x0, 0x0, 0x7f, 0x80, 0x7f, 0xff, 274 | 0x30, 0x3, 0xff, 0xf8, 0x0, 0xf, 0x30, 0x3, 275 | 0xf0, 0x0, 0x0, 0xf, 0x30, 0x3, 0xf0, 0x0, 276 | 0x0, 0xf, 0x86, 0x68, 0xf0, 0x0, 0x0, 0xd, 277 | 0xdd, 0xdd, 0xd0, 0x0, 278 | 279 | /* U+F0CF2 "󰳲" */ 280 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 281 | 0x0, 0x8c, 0x10, 0x0, 0x0, 0x0, 0x77, 0xaf, 282 | 0xfc, 0x77, 0x30, 0x0, 0x1f, 0xff, 0xff, 0xff, 283 | 0xf6, 0x0, 0x1, 0xfc, 0x3d, 0xfa, 0xaf, 0x60, 284 | 0x0, 0x8f, 0xa0, 0xba, 0xb, 0xfc, 0x10, 0x8f, 285 | 0xff, 0xfa, 0xb, 0xff, 0xfc, 0x3, 0xff, 0xfa, 286 | 0xb, 0xbd, 0xff, 0x70, 0x4, 0xfc, 0xb, 0xf0, 287 | 0x4f, 0x90, 0x0, 0x1f, 0xfd, 0xff, 0xbd, 0xf6, 288 | 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0x60, 0x0, 289 | 0x0, 0x3, 0xff, 0x70, 0x0, 0x0, 0x0, 0x0, 290 | 0x3, 0x60, 0x0, 0x0, 0x0 291 | }; 292 | 293 | 294 | /*--------------------- 295 | * GLYPH DESCRIPTION 296 | *--------------------*/ 297 | 298 | static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { 299 | {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, 300 | {.bitmap_index = 0, .adv_w = 192, .box_w = 8, .box_h = 2, .ofs_x = 2, .ofs_y = 6}, 301 | {.bitmap_index = 8, .adv_w = 192, .box_w = 10, .box_h = 8, .ofs_x = 1, .ofs_y = 7}, 302 | {.bitmap_index = 48, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 303 | {.bitmap_index = 126, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 304 | {.bitmap_index = 204, .adv_w = 192, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 305 | {.bitmap_index = 294, .adv_w = 192, .box_w = 12, .box_h = 17, .ofs_x = 0, .ofs_y = -1}, 306 | {.bitmap_index = 396, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 307 | {.bitmap_index = 474, .adv_w = 192, .box_w = 12, .box_h = 17, .ofs_x = 0, .ofs_y = -1}, 308 | {.bitmap_index = 576, .adv_w = 192, .box_w = 12, .box_h = 20, .ofs_x = 0, .ofs_y = -3}, 309 | {.bitmap_index = 696, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 310 | {.bitmap_index = 781, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 311 | {.bitmap_index = 866, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 312 | {.bitmap_index = 944, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 313 | {.bitmap_index = 1029, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 314 | {.bitmap_index = 1114, .adv_w = 192, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = 1}, 315 | {.bitmap_index = 1205, .adv_w = 192, .box_w = 12, .box_h = 19, .ofs_x = 0, .ofs_y = -2}, 316 | {.bitmap_index = 1319, .adv_w = 192, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, 317 | {.bitmap_index = 1409, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, 318 | {.bitmap_index = 1487, .adv_w = 192, .box_w = 13, .box_h = 8, .ofs_x = 0, .ofs_y = 3}, 319 | {.bitmap_index = 1539, .adv_w = 192, .box_w = 12, .box_h = 12, .ofs_x = 0, .ofs_y = 1}, 320 | {.bitmap_index = 1611, .adv_w = 192, .box_w = 12, .box_h = 10, .ofs_x = 0, .ofs_y = 2}, 321 | {.bitmap_index = 1671, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = 1} 322 | }; 323 | 324 | /*--------------------- 325 | * CHARACTER MAPPING 326 | *--------------------*/ 327 | 328 | static const uint16_t unicode_list_0[] = { 329 | 0x0, 0x31, 0xe5fd, 0xe8b8, 0xf14c, 0xf266 330 | }; 331 | 332 | static const uint16_t unicode_list_1[] = { 333 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 334 | 0x263, 0x479, 0x558, 0x559, 0x55a, 0x55b, 0x55c, 0xc18 335 | }; 336 | 337 | /*Collect the unicode lists and glyph_id offsets*/ 338 | static const lv_font_fmt_txt_cmap_t cmaps[] = 339 | { 340 | { 341 | .range_start = 45, .range_length = 62055, .glyph_id_start = 1, 342 | .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 6, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY 343 | }, 344 | { 345 | .range_start = 983258, .range_length = 3097, .glyph_id_start = 7, 346 | .unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 16, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY 347 | } 348 | }; 349 | 350 | 351 | 352 | /*-------------------- 353 | * ALL CUSTOM DATA 354 | *--------------------*/ 355 | 356 | #if LVGL_VERSION_MAJOR == 8 357 | /*Store all the custom data of the font*/ 358 | static lv_font_fmt_txt_glyph_cache_t cache; 359 | #endif 360 | 361 | #if LVGL_VERSION_MAJOR >= 8 362 | static const lv_font_fmt_txt_dsc_t font_dsc = { 363 | #else 364 | static lv_font_fmt_txt_dsc_t font_dsc = { 365 | #endif 366 | .glyph_bitmap = glyph_bitmap, 367 | .glyph_dsc = glyph_dsc, 368 | .cmaps = cmaps, 369 | .kern_dsc = NULL, 370 | .kern_scale = 0, 371 | .cmap_num = 2, 372 | .bpp = 4, 373 | .kern_classes = 0, 374 | .bitmap_format = 0, 375 | #if LVGL_VERSION_MAJOR == 8 376 | .cache = &cache 377 | #endif 378 | 379 | }; 380 | 381 | extern const lv_font_t lv_font_montserrat_20; 382 | 383 | 384 | /*----------------- 385 | * PUBLIC FONT 386 | *----------------*/ 387 | 388 | /*Initialize a public general font descriptor*/ 389 | #if LVGL_VERSION_MAJOR >= 8 390 | const lv_font_t NerdFonts_Regular_20 = { 391 | #else 392 | lv_font_t NerdFonts_Regular_20 = { 393 | #endif 394 | .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ 395 | .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ 396 | .line_height = 20, /*The maximum line height required by the font*/ 397 | .base_line = 3, /*Baseline measured from the bottom of the line*/ 398 | #if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0) 399 | .subpx = LV_FONT_SUBPX_NONE, 400 | #endif 401 | #if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8 402 | .underline_position = -3, 403 | .underline_thickness = 1, 404 | #endif 405 | .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ 406 | #if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9 407 | .fallback = &lv_font_montserrat_20, 408 | #endif 409 | .user_data = NULL, 410 | }; 411 | 412 | 413 | 414 | #endif /*#if NERDFONTS_REGULAR_20*/ 415 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZMK Dongle Screen YADS (Yet another Dongle Screen) 2 | 3 | This project provides a Zephyr module for a dongle display shield based on the ST7789V display and the Seeeduino XAIO BLE microcontroller and the LVGL graphics library. 4 | The display can take advantage of a ambient light sensor to dim and brighten the display automatically. 5 | It offers various widgets for current output, displaying layer, mod, WPM, and battery status, as well as brightness adjustments via keyboard, automatic dimming after inactivity, and a customizable status screen for ZMK-based keyboards. 6 | 7 | **This project is inspired by [prospector-zmk-module](https://github.com/carrefinho/prospector-zmk-module) and [zmk-dongle-display](https://github.com/englmaxi/zmk-dongle-display). Thanks for your awesome work!** 8 | 9 | ## Demo 10 | 11 | ![Sample Screen of zmk-dongle-screen](/docs/images/screen.jpg) 12 | 13 | 14 | 15 | ### Brightness changes with ambient light sensor and screen toggle 16 | 17 | https://github.com/user-attachments/assets/3379f79c-af90-4763-8ba5-8a8f34fd66cf 18 | 19 | ## Building a dongle 20 | 21 | To build a dongle yourself you can use the build guide by **carrefinho** ([prospector project](https://github.com/carrefinho/prospector)) based on the Seeed Studio XIAO nRF52840. 22 | 23 | nice!nano v2 supported. [Wiring guide](/docs/nice_nano_wire_guide.md). 24 | 25 | This repository only contains a module and no build guides or suggestions. 26 | 27 | ## Widgets Overview 28 | 29 | This module provides several widgets to visualize the current state of your ZMK-based keyboard: 30 | 31 | - **Output Widget** 32 | Indicates the current output state of the keyboard (USB or BLE profiles). The currently used interface (USB or BLE) is indicated with an arrow. 33 | - **USB:** 34 | - **White:** USB HID is ready and active (dongle is connected to a computer and working as a keyboard). 35 | - **Red:** USB HID is not ready (dongle is powered, e.g. via wall plug or power bank, but not connected to a computer). 36 | - **BLE:** 37 | For the currently selected Bluetooth profile (the number is shown in the next line): 38 | - **Green:** Connected (active BLE connection established) 39 | - **Blue:** Bonded (device is paired, but not currently connected) 40 | - **White:** Profile is free (no device paired or connected for this profile) 41 | 42 | - **Layer Widget** 43 | Displays the currently active keyboard layer. Useful for quickly identifying which layer is active. 44 | 45 | - **Mod Widget** 46 | Shows the status of modifier keys (e.g., Shift, Ctrl, Alt, GUI). Indicates which modifiers are currently pressed. 47 | 48 | - **WPM Widget** 49 | Displays the current words per minute (WPM) typing speed in real time. 50 | 51 | - **Battery Widget** 52 | Shows the battery level of the dongle and/or the keyboard, if supported. 53 | 54 | ## General Features 55 | 56 | - **Custom Status Screen** 57 | Combine and arrange widgets as you like for a fully customizable status display. (Code changes and recompiling are needed for this.) 58 | 59 | - **Deactivate Screen Modules via configuration** 60 | If you don't need a specific module to be shown (like WPM) you can simple disable them via configuration. No code changes are needed for this. 61 | 62 | - **Ambient light sensor adjustment** 63 | This module supports ambient light sensors. Tested is the `Adafruit APDS9960` sensor. 64 | Using the sensor allows to adjust the lightning to the ambient light level. This can be modified by the `Brightness Control` keys to apply a positive or negative modifier. 65 | If you want to use this feature you'll have to enable it via configuration. Please refer to the configuration overview below. 66 | 67 | - **Toggle the display via Keyboard** 68 | Toggle the display off and on via keyboard shortcut. By default F22 is mapped to this. You'll just have to assign this in your keyboard keymap. 69 | When the display is turned off via toggle and the idle timeout is reached the display will turn on once a new activity is recognized. 70 | 71 | - **Brightness Control** 72 | Adjust the display brightness via keyboard shortcuts. By default, F23 and F24 are mapped to this. You'll just have to assign this in your keyboard keymap. 73 | 74 | - **Configurable Display Orientation** 75 | Set the screen orientation to match your keyboard or desk setup (horizontal or vertical). Additionally, the screen can be flipped to match the orientation of the display in your casing. 76 | 77 | - **Idle Timeout** 78 | Automatically turns off or dims the display after a configurable period of inactivity (no keystrokes). It automatically turns on when the first keystroke is detected again. 79 | The idle timeout can be set in seconds. If set to `0`, the display will never dim or turn off automatically. 80 | When the idle timeout is reached, the display brightness will be set to 0. 81 | When activity resumes, the brightness will be restored to the last value (up to `DONGLE_SCREEN_MAX_BRIGHTNESS`). 82 | 83 | ## Installation 84 | 85 | **ZMK version compatability** 86 | YADS needs at least ZMK version `0.3.0` or `main` (if newer than `0.3.0`) to be build. 87 | 88 | 1. This guide assumes that you have already implemented a basic dongle setup as described [here](https://zmk.dev/docs/development/hardware-integration/dongle). 89 | 2. Once this is done, add this repository to your `west.yaml`. 90 | Example: 91 | 92 | ```yaml 93 | manifest: 94 | remotes: 95 | - name: zmkfirmware 96 | url-base: https://github.com/zmkfirmware 97 | - name: janpfischer 98 | url-base: https://github.com/janpfischer 99 | projects: 100 | - name: zmk 101 | remote: zmkfirmware 102 | revision: 0.3.0 # or main if newer than 0.3.0 103 | import: app/west.yml 104 | - name: zmk-dongle-screen 105 | remote: janpfischer 106 | revision: main 107 | self: 108 | path: config 109 | ``` 110 | 111 | Note: If you want to pin the release of `zmk-dongle-screen` or `zmk` in general you can update the `revision` to use a tag or commit SHA. 112 | 113 | Example for using `zmk-dongle-screen` version 0.0.1: 114 | 115 | ```yaml 116 | - name: zmk-dongle-screen 117 | remote: janpfischer 118 | revision: 0.0.1 119 | ``` 120 | 121 | 3. The shield must be included in your build configuration for the dongle you set up in step 1. 122 | Example `build.yaml` snippet: 123 | 124 | ```yaml 125 | include: 126 | - board: seeeduino_xiao_ble 127 | shield: [YOUR_CONFIGURED_DONGLE] dongle_screen 128 | #cmake-args: -DCONFIG_LOG_PROCESS_THREAD_STARTUP_DELAY_MS=8000 #optional if logging is enabled 129 | #snippet: zmk-usb-logging #only enable for debugging 130 | artifact-name: dongle-screen 131 | ``` 132 | 133 | 4. Keyboard splits must be configured as peripherals. 134 | Example `build.yaml` snippet: 135 | 136 | ```yaml 137 | include: 138 | - board: seeeduino_xiao_ble 139 | shield: split_left 140 | cmake-args: -DCONFIG_ZMK_SPLIT=y -DCONFIG_ZMK_SPLIT_ROLE_CENTRAL=n 141 | artifact-name: split-dongle-left 142 | - board: seeeduino_xiao_ble 143 | shield: split_right 144 | cmake-args: -DCONFIG_ZMK_SPLIT=y -DCONFIG_ZMK_SPLIT_ROLE_CENTRAL=n 145 | artifact-name: split-dongle-right 146 | ``` 147 | 148 | 5. Adjust the desired configuration options in your `[YOUR_CONFIGURED_DONGLE].conf` (see table below). 149 | 150 | ### Configuration sample 151 | 152 | A sample `build.yaml` based on `seeeduino_xiao_ble` boards for the keyboard and the dongle including a `settings_reset` firmware could look like this: 153 | 154 | ```yaml 155 | include: 156 | - board: seeeduino_xiao_ble 157 | shield: totem_left 158 | cmake-args: -DCONFIG_ZMK_SPLIT=y -DCONFIG_ZMK_SPLIT_ROLE_CENTRAL=n 159 | artifact-name: totem-dongle-left 160 | - board: seeeduino_xiao_ble 161 | shield: totem_right 162 | cmake-args: -DCONFIG_ZMK_SPLIT=y -DCONFIG_ZMK_SPLIT_ROLE_CENTRAL=n 163 | artifact-name: totem-dongle-right 164 | - board: seeeduino_xiao_ble 165 | shield: totem_dongle dongle_screen 166 | cmake-args: -DCONFIG_LOG_PROCESS_THREAD_STARTUP_DELAY_MS=8000 167 | snippet: zmk-usb-logging 168 | artifact-name: totem-dongle-screen 169 | - board: seeeduino_xiao_ble 170 | shield: settings_reset 171 | artifact-name: totem-settings-reset 172 | ``` 173 | 174 | ## Configuration Options 175 | 176 | | Name | Type | Default | Description | 177 | | -------------------------------------------------------------- | ---- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 178 | | `CONFIG_DONGLE_SCREEN_HORIZONTAL` | bool | y | Orientation of the screen. By default, it is horizontal (laying on the side). | 179 | | `CONFIG_DONGLE_SCREEN_FLIPPED` | bool | n | Should the screen orientation be flipped in horizontal or vertical orientation? | 180 | | `CONFIG_DONGLE_SCREEN_SYSTEM_ICON` | int | 0 | The icon to display when the 'LGUI'/'RGUI' is pressed. (0: macOS, 1: Linux, 2: Windows) | 181 | | `CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT` | bool | n | If enabled, the ambient light sensor will be used to automatically adjust screen brightness. | 182 | | `CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_EVALUATION_INTERVAL_MS` | int | 1000 | The interval how often the ambient light level should be evaluated. | 183 | | `CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MIN_RAW_VALUE` | int | 0 | Depending on the position and if the sensor is behind transparent plastic or not the sensor readings can be vary. Behind plastic the default value is proven good. If your ambient light changes are not too reactive you might change this. | 184 | | `CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MAX_RAW_VALUE` | int | 100 | Depending on the position and if the sensor is behind transparent plastic or not the sensor readings can be vary. Behind plastic the default value is proven good. If your ambient light changes are not too reactive you might change this. | 185 | | `CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S` | int | 600 | Screen idle timeout in seconds (0 = never off). Time in seconds after which the screen turns off when idle. | 186 | | `CONFIG_DONGLE_SCREEN_MAX_BRIGHTNESS` | int | 80 | Maximum screen brightness (1-100). This is the brightness used when the dongle is powered on and the maximum used by the dimmer. | 187 | | `CONFIG_DONGLE_SCREEN_MIN_BRIGHTNESS` | int | 1 | Minimum screen brightness (1-99). This is the brightness used as a minimum value for brightness adjustments with the modifier keys and the ambient light sensor. | 188 | | `CONFIG_DONGLE_SCREEN_DEFAULT_BRIGHTNESS` | int | `DONGLE_SCREEN_MAX_BRIGHTNESS` | The initial brightness level for the screen backlight. This value is used at startup and when the screen is turned on. It is defaulted to the MAX brightness but can be overridden. Must be between MIN and MAX brightness values. | 189 | | `CONFIG_DONGLE_SCREEN_BRIGHTNESS_MODIFIER` | int | 0 | The modifier to start the dongle with. Useful if you found a modifier comfortable for you. Espacially for ambient light. Otherwise no need to change. | 190 | | `CONFIG_DONGLE_SCREEN_TOGGLE_KEYCODE` | int | 113 | Keycode that toggles the screen off and on (default: F22). | 191 | | `CONFIG_DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL` | bool | y | Allows controlling the screen brightness via keyboard (e.g., F23/F24). | 192 | | `CONFIG_DONGLE_SCREEN_BRIGHTNESS_UP_KEYCODE` | int | 115 | Keycode for increasing screen brightness (default: F24). | 193 | | `CONFIG_DONGLE_SCREEN_BRIGHTNESS_DOWN_KEYCODE` | int | 114 | Keycode for decreasing screen brightness (default: F23). | 194 | | `CONFIG_DONGLE_SCREEN_BRIGHTNESS_STEP` | int | 10 | Step for brightness adjustment with keyboard. How much brightness (range MIN_BRIGHTNESS to MAX_BRIGHTNESS) should be applied per keystroke. | 195 | | `CONFIG_DONGLE_SCREEN_WPM_ACTIVE` | bool | y | If the WPM Widget should be active or not. | 196 | | `CONFIG_DONGLE_SCREEN_MODIFIER_ACTIVE` | bool | y | If the Modifier Widget should be active or not. | 197 | | `CONFIG_DONGLE_SCREEN_LAYER_ACTIVE` | bool | y | If the Layer Widget should be active or not. | 198 | | `CONFIG_DONGLE_SCREEN_OUTPUT_ACTIVE` | bool | y | If the Output Widget should be active or not. | 199 | | `CONFIG_DONGLE_SCREEN_BATTERY_ACTIVE` | bool | y | If the Battery Widget should be active or not. | 200 | | `CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_TEST` | bool | n | If enabled, the ambient light sensor will be mocked to adjust screen brightness. | 201 | 202 | ## Example Configuration (`prj.conf`) 203 | 204 | ```conf 205 | CONFIG_DONGLE_SCREEN_HORIZONTAL=y 206 | CONFIG_DONGLE_SCREEN_FLIPPED=n 207 | CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT=y 208 | CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S=300 209 | CONFIG_DONGLE_SCREEN_MAX_BRIGHTNESS=90 210 | CONFIG_DONGLE_SCREEN_MIN_BRIGHTNESS=10 211 | CONFIG_DONGLE_SCREEN_DEFAULT_BRIGHTNESS=30 212 | CONFIG_DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL=y 213 | CONFIG_DONGLE_SCREEN_BRIGHTNESS_UP_KEYCODE=115 214 | CONFIG_DONGLE_SCREEN_BRIGHTNESS_DOWN_KEYCODE=114 215 | CONFIG_DONGLE_SCREEN_BRIGHTNESS_STEP=5 216 | ``` 217 | 218 | ## Pairing 219 | 220 | The battery widget assigns the battery indicators from left to right, based on the sequence in which the keyboard halves are paired to the dongle. 221 | 222 | For split keyboards, it is essential to pair the left half first after flashing the dongle, followed by the right half. This ensures the correct mapping of battery status indicators and avoids swapped displays in the widget. 223 | 224 | The recommended procedure is as follows: 225 | 226 | 1. Switch off both keyboard halves. 227 | 2. Flash the dongle 228 | 3. Disconnect the dongle 229 | 4. Flash the left half 230 | 5. Flash the right half 231 | 6. Reconnect the dongle 232 | 7. Switch on the left half and wait until the battery indicator appears on the dongle 233 | 8. Switch on the right half 234 | 235 | ### Reset Dongle 236 | 237 | If the dongle has already been paired with both keyboard halves and the battery widget displays swapped indicators (i.e., the left battery indicator refers to the right keyboard half), a full reset of the dongle is required. 238 | 239 | To achieve this, an appropriate configuration for the specific microcontroller must be added to the `build.yaml` in order to generate a `settings_reset-[microcontroller-name]-zmk.uf2` image. This image enables the complete removal of all stored pairing data from the dongle. 240 | 241 | ```yaml 242 | include: 243 | ... 244 | - board: seeeduino_xiao_ble 245 | shield: settings_reset 246 | 247 | - board: nice_nano_v2 248 | shield: settings_reset 249 | ... 250 | ``` 251 | 252 | After flashing the reset file, the pairing process should be repeated in the sequence described above to ensure correct mapping of the battery indicators. 253 | 254 | ## Development 255 | 256 | If you want to develop new features or change the layout of the screen you'll have to clone this repo and build it on your own. 257 | Refer to the [ZMK Local toolchain](https://zmk.dev/docs/development/local-toolchain/build-flash) documentation for this. 258 | 259 | A command for building locally _can_ look something like this: 260 | 261 | ``` 262 | west build -p -s /workspaces/zmk/app -d "/workspaces/zmk-build-output/totem_dongle" -b "seeeduino_xiao_ble" -S zmk-usb-logging -- -DZMK_CONFIG=/workspaces/zmk-config/config -DSHIELD="totem_dongle dongle_screen" -DZMK_EXTRA_MODULES=/workspaces/zmk-modules/zmk-dongle-screen/ 263 | ``` 264 | 265 | _Note: a matching entry for `-DSHIELD` must already be present in your `build.yaml` in your configuration, which is given as the `-DZMK_CONFIG` argument._ 266 | 267 | ## License 268 | 269 | MIT License 270 | 271 | --- 272 | 273 | _This project is part of the ZMK community and licensed under the MIT License._ 274 | -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/brightness.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int random0to100() 13 | { 14 | return rand() % 101; // 0 to 100 15 | } 16 | 17 | LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 18 | 19 | #if CONFIG_DONGLE_SCREEN_MIN_BRIGHTNESS > CONFIG_DONGLE_SCREEN_MAX_BRIGHTNESS 20 | #error "DONGLE_SCREEN_MIN_BRIGHTNESS must be less than or equal to DONGLE_SCREEN_MAX_BRIGHTNESS!" 21 | #endif 22 | 23 | #if CONFIG_DONGLE_SCREEN_DEFAULT_BRIGHTNESS + CONFIG_DONGLE_SCREEN_BRIGHTNESS_MODIFIER < CONFIG_DONGLE_SCREEN_MIN_BRIGHTNESS 24 | #error "DONGLE_SCREEN_DEFAULT_BRIGHTNESS + MODIFIER can't be smaller than MIN brightness value!" 25 | #endif 26 | 27 | #if CONFIG_DONGLE_SCREEN_DEFAULT_BRIGHTNESS + CONFIG_DONGLE_SCREEN_BRIGHTNESS_MODIFIER > CONFIG_DONGLE_SCREEN_MAX_BRIGHTNESS 28 | #error "DONGLE_SCREEN_DEFAULT_BRIGHTNESS + MODIFIER can't be greater than MAX brightness value!" 29 | #endif 30 | 31 | #if CONFIG_DONGLE_SCREEN_BRIGHTNESS_MODIFIER + CONFIG_DONGLE_SCREEN_MIN_BRIGHTNESS > CONFIG_DONGLE_SCREEN_MAX_BRIGHTNESS 32 | #error "DONGLE_SCREEN_BRIGHTNESS_MODIFIER + DONGLE_SCREEN_MIN_BRIGHTNESS can't be greater than DONGLE_SCREEN_MAX_BRIGHTNESS!" 33 | #endif 34 | 35 | #if CONFIG_DONGLE_SCREEN_BRIGHTNESS_MODIFIER + CONFIG_DONGLE_SCREEN_MAX_BRIGHTNESS < CONFIG_DONGLE_SCREEN_MIN_BRIGHTNESS 36 | #error "DONGLE_SCREEN_BRIGHTNESS_MODIFIER + DONGLE_SCREEN_MAX_BRIGHTNESS can't be smaller than DONGLE_SCREEN_MIN_BRIGHTNESS!" 37 | #endif 38 | 39 | #if CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT && (CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MIN_RAW_VALUE > CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MAX_RAW_VALUE) 40 | #error "DONGLE_SCREEN_AMBIENT_LIGHT_MIN_RAW_VALUE can't be greater than DONGLE_SCREEN_AMBIENT_LIGHT_MAX_RAW_VALUE when DONGLE_SCREEN_AMBIENT_LIGHT is activated!" 41 | #endif 42 | 43 | #define BRIGHTNESS_STEP 1 44 | #define BRIGHTNESS_DELAY_MS 2 45 | #define BRIGHTNESS_FADE_DURATION_MS 500 46 | #define SCREEN_IDLE_TIMEOUT_MS (CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S * 1000) 47 | #define BRIGHTNESS_CHANGE_THRESHOLD 5 48 | 49 | static const struct device *pwm_leds_dev = DEVICE_DT_GET_ONE(pwm_leds); 50 | #define DISP_BL DT_NODE_CHILD_IDX(DT_NODELABEL(disp_bl)) 51 | 52 | static int64_t last_activity = 0; 53 | static uint8_t max_brightness = CONFIG_DONGLE_SCREEN_MAX_BRIGHTNESS; 54 | static uint8_t min_brightness = CONFIG_DONGLE_SCREEN_MIN_BRIGHTNESS; 55 | static int8_t current_brightness = CONFIG_DONGLE_SCREEN_DEFAULT_BRIGHTNESS; 56 | 57 | static int8_t brightness_modifier = CONFIG_DONGLE_SCREEN_BRIGHTNESS_MODIFIER; 58 | 59 | static bool off_through_modifier = false; // Used to track if the screen was turned off through the brightness modifier 60 | 61 | /** 62 | * @brief Structure to hold brightness calculation results 63 | */ 64 | struct brightness_result 65 | { 66 | uint8_t adjusted_brightness; // The adjusted base brightness value 67 | int8_t adjusted_modifier; // The adjusted modifier value 68 | uint8_t effective_brightness; // Final brightness (adjusted_brightness + adjusted_modifier) 69 | bool was_clamped; // Whether any clamping occurred 70 | bool hit_min_limit; // Whether minimum limit was reached 71 | bool hit_max_limit; // Whether maximum limit was reached 72 | }; 73 | 74 | static uint8_t clamp_brightness(int8_t value) 75 | { 76 | if (value > max_brightness) 77 | { 78 | LOG_WRN("CLAMPED: Screen brightness %d would be over %d", value, max_brightness); 79 | return max_brightness; 80 | } 81 | if (value < min_brightness) 82 | { 83 | LOG_WRN("CLAMPED: Screen brightness %d would be under %d", value, min_brightness); 84 | return min_brightness; 85 | } 86 | return value; 87 | } 88 | 89 | static void apply_brightness(uint8_t value) 90 | { 91 | led_set_brightness(pwm_leds_dev, DISP_BL, value); 92 | LOG_INF("Screen brightness set to %d", value); 93 | } 94 | 95 | static int8_t calculate_safe_modifier_change(uint8_t base_brightness, int8_t current_modifier, int8_t desired_change) 96 | { 97 | int16_t current_effective = base_brightness + current_modifier; 98 | int16_t desired_effective = current_effective + desired_change; 99 | 100 | // Determine the appropriate boundary based on direction of change 101 | int16_t boundary = (desired_change > 0) ? max_brightness : min_brightness; 102 | 103 | // Check if the desired change is within bounds 104 | if ((desired_change > 0 && desired_effective <= boundary) || 105 | (desired_change < 0 && desired_effective >= boundary)) 106 | { 107 | return desired_change; // Full change is safe 108 | } 109 | 110 | // Calculate maximum safe change toward the boundary 111 | int16_t safe_change = boundary - current_effective; 112 | 113 | // Ensure we don't return a change in the wrong direction or zero when some change is possible 114 | if ((desired_change > 0 && safe_change > 0) || (desired_change < 0 && safe_change < 0)) 115 | { 116 | return (int8_t)safe_change; 117 | } 118 | 119 | return 0; // No safe change possible 120 | } 121 | 122 | static struct brightness_result calculate_brightness_with_bounds(uint8_t base_brightness, int8_t modifier, bool enforce_ambient_constraints) 123 | { 124 | struct brightness_result result = {0}; 125 | 126 | // Start with clamped base brightness 127 | result.adjusted_brightness = clamp_brightness(base_brightness); 128 | result.adjusted_modifier = modifier; 129 | 130 | // Handle ambient light constraints by adjusting base brightness 131 | if (enforce_ambient_constraints) 132 | { 133 | #if CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT 134 | int16_t effective = result.adjusted_brightness + result.adjusted_modifier; 135 | if (effective <= min_brightness) 136 | { 137 | // Need to increase base brightness to meet minimum 138 | uint8_t needed_increase = min_brightness - effective + 1; // +1 to get above minimum 139 | uint8_t old_brightness = result.adjusted_brightness; 140 | 141 | result.adjusted_brightness = clamp_brightness(result.adjusted_brightness + needed_increase); 142 | result.was_clamped = true; 143 | result.hit_min_limit = true; 144 | 145 | LOG_DBG("Ambient: brightness (%d) + modifier (%d) = %d below min (%d), adjusted brightness by +%d to %d, resulting in %d", 146 | old_brightness, modifier, effective, min_brightness, 147 | result.adjusted_brightness - old_brightness, result.adjusted_brightness, 148 | result.adjusted_brightness + modifier); 149 | } 150 | else if (effective > max_brightness) 151 | { 152 | // Need to decrease base brightness to stay within maximum 153 | uint8_t needed_decrease = effective - max_brightness; 154 | uint8_t old_brightness = result.adjusted_brightness; 155 | 156 | if (result.adjusted_brightness >= needed_decrease) 157 | { 158 | result.adjusted_brightness -= needed_decrease; 159 | } 160 | else 161 | { 162 | result.adjusted_brightness = min_brightness; 163 | } 164 | 165 | result.was_clamped = true; 166 | result.hit_max_limit = true; 167 | 168 | LOG_DBG("Ambient: brightness (%d) + modifier (%d) = %d above max (%d), adjusted brightness by -%d to %d, resulting in %d", 169 | old_brightness, modifier, effective, max_brightness, 170 | old_brightness - result.adjusted_brightness, result.adjusted_brightness, 171 | result.adjusted_brightness + modifier); 172 | } 173 | #endif 174 | } 175 | 176 | // Recalculate effective brightness after any ambient adjustments 177 | result.effective_brightness = clamp_brightness(result.adjusted_brightness + result.adjusted_modifier); 178 | 179 | // Final boundary check 180 | if (result.effective_brightness <= min_brightness) 181 | { 182 | result.hit_min_limit = true; 183 | } 184 | if (result.effective_brightness >= max_brightness) 185 | { 186 | result.hit_max_limit = true; 187 | } 188 | 189 | return result; 190 | } 191 | 192 | static bool should_screen_turn_off(uint8_t base_brightness, int8_t modifier) 193 | { 194 | return (base_brightness + modifier) < min_brightness; 195 | } 196 | 197 | static bool should_screen_turn_on(uint8_t base_brightness, int8_t modifier) 198 | { 199 | return (base_brightness + modifier) > min_brightness; 200 | } 201 | 202 | // Threaded fade logic 203 | // Contains starting and target brightness levels to be animated 204 | struct fade_request_t 205 | { 206 | uint8_t from; // Starting brightness level 207 | uint8_t to; // Target brightness level 208 | }; 209 | 210 | #define FADE_QUEUE_SIZE 4 211 | 212 | // Message queue used to send fade requests to the fade handler thread. 213 | // It holds up to 4 fade_request_t elements and ensures brightness updates are handled sequentially. 214 | K_MSGQ_DEFINE(fade_msgq, sizeof(struct fade_request_t), FADE_QUEUE_SIZE, 4); 215 | 216 | // Cubic ease-in-out function to smooth the interpolation curve. 217 | // Provides a natural "S-curve" animation effect: starts slow, accelerates, then slows again. 218 | // Helps avoid abrupt changes in perceived brightness. 219 | static float ease_in_out(float t) 220 | { 221 | if (t < 0.5f) 222 | return 4.0f * t * t * t; 223 | float f = -2.0f * t + 2.0f; 224 | return 1.0f - (f * f * f) / 2.0f; 225 | } 226 | 227 | // Dedicated thread responsible for handling all fade animations. 228 | // Receives fade requests from the queue and applies brightness changes over time using easing. 229 | void fade_thread(void) 230 | { 231 | struct fade_request_t req; 232 | 233 | while (1) 234 | { 235 | // Wait indefinitely for the next fade request to arrive in the queue 236 | if (k_msgq_get(&fade_msgq, &req, K_FOREVER) == 0) 237 | { 238 | 239 | // Skip animation entirely if brightness difference is too small 240 | if (req.from == req.to || abs(req.to - req.from) <= 1) 241 | { 242 | apply_brightness(req.to); 243 | continue; 244 | } 245 | 246 | // Calculate brightness difference and use it to determine number of steps 247 | int diff = abs(req.to - req.from); 248 | int steps = CLAMP(diff * 2, 6, 32); // More steps for smoother fades over large differences 249 | 250 | // Set total animation time: scale with difference but clamp between 500ms and 1000ms 251 | int total_duration_ms = CLAMP(diff * 20, 500, 1000); // 20ms per level as baseline 252 | int delay_us = (total_duration_ms * 1000) / steps; // Delay between steps in microseconds 253 | 254 | uint8_t last_applied = 255; // Used to prevent redundant LED updates to save performance 255 | 256 | // Interpolate brightness across 'steps' frames using easing 257 | for (int i = 0; i <= steps; i++) 258 | { 259 | float t = (float)i / steps; // Normalized time in [0, 1] 260 | float eased = ease_in_out(t); // Eased time for smoother progression 261 | float interpolated = req.from + (req.to - req.from) * eased; // Interpolated value 262 | uint8_t brightness = (uint8_t)(interpolated + 0.5f); // Rounded to nearest integer 263 | 264 | // Only send update if brightness actually changed 265 | if (brightness != last_applied) 266 | { 267 | apply_brightness(brightness); 268 | last_applied = brightness; 269 | } 270 | 271 | k_usleep(delay_us); // Sleep before next step to pace the fade 272 | } 273 | 274 | // safeguard to ensure the target value is set at the end 275 | if (last_applied != req.to) 276 | { 277 | apply_brightness(req.to); 278 | } 279 | } 280 | } 281 | } 282 | 283 | // Launch the fade thread with 768 bytes of stack, medium priority (6) 284 | // 512 was too small for logging, math (float, int), small loop, few stack-local variables 285 | // 768 is just a guess, optimization is possible, probably 286 | K_THREAD_DEFINE(fade_tid, 768, fade_thread, NULL, NULL, NULL, 6, 0, 0); 287 | 288 | // Function to submit a brightness fade request 289 | // Ensures that only the most recent fade request is applied by purging the queue first for changes in between animations 290 | static void fade_to_brightness(uint8_t from, uint8_t to) 291 | { 292 | struct fade_request_t req = {.from = from, .to = to}; 293 | k_msgq_purge(&fade_msgq); // Clear any pending fades to avoid outdated transitions 294 | k_msgq_put(&fade_msgq, &req, K_NO_WAIT); // Submit the new fade request without blocking 295 | } 296 | 297 | void set_screen_brightness(uint8_t value, bool ambient) 298 | { 299 | struct brightness_result result = calculate_brightness_with_bounds(value, brightness_modifier, ambient); 300 | 301 | uint8_t current_effective = clamp_brightness(current_brightness + brightness_modifier); 302 | 303 | fade_to_brightness(current_effective, result.effective_brightness); 304 | current_brightness = result.adjusted_brightness; 305 | } 306 | 307 | #if CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S > 0 || CONFIG_DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL 308 | // --- Brightness logic --- 309 | static bool screen_on = true; 310 | // --- Screen on/off --- 311 | 312 | static void screen_set_on(bool on) 313 | { 314 | if (on && !screen_on) 315 | { 316 | // Use unified helper to check if we need brightness adjustment 317 | if (should_screen_turn_off(current_brightness, brightness_modifier)) 318 | { 319 | struct brightness_result result = calculate_brightness_with_bounds(current_brightness, brightness_modifier, false); 320 | current_brightness = result.adjusted_brightness; 321 | LOG_DBG("SCREEN TURN ON: Adjusted brightness to ensure screen can turn on: %d", current_brightness); 322 | } 323 | 324 | fade_to_brightness(0, clamp_brightness(current_brightness + brightness_modifier)); 325 | screen_on = true; 326 | off_through_modifier = false; // Reset the flag, because the screen is turned on again 327 | LOG_INF("Screen on (smooth)"); 328 | } 329 | else if (!on && screen_on) 330 | { 331 | fade_to_brightness(clamp_brightness(current_brightness + brightness_modifier), 0); 332 | screen_on = false; 333 | LOG_INF("Screen off (smooth)"); 334 | } 335 | else 336 | { 337 | LOG_DBG("Screen state is already %s, no action taken.", on ? "on" : "off"); 338 | } 339 | } 340 | 341 | #endif 342 | 343 | // --- Idle thread --- 344 | 345 | #if CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S > 0 346 | 347 | void screen_idle_thread(void) 348 | { 349 | while (1) 350 | { 351 | // Thread should run even if the screen is off, but only if the screen is off through the modifier 352 | if (screen_on || (!screen_on && off_through_modifier)) 353 | { 354 | int64_t now = k_uptime_get(); 355 | int64_t elapsed = now - last_activity; 356 | int64_t remaining = SCREEN_IDLE_TIMEOUT_MS - elapsed; 357 | 358 | if (remaining <= 0) 359 | { 360 | screen_set_on(false); 361 | off_through_modifier = false; // Reset the flag, because the screen is turned off 362 | // After turning off, sleep until next activity (key event will wake screen) 363 | k_sleep(K_FOREVER); 364 | } 365 | else 366 | { 367 | // Sleep exactly as long as needed until timeout or next key event 368 | k_sleep(K_MSEC(remaining)); 369 | } 370 | } 371 | else 372 | { 373 | // If Screen is off, sleep forever (will be interrupted by key event) 374 | k_sleep(K_FOREVER); 375 | } 376 | } 377 | } 378 | 379 | K_THREAD_DEFINE(screen_idle_tid, 512, screen_idle_thread, NULL, NULL, NULL, 7, 0, 0); 380 | 381 | void brightness_wake_screen_on_reconnect(void) 382 | { 383 | if (!screen_on) 384 | { 385 | LOG_INF("Peripheral reconnected, waking screen"); 386 | 387 | screen_set_on(true); 388 | 389 | // Reset idle timer 390 | last_activity = k_uptime_get(); 391 | 392 | k_wakeup(screen_idle_tid); 393 | } 394 | else 395 | { 396 | LOG_DBG("Peripheral reconnected but screen already on"); 397 | } 398 | } 399 | 400 | #endif 401 | 402 | // --- Brightness control via keyboard --- 403 | 404 | #if CONFIG_DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL 405 | 406 | static void increase_brightness(void) 407 | { 408 | LOG_DBG("Current brightness: %d, current modifier: %d", current_brightness, brightness_modifier); 409 | 410 | int8_t safe_increase = calculate_safe_modifier_change(current_brightness, brightness_modifier, CONFIG_DONGLE_SCREEN_BRIGHTNESS_STEP); 411 | 412 | if (safe_increase > 0) 413 | { 414 | brightness_modifier += safe_increase; 415 | LOG_DBG("Brightness modifier increased by %d to %d", safe_increase, brightness_modifier); 416 | set_screen_brightness(current_brightness, false); 417 | 418 | // Check if we should turn screen on 419 | if (should_screen_turn_on(current_brightness, brightness_modifier) && off_through_modifier) 420 | { 421 | LOG_INF("Brightness sufficient to turn screen on"); 422 | screen_set_on(true); 423 | } 424 | } 425 | else 426 | { 427 | LOG_DBG("Brightness modifier cannot be increased further (at maximum)"); 428 | } 429 | } 430 | 431 | static void decrease_brightness(void) 432 | { 433 | LOG_DBG("Current brightness: %d, current modifier: %d", current_brightness, brightness_modifier); 434 | 435 | int8_t safe_decrease = calculate_safe_modifier_change(current_brightness, brightness_modifier, -CONFIG_DONGLE_SCREEN_BRIGHTNESS_STEP); 436 | 437 | if (safe_decrease < 0) 438 | { // safe_decrease will be negative for decreases 439 | brightness_modifier += safe_decrease; // Adding a negative value decreases 440 | LOG_DBG("Brightness modifier decreased by %d to %d", -safe_decrease, brightness_modifier); 441 | set_screen_brightness(current_brightness, false); 442 | 443 | // Check if we should turn screen off 444 | if (should_screen_turn_off(current_brightness, brightness_modifier)) 445 | { 446 | LOG_INF("Brightness too low, turning screen off"); 447 | off_through_modifier = true; 448 | screen_set_on(false); 449 | } 450 | } 451 | else 452 | { 453 | LOG_DBG("Brightness modifier cannot be decreased further (at minimum)"); 454 | } 455 | } 456 | 457 | #endif // CONFIG_DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL 458 | 459 | #if CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S > 0 || CONFIG_DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL 460 | 461 | // --- Key event listener --- 462 | 463 | static int key_listener(const zmk_event_t *eh) 464 | { 465 | const struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); 466 | if (ev && ev->state) 467 | { // Only on key down 468 | LOG_DBG("Key pressed: keycode=%d", ev->keycode); 469 | 470 | #if CONFIG_DONGLE_SCREEN_BRIGHTNESS_KEYBOARD_CONTROL 471 | if (ev->keycode == CONFIG_DONGLE_SCREEN_BRIGHTNESS_UP_KEYCODE) 472 | { 473 | LOG_INF("Brightness UP key recognized!"); 474 | increase_brightness(); 475 | return 0; 476 | } 477 | else if (ev->keycode == CONFIG_DONGLE_SCREEN_BRIGHTNESS_DOWN_KEYCODE) 478 | { 479 | LOG_INF("Brightness DOWN key recognized!"); 480 | decrease_brightness(); 481 | return 0; 482 | } 483 | else if (ev->keycode == CONFIG_DONGLE_SCREEN_TOGGLE_KEYCODE) 484 | { 485 | LOG_INF("Toggle screen key recognized!"); 486 | // Toggle screen on/off 487 | if (screen_on) 488 | { 489 | off_through_modifier = true; // Track that the screen was turned off through the toggle key 490 | screen_set_on(false); 491 | } 492 | else 493 | { 494 | screen_set_on(true); 495 | } 496 | return 0; 497 | } 498 | 499 | #endif 500 | } 501 | 502 | #if CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S > 0 503 | last_activity = k_uptime_get(); 504 | if (!screen_on && !off_through_modifier) 505 | { 506 | screen_set_on(true); 507 | k_wakeup(screen_idle_tid); 508 | } 509 | #else 510 | // Without idle thread: just turn on screen 511 | if (!screen_on) 512 | { 513 | screen_set_on(true); 514 | } 515 | #endif 516 | return 0; 517 | } 518 | 519 | ZMK_LISTENER(screen_idle, key_listener); 520 | ZMK_SUBSCRIPTION(screen_idle, zmk_keycode_state_changed); 521 | ZMK_SUBSCRIPTION(screen_idle, zmk_layer_state_changed); 522 | 523 | #endif 524 | 525 | #if IS_ENABLED(CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT) 526 | 527 | #define AMBIENT_LIGHT_SENSOR_NODE DT_INST(0, avago_apds9960) 528 | static const struct device *ambient_sensor = DEVICE_DT_GET(AMBIENT_LIGHT_SENSOR_NODE); 529 | 530 | // Passe diese Werte nach deinen Messungen an! 531 | const int32_t min_sensor = CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MIN_RAW_VALUE; 532 | const int32_t max_sensor = CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MAX_RAW_VALUE; 533 | 534 | static uint8_t ambient_to_brightness(int32_t sensor_value) 535 | { 536 | if (sensor_value < min_sensor) 537 | { 538 | LOG_INF("Ambient sensor reading (%d) below DONGLE_SCREEN_AMBIENT_LIGHT_MIN_RAW_VALUE: (%d) Will set the sensor reading to the minimum configured.", sensor_value, CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MIN_RAW_VALUE); 539 | sensor_value = min_sensor; 540 | } 541 | 542 | if (sensor_value > max_sensor) 543 | { 544 | LOG_INF("Ambient sensor reading (%d) above DONGLE_SCREEN_AMBIENT_LIGHT_MAX_RAW_VALUE: (%d) Will set the sensor reading to the maximum configured.", sensor_value, CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_MAX_RAW_VALUE); 545 | sensor_value = max_sensor; 546 | } 547 | 548 | uint8_t brightness = min_brightness + 549 | ((sensor_value - min_sensor) * (max_brightness - min_brightness)) / 550 | (max_sensor - min_sensor); 551 | return clamp_brightness(brightness); 552 | } 553 | 554 | static void ambient_light_thread(void) 555 | { 556 | struct sensor_value val; 557 | uint8_t last_brightness = 0xFF; // Invalid initial value to force first update 558 | 559 | while (1) 560 | { 561 | 562 | #ifndef CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_TEST 563 | 564 | if (!device_is_ready(ambient_sensor)) 565 | { 566 | LOG_ERR("Ambient light sensor not ready!"); 567 | k_sleep(K_SECONDS(5)); 568 | continue; 569 | } 570 | int rc = sensor_sample_fetch(ambient_sensor); 571 | if (rc == 0) 572 | { 573 | rc = sensor_channel_get(ambient_sensor, SENSOR_CHAN_LIGHT, &val); 574 | if (rc == 0) 575 | { 576 | #else 577 | k_sleep(K_SECONDS(10)); 578 | val.val1 = random0to100(); 579 | 580 | #endif 581 | uint8_t new_brightness = ambient_to_brightness(val.val1); 582 | 583 | if (abs(new_brightness - last_brightness) > BRIGHTNESS_CHANGE_THRESHOLD) 584 | { 585 | struct brightness_result result = calculate_brightness_with_bounds(new_brightness, brightness_modifier, true); 586 | 587 | LOG_DBG("Ambient light: %d (raw) -> brightness %d, effective (incl. modifier) %d", 588 | val.val1, result.adjusted_brightness, result.effective_brightness); 589 | 590 | if (result.hit_min_limit) 591 | { 592 | LOG_DBG("Ambient brightness at minimum limit"); 593 | } 594 | if (result.hit_max_limit) 595 | { 596 | LOG_DBG("Ambient brightness at maximum limit"); 597 | } 598 | 599 | if (screen_on) 600 | { 601 | set_screen_brightness(new_brightness, true); 602 | } 603 | else 604 | { 605 | // If the screen is off, just set the brightness variable 606 | // to have the current ambient brightness when the screen is turned on again 607 | current_brightness = result.adjusted_brightness; 608 | } 609 | last_brightness = new_brightness; 610 | } 611 | #ifndef CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_TEST 612 | } 613 | } 614 | #endif // CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_TEST 615 | k_sleep(K_MSEC(CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT_EVALUATION_INTERVAL_MS)); // Adjust interval as needed 616 | } 617 | } 618 | 619 | K_THREAD_DEFINE(ambient_light_tid, 512, ambient_light_thread, NULL, NULL, NULL, 7, 0, 0); 620 | 621 | #endif // CONFIG_DONGLE_SCREEN_AMBIENT_LIGHT 622 | 623 | // --- Initialization --- 624 | 625 | static int init_fixed_brightness(void) 626 | { 627 | set_screen_brightness(current_brightness, false); 628 | last_activity = k_uptime_get(); 629 | #if CONFIG_DONGLE_SCREEN_IDLE_TIMEOUT_S > 0 630 | // Wake up the idle thread at boot 631 | k_wakeup(screen_idle_tid); 632 | #else 633 | LOG_INF("Screen idle timeout disabled"); 634 | #endif 635 | return 0; 636 | } 637 | 638 | SYS_INIT(init_fixed_brightness, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); -------------------------------------------------------------------------------- /boards/shields/dongle_screen/src/fonts/NerdFonts_Regular_40.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Size: 40 px 3 | * Bpp: 4 4 | * Opts: --bpp 4 --size 40 --no-compress --use-color-info --stride 1 --align 1 --font JetBrainsMonoNLNerdFontMono-Regular.ttf --symbols -^󰕓󰘳󰌽󰘴󰘵󰘶󰘲󰃚󰃛󰃜󰃝󰃞󰃟󰃠󰃡󰳲 --format lvgl -o NerdFonts_Regular_40.c 5 | ******************************************************************************/ 6 | 7 | #include "lvgl.h" 8 | 9 | #ifndef NERDFONTS_REGULAR_40 10 | #define NERDFONTS_REGULAR_40 1 11 | #endif 12 | 13 | #if NERDFONTS_REGULAR_40 14 | 15 | /*----------------- 16 | * BITMAPS 17 | *----------------*/ 18 | 19 | /*Store the image of the glyphs*/ 20 | static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { 21 | /* U+002D "-" */ 22 | 0x14, 0x44, 0x44, 0x44, 0x44, 0x44, 0x41, 0x6f, 23 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x6f, 0xff, 24 | 0xff, 0xff, 0xff, 0xff, 0xf6, 0x6f, 0xff, 0xff, 25 | 0xff, 0xff, 0xff, 0xf6, 26 | 27 | /* U+005E "^" */ 28 | 0x0, 0x0, 0x0, 0xd, 0xff, 0xe0, 0x0, 0x0, 29 | 0x0, 0x0, 0x0, 0x0, 0x5f, 0xff, 0xf6, 0x0, 30 | 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0xff, 0xfd, 31 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xff, 0xc9, 32 | 0xff, 0x40, 0x0, 0x0, 0x0, 0x0, 0xa, 0xff, 33 | 0x64, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x1f, 34 | 0xff, 0x0, 0xef, 0xf2, 0x0, 0x0, 0x0, 0x0, 35 | 0x8f, 0xf9, 0x0, 0x7f, 0xf9, 0x0, 0x0, 0x0, 36 | 0x0, 0xff, 0xf2, 0x0, 0x1f, 0xff, 0x10, 0x0, 37 | 0x0, 0x7, 0xff, 0xb0, 0x0, 0x9, 0xff, 0x70, 38 | 0x0, 0x0, 0xe, 0xff, 0x40, 0x0, 0x3, 0xff, 39 | 0xe0, 0x0, 0x0, 0x5f, 0xfd, 0x0, 0x0, 0x0, 40 | 0xcf, 0xf5, 0x0, 0x0, 0xcf, 0xf6, 0x0, 0x0, 41 | 0x0, 0x5f, 0xfc, 0x0, 0x3, 0xff, 0xe0, 0x0, 42 | 0x0, 0x0, 0xe, 0xff, 0x30, 0xa, 0xff, 0x80, 43 | 0x0, 0x0, 0x0, 0x8, 0xff, 0xb0, 0x2f, 0xff, 44 | 0x10, 0x0, 0x0, 0x0, 0x1, 0xff, 0xf2, 0x9f, 45 | 0xfa, 0x0, 0x0, 0x0, 0x0, 0x0, 0xaf, 0xf9, 46 | 47 | /* U+E62A "" */ 48 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x63, 0x36, 0x66, 49 | 0x66, 0x66, 0x66, 0x66, 0xff, 0xff, 0xff, 0xff, 50 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 51 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 52 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 53 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 54 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 55 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 56 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 57 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 58 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 59 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 60 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 61 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 63 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 64 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 65 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 66 | 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x12, 0x22, 67 | 0x22, 0x22, 0x22, 0x22, 0xff, 0xff, 0xff, 0xff, 68 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 69 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 70 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 71 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 72 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 73 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 74 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 75 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 76 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 77 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 79 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 80 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 81 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x9f, 0xff, 82 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 83 | 0xff, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 84 | 0x99, 0x99, 0x99, 0x99, 0x99, 0x95, 0x59, 0x99, 85 | 0x99, 0x99, 0x99, 0x99, 86 | 87 | /* U+E8E5 "" */ 88 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x62, 0x26, 0x66, 89 | 0x66, 0x66, 0x66, 0x66, 0xff, 0xff, 0xff, 0xff, 90 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 91 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 92 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 93 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 94 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 95 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 96 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 97 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 98 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 99 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 100 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 101 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 102 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 103 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 104 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 105 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 106 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 107 | 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 108 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 109 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 110 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 111 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 112 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 113 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 114 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 115 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 116 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 117 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 118 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 119 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 120 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 121 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x5f, 0xff, 122 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 123 | 0xff, 0xf5, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 124 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x62, 0x26, 0x66, 125 | 0x66, 0x66, 0x66, 0x66, 126 | 127 | /* U+F179 "" */ 128 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 129 | 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 130 | 0x0, 0x0, 0x0, 0x3d, 0xfe, 0x0, 0x0, 0x0, 131 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 132 | 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 133 | 0x0, 0x0, 0x2f, 0xff, 0xf5, 0x0, 0x0, 0x0, 134 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f, 0xff, 135 | 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 136 | 0x0, 0x0, 0xef, 0xfd, 0x10, 0x0, 0x0, 0x0, 137 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x90, 138 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 0xab, 139 | 0x95, 0x0, 0x10, 0x38, 0xbd, 0xb8, 0x30, 0x0, 140 | 0x0, 0x8, 0xff, 0xff, 0xff, 0xfa, 0x9e, 0xff, 141 | 0xff, 0xff, 0xfa, 0x0, 0x0, 0xbf, 0xff, 0xff, 142 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 143 | 0x8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 144 | 0xff, 0xff, 0xff, 0x90, 0x2f, 0xff, 0xff, 0xff, 145 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0, 146 | 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 147 | 0xff, 0xff, 0xc0, 0x0, 0xcf, 0xff, 0xff, 0xff, 148 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60, 0x0, 149 | 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 150 | 0xff, 0xff, 0x20, 0x0, 0xff, 0xff, 0xff, 0xff, 151 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x0, 152 | 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 153 | 0xff, 0xff, 0x20, 0x0, 0xdf, 0xff, 0xff, 0xff, 154 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x0, 155 | 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 156 | 0xff, 0xff, 0xd0, 0x0, 0x7f, 0xff, 0xff, 0xff, 157 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x0, 158 | 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 159 | 0xff, 0xff, 0xff, 0xa1, 0xd, 0xff, 0xff, 0xff, 160 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 161 | 0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 162 | 0xff, 0xff, 0xff, 0xf8, 0x0, 0xef, 0xff, 0xff, 163 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 164 | 0x0, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 165 | 0xff, 0xff, 0xff, 0x70, 0x0, 0xb, 0xff, 0xff, 166 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0x0, 167 | 0x0, 0x1, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 168 | 0xff, 0xff, 0xe2, 0x0, 0x0, 0x0, 0x3e, 0xff, 169 | 0xff, 0xfe, 0xcd, 0xff, 0xff, 0xff, 0x30, 0x0, 170 | 0x0, 0x0, 0x1, 0x9d, 0xb7, 0x20, 0x0, 0x27, 171 | 0xbd, 0xa2, 0x0, 0x0, 172 | 173 | /* U+F293 "" */ 174 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x12, 0x21, 0x0, 175 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x17, 176 | 0xbf, 0xff, 0xff, 0xfd, 0x93, 0x0, 0x0, 0x0, 177 | 0x0, 0x0, 0x8, 0xff, 0xff, 0xff, 0xff, 0xff, 178 | 0xff, 0xb1, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xff, 179 | 0xff, 0xf9, 0xff, 0xff, 0xff, 0xfe, 0x20, 0x0, 180 | 0x0, 0xd, 0xff, 0xff, 0xff, 0xf3, 0x6f, 0xff, 181 | 0xff, 0xff, 0xe2, 0x0, 0x0, 0x9f, 0xff, 0xff, 182 | 0xff, 0xf3, 0x7, 0xff, 0xff, 0xff, 0xfb, 0x0, 183 | 0x2, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x0, 0x9f, 184 | 0xff, 0xff, 0xff, 0x40, 0x8, 0xff, 0xff, 0xff, 185 | 0xff, 0xf3, 0x0, 0xa, 0xff, 0xff, 0xff, 0xa0, 186 | 0xe, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x0, 0x0, 187 | 0xbf, 0xff, 0xff, 0xf0, 0x2f, 0xff, 0xff, 0xcf, 188 | 0xff, 0xf3, 0x6, 0x50, 0xc, 0xff, 0xff, 0xf3, 189 | 0x6f, 0xff, 0xfa, 0x6, 0xff, 0xf3, 0x6, 0xf5, 190 | 0x0, 0xcf, 0xff, 0xf7, 0x9f, 0xff, 0xf4, 0x0, 191 | 0x6f, 0xf3, 0x6, 0xff, 0x10, 0x3f, 0xff, 0xf9, 192 | 0xbf, 0xff, 0xff, 0x30, 0x6, 0xf3, 0x6, 0xf4, 193 | 0x1, 0xdf, 0xff, 0xfc, 0xdf, 0xff, 0xff, 0xf3, 194 | 0x0, 0x63, 0x6, 0x40, 0xc, 0xff, 0xff, 0xfd, 195 | 0xef, 0xff, 0xff, 0xff, 0x30, 0x0, 0x0, 0x0, 196 | 0xbf, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 197 | 0xf3, 0x0, 0x0, 0x9, 0xff, 0xff, 0xff, 0xff, 198 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x30, 0x0, 0x8f, 199 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 200 | 0xff, 0x40, 0x0, 0x7f, 0xff, 0xff, 0xff, 0xff, 201 | 0xff, 0xff, 0xff, 0xff, 0xf4, 0x0, 0x0, 0x8, 202 | 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 203 | 0x40, 0x0, 0x0, 0x0, 0x8f, 0xff, 0xff, 0xfe, 204 | 0xdf, 0xff, 0xff, 0xf4, 0x0, 0x53, 0x5, 0x40, 205 | 0x9, 0xff, 0xff, 0xfd, 0xbf, 0xff, 0xff, 0x40, 206 | 0x5, 0xf4, 0x6, 0xf3, 0x0, 0xaf, 0xff, 0xfc, 207 | 0x9f, 0xff, 0xf4, 0x0, 0x5f, 0xf4, 0x6, 0xfe, 208 | 0x0, 0xd, 0xff, 0xf9, 0x6f, 0xff, 0xf9, 0x5, 209 | 0xff, 0xf4, 0x6, 0xf5, 0x0, 0x9f, 0xff, 0xf7, 210 | 0x2f, 0xff, 0xff, 0xcf, 0xff, 0xf4, 0x6, 0x50, 211 | 0x9, 0xff, 0xff, 0xf3, 0xd, 0xff, 0xff, 0xff, 212 | 0xff, 0xf4, 0x0, 0x0, 0x9f, 0xff, 0xff, 0xf0, 213 | 0x7, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x0, 0x9, 214 | 0xff, 0xff, 0xff, 0xa0, 0x1, 0xff, 0xff, 0xff, 215 | 0xff, 0xf5, 0x0, 0x9f, 0xff, 0xff, 0xff, 0x30, 216 | 0x0, 0x6f, 0xff, 0xff, 0xff, 0xf5, 0x9, 0xff, 217 | 0xff, 0xff, 0xfa, 0x0, 0x0, 0x9, 0xff, 0xff, 218 | 0xff, 0xf5, 0x9f, 0xff, 0xff, 0xff, 0xe1, 0x0, 219 | 0x0, 0x0, 0x8f, 0xff, 0xff, 0xfd, 0xff, 0xff, 220 | 0xff, 0xfe, 0x20, 0x0, 0x0, 0x0, 0x3, 0xcf, 221 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x91, 0x0, 0x0, 222 | 0x0, 0x0, 0x0, 0x2, 0x69, 0xbc, 0xcc, 0xb8, 223 | 0x50, 0x0, 0x0, 0x0, 224 | 225 | /* U+F00DA "󰃚" */ 226 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x20, 0x0, 227 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 228 | 0xae, 0xff, 0xff, 0xe9, 0x40, 0x0, 0x0, 0x0, 229 | 0x0, 0x0, 0x4, 0xdf, 0xff, 0xff, 0xff, 0xff, 230 | 0xfc, 0x40, 0x0, 0x0, 0x0, 0x0, 0x8f, 0xff, 231 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0, 0x0, 232 | 0x0, 0xa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 233 | 0xff, 0xff, 0xa0, 0x0, 0x0, 0x8f, 0xff, 0xff, 234 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x0, 235 | 0x3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 236 | 0xff, 0xff, 0xff, 0x20, 0xc, 0xff, 0xff, 0xff, 237 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 238 | 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 239 | 0xff, 0xff, 0xff, 0xf2, 0x8f, 0xff, 0xff, 0xff, 240 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 241 | 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 242 | 0xff, 0xff, 0xff, 0xfb, 0xef, 0xff, 0xff, 0xff, 243 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 244 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 245 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 246 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 247 | 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 248 | 0xff, 0xff, 0xff, 0xfd, 0xaf, 0xff, 0xff, 0xff, 249 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 250 | 0x6f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 251 | 0xff, 0xff, 0xff, 0xf5, 0x1f, 0xff, 0xff, 0xff, 252 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 253 | 0x8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 254 | 0xff, 0xff, 0xff, 0x70, 0x1, 0xef, 0xff, 0xff, 255 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 256 | 0x0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 257 | 0xff, 0xff, 0xf2, 0x0, 0x0, 0x5, 0xff, 0xff, 258 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x0, 259 | 0x0, 0x0, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 260 | 0xff, 0xd2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e, 261 | 0xff, 0xff, 0xff, 0xff, 0xe6, 0x0, 0x0, 0x0, 262 | 0x0, 0x0, 0x0, 0x0, 0x48, 0xac, 0xca, 0x83, 263 | 0x0, 0x0, 0x0, 0x0, 264 | 265 | /* U+F00DB "󰃛" */ 266 | 0x0, 0x0, 0x0, 0x1, 0x10, 0x0, 0x0, 0x0, 267 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0xbe, 0xff, 268 | 0xff, 0xeb, 0x72, 0x0, 0x0, 0x0, 0x0, 0x0, 269 | 0x4c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, 270 | 0x0, 0x0, 0x0, 0x0, 0x4e, 0xff, 0xff, 0xff, 271 | 0xff, 0xff, 0xff, 0xff, 0xb1, 0x0, 0x0, 0x0, 272 | 0x1, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 273 | 0xfe, 0x30, 0x0, 0x0, 0x0, 0xc, 0xff, 0xff, 274 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x0, 0x0, 275 | 0x0, 0x1, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 276 | 0xff, 0xff, 0x30, 0x0, 0x0, 0x0, 0x2f, 0xff, 277 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe1, 0x0, 278 | 0x0, 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 279 | 0xff, 0xff, 0xf9, 0x0, 0x0, 0x0, 0x0, 0xdf, 280 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 281 | 0x0, 0x0, 0x0, 0x6f, 0xff, 0xff, 0xff, 0xff, 282 | 0xff, 0xff, 0xff, 0xa0, 0x0, 0x0, 0x0, 0xf, 283 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 284 | 0x0, 0x0, 0x0, 0xa, 0xff, 0xff, 0xff, 0xff, 285 | 0xff, 0xff, 0xff, 0xf5, 0x0, 0x0, 0x0, 0x6, 286 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 287 | 0x0, 0x0, 0x0, 0x3, 0xff, 0xff, 0xff, 0xff, 288 | 0xff, 0xff, 0xff, 0xfc, 0x0, 0x0, 0x0, 0x2, 289 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 290 | 0x0, 0x0, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 291 | 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x1, 292 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 293 | 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0xff, 0xff, 294 | 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x4, 295 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 296 | 0x0, 0x0, 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 297 | 0xff, 0xff, 0xff, 0xf8, 0x0, 0x0, 0x0, 0xc, 298 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 299 | 0x0, 0x0, 0x0, 0x1f, 0xff, 0xff, 0xff, 0xff, 300 | 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 0x0, 0x8f, 301 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 302 | 0x0, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 303 | 0xff, 0xff, 0xfe, 0x10, 0x0, 0x0, 0xa, 0xff, 304 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x0, 305 | 0x0, 0x0, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 306 | 0xff, 0xff, 0xa0, 0x0, 0x0, 0x3, 0xff, 0xff, 307 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0, 0x0, 308 | 0x0, 0x3e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 309 | 0xff, 0xc1, 0x0, 0x0, 0x5, 0xff, 0xff, 0xff, 310 | 0xff, 0xff, 0xff, 0xff, 0xfa, 0x0, 0x0, 0x0, 311 | 0x8f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 312 | 0x50, 0x0, 0x0, 0x0, 0x6, 0xdf, 0xff, 0xff, 313 | 0xff, 0xff, 0xfd, 0x60, 0x0, 0x0, 0x0, 0x0, 314 | 0x0, 0x2, 0x69, 0xbc, 0xcb, 0x96, 0x20, 0x0, 315 | 0x0, 0x0, 0x0, 0x0, 316 | 317 | /* U+F00DC "󰃜" */ 318 | 0x25, 0x9b, 0xcd, 0xcb, 0x96, 0x20, 0x0, 0x0, 319 | 0x0, 0x0, 0x0, 0x0, 0x28, 0xef, 0xff, 0xff, 320 | 0xff, 0xfd, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 321 | 0x0, 0x8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x91, 322 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0xff, 323 | 0xff, 0xff, 0xff, 0xfe, 0x40, 0x0, 0x0, 0x0, 324 | 0x0, 0x0, 0x0, 0x9f, 0xff, 0xff, 0xff, 0xff, 325 | 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 326 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0x0, 0x0, 327 | 0x0, 0x0, 0x0, 0x0, 0xaf, 0xff, 0xff, 0xff, 328 | 0xff, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 329 | 0xc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x50, 0x0, 330 | 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0xff, 331 | 0xff, 0xff, 0xf1, 0x0, 0x0, 0x0, 0x0, 0x0, 332 | 0x0, 0x8f, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x0, 333 | 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xff, 0xff, 334 | 0xff, 0xff, 0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 335 | 0x0, 0x8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 336 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 337 | 0xff, 0xff, 0xff, 0xf0, 0x0, 0x0, 0x0, 0x0, 338 | 0x0, 0x0, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xf4, 339 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f, 0xff, 340 | 0xff, 0xff, 0xff, 0xf8, 0x0, 0x0, 0x0, 0x0, 341 | 0x0, 0x0, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xfb, 342 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xff, 343 | 0xff, 0xff, 0xff, 0xfd, 0x0, 0x0, 0x0, 0x0, 344 | 0x0, 0x0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 345 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2f, 0xff, 346 | 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 347 | 0x0, 0x0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 348 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4f, 0xff, 349 | 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 350 | 0x0, 0x0, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xfc, 351 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8f, 0xff, 352 | 0xff, 0xff, 0xff, 0xf9, 0x0, 0x0, 0x0, 0x0, 353 | 0x0, 0x0, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xf5, 354 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xff, 0xff, 355 | 0xff, 0xff, 0xff, 0xf1, 0x0, 0x0, 0x0, 0x0, 356 | 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 357 | 0x0, 0x0, 0x0, 0x0, 0x0, 0xd, 0xff, 0xff, 358 | 0xff, 0xff, 0xff, 0x40, 0x0, 0x0, 0x0, 0x0, 359 | 0x0, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x0, 360 | 0x0, 0x0, 0x0, 0x0, 0x1, 0xef, 0xff, 0xff, 361 | 0xff, 0xff, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x0, 362 | 0xa, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x0, 363 | 0x0, 0x0, 0x0, 0x0, 0x7f, 0xff, 0xff, 0xff, 364 | 0xff, 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 365 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0, 0x0, 366 | 0x0, 0x0, 0x0, 0x6f, 0xff, 0xff, 0xff, 0xff, 367 | 0xfb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xff, 368 | 0xff, 0xff, 0xff, 0xff, 0x70, 0x0, 0x0, 0x0, 369 | 0x0, 0x4, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xc3, 370 | 0x0, 0x0, 0x0, 0x0, 0x5, 0xbf, 0xff, 0xff, 371 | 0xff, 0xff, 0xb4, 0x0, 0x0, 0x0, 0x0, 0x0, 372 | 0x49, 0xde, 0xff, 0xfe, 0xc9, 0x61, 0x0, 0x0, 373 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 374 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 375 | 376 | /* U+F00DD "󰃝" */ 377 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 378 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 379 | 0x0, 0x0, 0xbf, 0x30, 0x0, 0x0, 0x0, 0x0, 380 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0xff, 381 | 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 382 | 0x0, 0x0, 0xbf, 0xff, 0xff, 0x40, 0x0, 0x0, 383 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 384 | 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 385 | 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 386 | 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xfe, 387 | 0x74, 0x35, 0xaf, 0xff, 0xff, 0xb0, 0x0, 0x0, 388 | 0x0, 0x4f, 0xff, 0xff, 0xf9, 0x0, 0x0, 0x2d, 389 | 0xff, 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 390 | 0xff, 0xfb, 0x0, 0x0, 0xd, 0xff, 0xb0, 0x0, 391 | 0x0, 0x0, 0xbf, 0xff, 0xff, 0xff, 0xf7, 0x0, 392 | 0x0, 0x3f, 0xff, 0x30, 0x0, 0x0, 0xbf, 0xff, 393 | 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 0xcf, 0xff, 394 | 0x30, 0x0, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 395 | 0x20, 0x0, 0x8, 0xff, 0xff, 0x30, 0x8f, 0xff, 396 | 0xff, 0xff, 0xff, 0xff, 0xf3, 0x0, 0x0, 0x7f, 397 | 0xff, 0xfe, 0x1, 0xcf, 0xff, 0xff, 0xff, 0xff, 398 | 0xff, 0x20, 0x0, 0x8, 0xff, 0xff, 0x50, 0x1, 399 | 0xcf, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 400 | 0xbf, 0xff, 0x50, 0x0, 0x1, 0xdf, 0xff, 0xff, 401 | 0xff, 0xf8, 0x0, 0x0, 0x2f, 0xff, 0x50, 0x0, 402 | 0x0, 0x4, 0xff, 0xff, 0xff, 0xfd, 0x0, 0x0, 403 | 0xc, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 0xff, 404 | 0xff, 0xfc, 0x10, 0x0, 0x1b, 0xff, 0xfb, 0x0, 405 | 0x0, 0x0, 0x4, 0xff, 0xff, 0xfe, 0x52, 0x13, 406 | 0x8e, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 407 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 408 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 409 | 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 410 | 0x1, 0x11, 0x12, 0xdf, 0xff, 0xff, 0x61, 0x11, 411 | 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 412 | 0xcf, 0xff, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 413 | 0x0, 0x0, 0x0, 0x0, 0x1, 0xcf, 0x50, 0x0, 414 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 415 | 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 416 | 0x0, 417 | 418 | /* U+F00DE "󰃞" */ 419 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 420 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 421 | 0x0, 0x0, 0xbf, 0x30, 0x0, 0x0, 0x0, 0x0, 422 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0xff, 423 | 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 424 | 0x0, 0x0, 0xbf, 0xff, 0xff, 0x40, 0x0, 0x0, 425 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 426 | 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 427 | 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 428 | 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xfd, 429 | 0x74, 0x35, 0xaf, 0xff, 0xff, 0xb0, 0x0, 0x0, 430 | 0x0, 0x4f, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x2d, 431 | 0xff, 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xf5, 432 | 0x0, 0x0, 0x0, 0x0, 0xd, 0xff, 0xb0, 0x0, 433 | 0x0, 0x0, 0xbf, 0xfa, 0x0, 0x0, 0x0, 0x0, 434 | 0x0, 0x3f, 0xff, 0x30, 0x0, 0x0, 0xbf, 0xff, 435 | 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcf, 0xff, 436 | 0x30, 0x0, 0xbf, 0xff, 0xf0, 0x0, 0x0, 0x0, 437 | 0x0, 0x0, 0x8, 0xff, 0xff, 0x30, 0x8f, 0xff, 438 | 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 439 | 0xff, 0xfe, 0x1, 0xcf, 0xff, 0xf0, 0x0, 0x0, 440 | 0x0, 0x0, 0x0, 0x8, 0xff, 0xff, 0x50, 0x1, 441 | 0xcf, 0xff, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 442 | 0xbf, 0xff, 0x50, 0x0, 0x1, 0xdf, 0xf9, 0x0, 443 | 0x0, 0x0, 0x0, 0x0, 0x2f, 0xff, 0x50, 0x0, 444 | 0x0, 0x4, 0xff, 0xf3, 0x0, 0x0, 0x0, 0x0, 445 | 0xc, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 0xff, 446 | 0xe4, 0x0, 0x0, 0x0, 0x1b, 0xff, 0xfb, 0x0, 447 | 0x0, 0x0, 0x4, 0xff, 0xff, 0xfb, 0x52, 0x13, 448 | 0x8e, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 449 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 450 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 451 | 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 452 | 0x1, 0x11, 0x12, 0xcf, 0xff, 0xff, 0x61, 0x11, 453 | 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 454 | 0xcf, 0xff, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 455 | 0x0, 0x0, 0x0, 0x0, 0x1, 0xcf, 0x50, 0x0, 456 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 457 | 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 458 | 0x0, 459 | 460 | /* U+F00DF "󰃟" */ 461 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x10, 0x0, 462 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 463 | 0x0, 0x1d, 0xd1, 0x0, 0x0, 0x0, 0x0, 0x0, 464 | 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xfd, 0x10, 465 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 466 | 0x1d, 0xff, 0xff, 0xd2, 0x0, 0x0, 0x0, 0x0, 467 | 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 468 | 0xff, 0xff, 0x70, 0x0, 0x0, 0x7, 0xff, 0xff, 469 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x0, 470 | 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0x46, 0xcf, 471 | 0xff, 0xff, 0x80, 0x0, 0x0, 0x7, 0xff, 0xff, 472 | 0xff, 0xff, 0x0, 0x4, 0xef, 0xff, 0x80, 0x0, 473 | 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 474 | 0x2f, 0xff, 0x80, 0x0, 0x0, 0x1d, 0xff, 0xff, 475 | 0xff, 0xff, 0x0, 0x0, 0x7, 0xff, 0xd1, 0x0, 476 | 0x1, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 477 | 0x1, 0xff, 0xfd, 0x10, 0x1d, 0xff, 0xff, 0xff, 478 | 0xff, 0xff, 0x0, 0x0, 0x0, 0xcf, 0xff, 0xd1, 479 | 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 480 | 0x0, 0xaf, 0xff, 0xfb, 0x2e, 0xff, 0xff, 0xff, 481 | 0xff, 0xff, 0x0, 0x0, 0x0, 0xcf, 0xff, 0xe2, 482 | 0x2, 0xef, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 483 | 0x0, 0xef, 0xfe, 0x20, 0x0, 0x2e, 0xff, 0xff, 484 | 0xff, 0xff, 0x0, 0x0, 0x6, 0xff, 0xf3, 0x0, 485 | 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 486 | 0x1e, 0xff, 0x80, 0x0, 0x0, 0x7, 0xff, 0xff, 487 | 0xff, 0xff, 0x0, 0x2, 0xdf, 0xff, 0x80, 0x0, 488 | 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0x14, 0x9f, 489 | 0xff, 0xff, 0x80, 0x0, 0x0, 0x7, 0xff, 0xff, 490 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x0, 491 | 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 492 | 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 0x11, 0x11, 493 | 0x3e, 0xff, 0xff, 0xe3, 0x11, 0x11, 0x0, 0x0, 494 | 0x0, 0x0, 0x0, 0x0, 0x2, 0xef, 0xfe, 0x30, 495 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 496 | 0x0, 0x2e, 0xe2, 0x0, 0x0, 0x0, 0x0, 0x0, 497 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x20, 0x0, 498 | 0x0, 0x0, 0x0, 0x0, 499 | 500 | /* U+F00E0 "󰃠" */ 501 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 502 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 503 | 0x0, 0x0, 0xbf, 0x30, 0x0, 0x0, 0x0, 0x0, 504 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0xff, 505 | 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 506 | 0x0, 0x0, 0xbf, 0xff, 0xff, 0x40, 0x0, 0x0, 507 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 508 | 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 509 | 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 510 | 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xfd, 511 | 0x74, 0x35, 0xaf, 0xff, 0xff, 0xb0, 0x0, 0x0, 512 | 0x0, 0x4f, 0xff, 0xf7, 0x0, 0x0, 0x0, 0x2d, 513 | 0xff, 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xf5, 514 | 0x0, 0x49, 0xa7, 0x0, 0xd, 0xff, 0xb0, 0x0, 515 | 0x0, 0x0, 0xbf, 0xfa, 0x0, 0x9f, 0xff, 0xfe, 516 | 0x20, 0x3f, 0xff, 0x30, 0x0, 0x0, 0xbf, 0xff, 517 | 0x30, 0x6f, 0xff, 0xff, 0xfd, 0x0, 0xcf, 0xff, 518 | 0x30, 0x0, 0xbf, 0xff, 0xf0, 0xd, 0xff, 0xff, 519 | 0xff, 0xf4, 0x8, 0xff, 0xff, 0x30, 0x8f, 0xff, 520 | 0xfe, 0x0, 0xff, 0xff, 0xff, 0xff, 0x60, 0x7f, 521 | 0xff, 0xfe, 0x0, 0xcf, 0xff, 0xf0, 0xd, 0xff, 522 | 0xff, 0xff, 0xf5, 0x8, 0xff, 0xff, 0x50, 0x1, 523 | 0xcf, 0xff, 0x20, 0x7f, 0xff, 0xff, 0xfe, 0x0, 524 | 0xbf, 0xff, 0x50, 0x0, 0x1, 0xcf, 0xf9, 0x0, 525 | 0xbf, 0xff, 0xff, 0x30, 0x2f, 0xff, 0x50, 0x0, 526 | 0x0, 0x4, 0xff, 0xf3, 0x0, 0x6b, 0xc9, 0x20, 527 | 0xc, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 0xff, 528 | 0xe4, 0x0, 0x0, 0x0, 0x1b, 0xff, 0xfb, 0x0, 529 | 0x0, 0x0, 0x4, 0xff, 0xff, 0xfb, 0x52, 0x13, 530 | 0x8e, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 531 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 532 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 533 | 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 534 | 0x1, 0x11, 0x12, 0xcf, 0xff, 0xff, 0x61, 0x11, 535 | 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 536 | 0xcf, 0xff, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 537 | 0x0, 0x0, 0x0, 0x0, 0x1, 0xcf, 0x50, 0x0, 538 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 539 | 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 540 | 0x0, 541 | 542 | /* U+F00E1 "󰃡" */ 543 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 544 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 545 | 0x0, 0x0, 0xae, 0x30, 0x0, 0x0, 0x0, 0x0, 546 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0xff, 547 | 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 548 | 0x0, 0x0, 0xbf, 0xff, 0xff, 0x30, 0x0, 0x0, 549 | 0x0, 0x0, 0x0, 0x3, 0xee, 0xee, 0xff, 0xff, 550 | 0xff, 0xff, 0xee, 0xee, 0xa0, 0x0, 0x0, 0x0, 551 | 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 552 | 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 553 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 554 | 0x0, 0x4f, 0xff, 0xff, 0xff, 0x54, 0xbf, 0xff, 555 | 0xff, 0xfb, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 556 | 0xff, 0xb0, 0x4, 0xff, 0xff, 0xff, 0xb0, 0x0, 557 | 0x0, 0x0, 0xaf, 0xff, 0xff, 0xf5, 0x1, 0xe, 558 | 0xff, 0xff, 0xff, 0x30, 0x0, 0x0, 0xaf, 0xff, 559 | 0xff, 0xff, 0x2, 0x80, 0x9f, 0xff, 0xff, 0xfe, 560 | 0x30, 0x0, 0xaf, 0xff, 0xff, 0xff, 0xa0, 0x7e, 561 | 0x3, 0xff, 0xff, 0xff, 0xfe, 0x30, 0x8f, 0xff, 562 | 0xff, 0xff, 0xf4, 0xc, 0xf3, 0xd, 0xff, 0xff, 563 | 0xff, 0xfe, 0x1, 0xdf, 0xff, 0xff, 0xfe, 0x0, 564 | 0x44, 0x10, 0x8f, 0xff, 0xff, 0xff, 0x50, 0x1, 565 | 0xdf, 0xff, 0xff, 0x90, 0x13, 0x33, 0x2, 0xff, 566 | 0xff, 0xff, 0x50, 0x0, 0x1, 0xdf, 0xff, 0xf3, 567 | 0xb, 0xff, 0xf2, 0xc, 0xff, 0xff, 0x60, 0x0, 568 | 0x0, 0x4, 0xff, 0xfe, 0x23, 0xff, 0xff, 0x92, 569 | 0x8f, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 0xff, 570 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x0, 571 | 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 0xff, 572 | 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x4f, 573 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 574 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 575 | 0xff, 0xff, 0xff, 0xff, 0xb0, 0x0, 0x0, 0x0, 576 | 0x1, 0x11, 0x12, 0xdf, 0xff, 0xff, 0x61, 0x11, 577 | 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 578 | 0xdf, 0xff, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 579 | 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf, 0x50, 0x0, 580 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 581 | 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 582 | 0x0, 583 | 584 | /* U+F033D "󰌽" */ 585 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x45, 0x20, 586 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 587 | 0x0, 0x0, 0xaf, 0xff, 0xfd, 0x30, 0x0, 0x0, 588 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xff, 589 | 0xff, 0xff, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x0, 590 | 0x0, 0x0, 0x0, 0x7f, 0xff, 0xff, 0xff, 0xfc, 591 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 592 | 0xdf, 0xff, 0xff, 0xff, 0xff, 0x20, 0x0, 0x0, 593 | 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0xff, 594 | 0xff, 0xff, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 595 | 0x0, 0x5, 0xff, 0xfb, 0x99, 0xbe, 0xff, 0xa0, 596 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xfd, 597 | 0x10, 0x0, 0x0, 0x9f, 0xd0, 0x0, 0x0, 0x0, 598 | 0x0, 0x0, 0x0, 0xd, 0xff, 0x80, 0x0, 0x5, 599 | 0xef, 0xf3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 600 | 0x7f, 0xff, 0xfe, 0x62, 0xbf, 0xff, 0xfb, 0x0, 601 | 0x0, 0x0, 0x0, 0x0, 0x6, 0xff, 0xff, 0xff, 602 | 0xff, 0xff, 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 603 | 0x0, 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 604 | 0xff, 0xf6, 0x0, 0x0, 0x0, 0x2, 0xef, 0xff, 605 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x30, 606 | 0x0, 0x0, 0xc, 0xff, 0xff, 0xff, 0xff, 0xff, 607 | 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 0x6f, 608 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 609 | 0xff, 0xf9, 0x0, 0x0, 0xef, 0xff, 0xff, 0xff, 610 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 611 | 0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 612 | 0xff, 0xff, 0xff, 0xff, 0xa0, 0xd, 0xff, 0xff, 613 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 614 | 0xff, 0xf0, 0x6, 0x84, 0x6f, 0xff, 0xff, 0xff, 615 | 0xff, 0xff, 0xff, 0xff, 0xfc, 0x37, 0x60, 0x0, 616 | 0x0, 0xe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 617 | 0xff, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x7, 0xff, 618 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x0, 619 | 0x0, 0x0, 0x0, 0x0, 0xbf, 0xff, 0xff, 0xff, 620 | 0xff, 0xff, 0xfe, 0x20, 0x0, 0x0, 0x0, 0x0, 621 | 0x0, 0xb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd2, 622 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5d, 623 | 0xff, 0xff, 0xff, 0xe8, 0x0, 0x0, 0x0, 0x0, 624 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x88, 0x74, 625 | 0x0, 0x0, 0x0, 0x0, 0x0, 626 | 627 | /* U+F0553 "󰕓" */ 628 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 629 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 630 | 0x0, 0x4, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0, 631 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0xf6, 0x0, 632 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 633 | 0x0, 0xbf, 0xff, 0x30, 0x0, 0x0, 0x0, 0x0, 634 | 0x0, 0x0, 0x0, 0x0, 0x7, 0xff, 0xff, 0xd0, 635 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 636 | 0x3f, 0xff, 0xff, 0xfa, 0x0, 0x0, 0x0, 0x0, 637 | 0x0, 0x0, 0x0, 0x1, 0xef, 0xff, 0xff, 0xff, 638 | 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 639 | 0xaa, 0xdf, 0xff, 0xaa, 0xa0, 0x0, 0x0, 0x0, 640 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0xfd, 0x0, 641 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 642 | 0x0, 0x6f, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 643 | 0x0, 0x25, 0x40, 0x0, 0x0, 0x6f, 0xfd, 0x0, 644 | 0x0, 0x0, 0x0, 0x0, 0x9, 0xff, 0xfe, 0x30, 645 | 0x0, 0x6f, 0xfd, 0x0, 0xb, 0xff, 0xff, 0xff, 646 | 0x7f, 0xff, 0xff, 0xe0, 0x0, 0x6f, 0xfd, 0x0, 647 | 0xb, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xf5, 648 | 0x0, 0x6f, 0xfd, 0x0, 0xb, 0xff, 0xff, 0xff, 649 | 0xff, 0xff, 0xff, 0xf7, 0x0, 0x6f, 0xfd, 0x0, 650 | 0xb, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xf3, 651 | 0x0, 0x6f, 0xfd, 0x0, 0xb, 0xff, 0xff, 0xff, 652 | 0x3f, 0xff, 0xff, 0xa0, 0x0, 0x6f, 0xfd, 0x0, 653 | 0xb, 0xff, 0xff, 0xff, 0x3, 0xff, 0xfb, 0x0, 654 | 0x0, 0x6f, 0xfd, 0x0, 0x8, 0xcf, 0xff, 0xdb, 655 | 0x0, 0xef, 0xf7, 0x0, 0x0, 0x6f, 0xfd, 0x0, 656 | 0x0, 0x1f, 0xff, 0x50, 0x0, 0xef, 0xf7, 0x0, 657 | 0x0, 0x6f, 0xfd, 0x0, 0x0, 0x1f, 0xff, 0x50, 658 | 0x0, 0xef, 0xf8, 0x11, 0x11, 0x7f, 0xfe, 0x11, 659 | 0x11, 0x2f, 0xff, 0x50, 0x0, 0xdf, 0xff, 0xff, 660 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x30, 661 | 0x0, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 662 | 0xff, 0xff, 0xfe, 0x0, 0x0, 0xa, 0xff, 0xff, 663 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0x0, 664 | 0x0, 0x0, 0x25, 0x55, 0x55, 0xaf, 0xfe, 0x55, 665 | 0x55, 0x54, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 666 | 0x0, 0x6f, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 667 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0xfd, 0x0, 668 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 669 | 0x0, 0x6f, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 670 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x6f, 0xfd, 0x0, 671 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 672 | 0x0, 0x9f, 0xfe, 0x20, 0x0, 0x0, 0x0, 0x0, 673 | 0x0, 0x0, 0x0, 0x0, 0xa, 0xff, 0xff, 0xe2, 674 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 675 | 0x3f, 0xff, 0xff, 0xfa, 0x0, 0x0, 0x0, 0x0, 676 | 0x0, 0x0, 0x0, 0x0, 0x7f, 0xff, 0xff, 0xfe, 677 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 678 | 0x6f, 0xff, 0xff, 0xfd, 0x0, 0x0, 0x0, 0x0, 679 | 0x0, 0x0, 0x0, 0x0, 0x1f, 0xff, 0xff, 0xf7, 680 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 681 | 0x4, 0xff, 0xff, 0xa0, 0x0, 0x0, 0x0, 0x0, 682 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x16, 0x74, 0x0, 683 | 0x0, 0x0, 0x0, 0x0, 684 | 685 | /* U+F0632 "󰘲" */ 686 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x50, 0x0, 687 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 688 | 0x0, 0x5f, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x0, 689 | 0x0, 0x0, 0x0, 0x0, 0x5, 0xff, 0xff, 0x50, 690 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 691 | 0x5f, 0xfe, 0xef, 0xf5, 0x0, 0x0, 0x0, 0x0, 692 | 0x0, 0x0, 0x0, 0x5, 0xff, 0xe2, 0x2e, 0xff, 693 | 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 694 | 0xfe, 0x20, 0x2, 0xef, 0xf5, 0x0, 0x0, 0x0, 695 | 0x0, 0x0, 0x5, 0xff, 0xe2, 0x0, 0x0, 0x2e, 696 | 0xff, 0x50, 0x0, 0x0, 0x0, 0x0, 0x5f, 0xfe, 697 | 0x20, 0x0, 0x0, 0x2, 0xef, 0xf5, 0x0, 0x0, 698 | 0x0, 0x5, 0xff, 0xe2, 0x0, 0x0, 0x0, 0x0, 699 | 0x3e, 0xff, 0x50, 0x0, 0x0, 0x5f, 0xff, 0x53, 700 | 0x10, 0x0, 0x0, 0x1, 0x36, 0xff, 0xf5, 0x0, 701 | 0x5, 0xff, 0xff, 0xff, 0x60, 0x0, 0x0, 0x6, 702 | 0xff, 0xff, 0xff, 0x50, 0x5f, 0xff, 0xff, 0xff, 703 | 0x60, 0x0, 0x0, 0x6, 0xff, 0xff, 0xff, 0xf5, 704 | 0x23, 0x33, 0x33, 0xff, 0x60, 0x0, 0x0, 0x6, 705 | 0xff, 0x33, 0x33, 0x32, 0x0, 0x0, 0x0, 0xff, 706 | 0x60, 0x0, 0x0, 0x6, 0xff, 0x0, 0x0, 0x0, 707 | 0x0, 0x0, 0x0, 0xff, 0x60, 0x0, 0x0, 0x6, 708 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 709 | 0x60, 0x0, 0x0, 0x6, 0xff, 0x0, 0x0, 0x0, 710 | 0x0, 0x0, 0x0, 0xff, 0x60, 0x0, 0x0, 0x6, 711 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 712 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 713 | 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 714 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x66, 715 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x0, 0x0, 0x0, 716 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 717 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 718 | 0x33, 0x33, 0x33, 0x33, 0x33, 0x0, 0x0, 0x0, 719 | 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 720 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 721 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 722 | 0x0, 0x0, 0x0, 0xff, 0x83, 0x33, 0x33, 0x38, 723 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 724 | 0x60, 0x0, 0x0, 0x6, 0xff, 0x0, 0x0, 0x0, 725 | 0x0, 0x0, 0x0, 0xff, 0xa6, 0x66, 0x66, 0x6a, 726 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 727 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 728 | 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 729 | 0xff, 0x0, 0x0, 0x0, 730 | 731 | /* U+F0633 "󰘳" */ 732 | 0x0, 0x3, 0x55, 0x10, 0x0, 0x0, 0x0, 0x0, 733 | 0x1, 0x55, 0x30, 0x0, 0x2, 0xcf, 0xff, 0xf8, 734 | 0x0, 0x0, 0x0, 0x0, 0x8f, 0xff, 0xfc, 0x20, 735 | 0x1e, 0xff, 0xff, 0xff, 0x90, 0x0, 0x0, 0x9, 736 | 0xff, 0xff, 0xff, 0xe1, 0x9f, 0xf5, 0x1, 0xbf, 737 | 0xf3, 0x0, 0x0, 0x3f, 0xfa, 0x10, 0x6f, 0xf9, 738 | 0xef, 0x90, 0x0, 0xf, 0xf7, 0x0, 0x0, 0x7f, 739 | 0xf0, 0x0, 0x9, 0xfe, 0xff, 0x70, 0x0, 0xc, 740 | 0xf9, 0x0, 0x0, 0x9f, 0xc0, 0x0, 0x7, 0xff, 741 | 0xdf, 0xb0, 0x0, 0xc, 0xf9, 0x0, 0x0, 0x9f, 742 | 0xc0, 0x0, 0xb, 0xfd, 0x7f, 0xf9, 0x43, 0x3d, 743 | 0xfa, 0x33, 0x33, 0xaf, 0xd3, 0x34, 0xaf, 0xf7, 744 | 0xc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 745 | 0xff, 0xff, 0xff, 0xb0, 0x0, 0x8f, 0xff, 0xff, 746 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x0, 747 | 0x0, 0x0, 0x22, 0x2d, 0xfa, 0x22, 0x22, 0xaf, 748 | 0xd2, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 749 | 0xf9, 0x0, 0x0, 0x9f, 0xc0, 0x0, 0x0, 0x0, 750 | 0x0, 0x0, 0x0, 0xc, 0xf9, 0x0, 0x0, 0x9f, 751 | 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 752 | 0xf9, 0x0, 0x0, 0x9f, 0xc0, 0x0, 0x0, 0x0, 753 | 0x0, 0x0, 0x0, 0xc, 0xf9, 0x0, 0x0, 0x9f, 754 | 0xc0, 0x0, 0x0, 0x0, 0x0, 0x6c, 0xff, 0xff, 755 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc5, 0x0, 756 | 0xa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 757 | 0xff, 0xff, 0xff, 0x90, 0x6f, 0xfc, 0x65, 0x5d, 758 | 0xfb, 0x55, 0x55, 0xbf, 0xd5, 0x56, 0xcf, 0xf6, 759 | 0xcf, 0xc0, 0x0, 0xc, 0xf9, 0x0, 0x0, 0x9f, 760 | 0xc0, 0x0, 0xc, 0xfc, 0xff, 0x70, 0x0, 0xc, 761 | 0xf9, 0x0, 0x0, 0x9f, 0xc0, 0x0, 0x7, 0xff, 762 | 0xef, 0x90, 0x0, 0xf, 0xf8, 0x0, 0x0, 0x8f, 763 | 0xe0, 0x0, 0x9, 0xfe, 0xaf, 0xf4, 0x0, 0xaf, 764 | 0xf3, 0x0, 0x0, 0x4f, 0xf9, 0x0, 0x4f, 0xfa, 765 | 0x2f, 0xff, 0xef, 0xff, 0xa0, 0x0, 0x0, 0xb, 766 | 0xff, 0xfe, 0xff, 0xe2, 0x3, 0xef, 0xff, 0xfa, 767 | 0x0, 0x0, 0x0, 0x0, 0xbf, 0xff, 0xfd, 0x30, 768 | 0x0, 0x5, 0x87, 0x30, 0x0, 0x0, 0x0, 0x0, 769 | 0x3, 0x77, 0x50, 0x0, 770 | 771 | /* U+F0634 "󰘴" */ 772 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x20, 0x0, 773 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 774 | 0x0, 0x1, 0xde, 0x20, 0x0, 0x0, 0x0, 0x0, 775 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xfe, 776 | 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 777 | 0x0, 0x1, 0xdf, 0xff, 0xfe, 0x20, 0x0, 0x0, 778 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xff, 779 | 0xff, 0xfe, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 780 | 0x0, 0x1, 0xdf, 0xff, 0x76, 0xff, 0xfe, 0x20, 781 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xff, 782 | 0x70, 0x6, 0xff, 0xfe, 0x20, 0x0, 0x0, 0x0, 783 | 0x0, 0x1, 0xdf, 0xff, 0x70, 0x0, 0x5, 0xff, 784 | 0xfe, 0x20, 0x0, 0x0, 0x0, 0x1, 0xdf, 0xff, 785 | 0x70, 0x0, 0x0, 0x5, 0xff, 0xfe, 0x20, 0x0, 786 | 0x0, 0x1, 0xdf, 0xff, 0x60, 0x0, 0x0, 0x0, 787 | 0x5, 0xff, 0xfe, 0x20, 0x0, 0x1, 0xdf, 0xff, 788 | 0x60, 0x0, 0x0, 0x0, 0x0, 0x5, 0xff, 0xfe, 789 | 0x20, 0x1, 0xdf, 0xff, 0x60, 0x0, 0x0, 0x0, 790 | 0x0, 0x0, 0x5, 0xff, 0xfe, 0x20, 0xaf, 0xff, 791 | 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 792 | 0xff, 0xfc, 0x1, 0xdf, 0x60, 0x0, 0x0, 0x0, 793 | 0x0, 0x0, 0x0, 0x0, 0x5, 0xfe, 0x20, 0x1, 794 | 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 795 | 0x0, 0x4, 0x20, 0x0, 796 | 797 | /* U+F0635 "󰘵" */ 798 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 799 | 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 800 | 0x60, 0x0, 0x0, 0x4f, 0xff, 0xff, 0xff, 0xff, 801 | 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 0x4f, 802 | 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xcf, 803 | 0xf7, 0x0, 0x0, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 804 | 0x0, 0x0, 0x0, 0x1f, 0xfe, 0x0, 0x0, 0x0, 805 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 806 | 0xff, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 807 | 0x0, 0x0, 0x0, 0x1, 0xff, 0xe0, 0x0, 0x0, 808 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 809 | 0x8f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 810 | 0x0, 0x0, 0x0, 0x0, 0x1f, 0xff, 0x10, 0x0, 811 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 812 | 0x8, 0xff, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 813 | 0x0, 0x0, 0x0, 0x0, 0x1, 0xff, 0xf1, 0x0, 814 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 815 | 0x0, 0x8f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 816 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xff, 0x10, 817 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 818 | 0x0, 0x8, 0xff, 0x90, 0x0, 0x0, 0x0, 0x0, 819 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xef, 0xf1, 820 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 821 | 0x0, 0x0, 0x7f, 0xf9, 0x0, 0x0, 0x0, 0x0, 822 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0xff, 823 | 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 824 | 0x0, 0x0, 0x7, 0xff, 0xa0, 0x0, 0x0, 0x0, 825 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xef, 826 | 0xf2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 827 | 0x0, 0x0, 0x0, 0x7f, 0xfc, 0xaa, 0xaa, 0xaa, 828 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 829 | 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 830 | 0x0, 0x0, 0x0, 0x7, 0xff, 0xff, 0xff, 0xff, 831 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 832 | 0x11, 0x11, 0x11, 0x11, 833 | 834 | /* U+F0636 "󰘶" */ 835 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x80, 0x0, 836 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 837 | 0x0, 0x7f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 838 | 0x0, 0x0, 0x0, 0x0, 0x7, 0xff, 0xff, 0x80, 839 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 840 | 0x7f, 0xfd, 0xdf, 0xf8, 0x0, 0x0, 0x0, 0x0, 841 | 0x0, 0x0, 0x0, 0x7, 0xff, 0xd1, 0x1d, 0xff, 842 | 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f, 843 | 0xfd, 0x10, 0x1, 0xdf, 0xf8, 0x0, 0x0, 0x0, 844 | 0x0, 0x0, 0x7, 0xff, 0xd1, 0x0, 0x0, 0x1d, 845 | 0xff, 0x80, 0x0, 0x0, 0x0, 0x0, 0x7f, 0xfc, 846 | 0x10, 0x0, 0x0, 0x1, 0xdf, 0xf8, 0x0, 0x0, 847 | 0x0, 0x7, 0xff, 0xc1, 0x0, 0x0, 0x0, 0x0, 848 | 0x1d, 0xff, 0x80, 0x0, 0x0, 0x7f, 0xff, 0x76, 849 | 0x20, 0x0, 0x0, 0x2, 0x68, 0xff, 0xf8, 0x0, 850 | 0x7, 0xff, 0xff, 0xff, 0x60, 0x0, 0x0, 0x6, 851 | 0xff, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xff, 852 | 0x60, 0x0, 0x0, 0x6, 0xff, 0xff, 0xff, 0xf8, 853 | 0x0, 0x0, 0x0, 0xff, 0x60, 0x0, 0x0, 0x6, 854 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 855 | 0x60, 0x0, 0x0, 0x6, 0xff, 0x0, 0x0, 0x0, 856 | 0x0, 0x0, 0x0, 0xff, 0x60, 0x0, 0x0, 0x6, 857 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 858 | 0x60, 0x0, 0x0, 0x6, 0xff, 0x0, 0x0, 0x0, 859 | 0x0, 0x0, 0x0, 0xff, 0x83, 0x33, 0x33, 0x38, 860 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 861 | 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 862 | 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 863 | 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 864 | 0x33, 0x33, 0x33, 0x33, 0x33, 0x0, 0x0, 0x0, 865 | 866 | /* U+F0CF2 "󰳲" */ 867 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 868 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 869 | 0x0, 0x0, 0x9f, 0x50, 0x0, 0x0, 0x0, 0x0, 870 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9f, 0xff, 871 | 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 872 | 0x0, 0x0, 0xaf, 0xff, 0xff, 0x50, 0x0, 0x0, 873 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 874 | 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 0x0, 875 | 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 876 | 0xfe, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 877 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 878 | 0x0, 0x4f, 0xff, 0xd5, 0x6e, 0xff, 0xff, 0xca, 879 | 0xff, 0xfe, 0x0, 0x0, 0x0, 0x4, 0xff, 0xf2, 880 | 0x0, 0x5f, 0xff, 0xb0, 0xa, 0xff, 0xe0, 0x0, 881 | 0x0, 0x0, 0xbf, 0xff, 0x0, 0x2, 0xff, 0xb0, 882 | 0x1, 0xdf, 0xff, 0x50, 0x0, 0x0, 0xbf, 0xff, 883 | 0xf9, 0x1, 0xbf, 0xb0, 0x2, 0xdf, 0xff, 0xff, 884 | 0x50, 0x0, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xb0, 885 | 0x2, 0xef, 0xff, 0xff, 0xff, 0x50, 0x8f, 0xff, 886 | 0xff, 0xff, 0xff, 0xa0, 0x2, 0xef, 0xff, 0xff, 887 | 0xff, 0xff, 0x21, 0xcf, 0xff, 0xff, 0xff, 0xa0, 888 | 0x2, 0xef, 0xff, 0xff, 0xff, 0xff, 0x70, 0x1, 889 | 0xdf, 0xff, 0xff, 0xa0, 0x2, 0xef, 0xb2, 0x4e, 890 | 0xff, 0xff, 0x70, 0x0, 0x1, 0xdf, 0xff, 0xa0, 891 | 0x2, 0xef, 0xf0, 0x0, 0x6f, 0xff, 0x80, 0x0, 892 | 0x0, 0x5, 0xff, 0xf3, 0x3, 0xef, 0xff, 0x0, 893 | 0x7, 0xff, 0xe0, 0x0, 0x0, 0x0, 0x4f, 0xff, 894 | 0xe6, 0xef, 0xff, 0xfb, 0x35, 0xef, 0xfe, 0x0, 895 | 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 0xff, 896 | 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 0x0, 0x4f, 897 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 898 | 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0xff, 0xff, 899 | 0xff, 0xff, 0xff, 0xff, 0xe0, 0x0, 0x0, 0x0, 900 | 0x1, 0x11, 0x11, 0xcf, 0xff, 0xff, 0x81, 0x11, 901 | 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 902 | 0xbf, 0xff, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 903 | 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x70, 0x0, 904 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 905 | 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 906 | 0x0 907 | }; 908 | 909 | 910 | /*--------------------- 911 | * GLYPH DESCRIPTION 912 | *--------------------*/ 913 | 914 | static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = { 915 | {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */, 916 | {.bitmap_index = 0, .adv_w = 384, .box_w = 14, .box_h = 4, .ofs_x = 5, .ofs_y = 12}, 917 | {.bitmap_index = 28, .adv_w = 384, .box_w = 18, .box_h = 16, .ofs_x = 3, .ofs_y = 14}, 918 | {.bitmap_index = 172, .adv_w = 384, .box_w = 24, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 919 | {.bitmap_index = 472, .adv_w = 384, .box_w = 24, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 920 | {.bitmap_index = 772, .adv_w = 384, .box_w = 24, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, 921 | {.bitmap_index = 1120, .adv_w = 384, .box_w = 24, .box_h = 33, .ofs_x = 0, .ofs_y = -2}, 922 | {.bitmap_index = 1516, .adv_w = 384, .box_w = 24, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 923 | {.bitmap_index = 1816, .adv_w = 384, .box_w = 24, .box_h = 33, .ofs_x = 0, .ofs_y = -2}, 924 | {.bitmap_index = 2212, .adv_w = 384, .box_w = 24, .box_h = 38, .ofs_x = 0, .ofs_y = -5}, 925 | {.bitmap_index = 2668, .adv_w = 384, .box_w = 25, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 926 | {.bitmap_index = 2981, .adv_w = 384, .box_w = 25, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 927 | {.bitmap_index = 3294, .adv_w = 384, .box_w = 24, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 928 | {.bitmap_index = 3594, .adv_w = 384, .box_w = 25, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 929 | {.bitmap_index = 3907, .adv_w = 384, .box_w = 25, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 930 | {.bitmap_index = 4220, .adv_w = 384, .box_w = 26, .box_h = 25, .ofs_x = -1, .ofs_y = 2}, 931 | {.bitmap_index = 4545, .adv_w = 384, .box_w = 24, .box_h = 37, .ofs_x = 0, .ofs_y = -4}, 932 | {.bitmap_index = 4989, .adv_w = 384, .box_w = 24, .box_h = 29, .ofs_x = 0, .ofs_y = 0}, 933 | {.bitmap_index = 5337, .adv_w = 384, .box_w = 24, .box_h = 25, .ofs_x = 0, .ofs_y = 2}, 934 | {.bitmap_index = 5637, .adv_w = 384, .box_w = 25, .box_h = 15, .ofs_x = 0, .ofs_y = 7}, 935 | {.bitmap_index = 5825, .adv_w = 384, .box_w = 24, .box_h = 23, .ofs_x = 0, .ofs_y = 3}, 936 | {.bitmap_index = 6101, .adv_w = 384, .box_w = 24, .box_h = 20, .ofs_x = 0, .ofs_y = 4}, 937 | {.bitmap_index = 6341, .adv_w = 384, .box_w = 25, .box_h = 25, .ofs_x = 0, .ofs_y = 2} 938 | }; 939 | 940 | /*--------------------- 941 | * CHARACTER MAPPING 942 | *--------------------*/ 943 | 944 | static const uint16_t unicode_list_0[] = { 945 | 0x0, 0x31, 0xe5fd, 0xe8b8, 0xf14c, 0xf266 946 | }; 947 | 948 | static const uint16_t unicode_list_1[] = { 949 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 950 | 0x263, 0x479, 0x558, 0x559, 0x55a, 0x55b, 0x55c, 0xc18 951 | }; 952 | 953 | /*Collect the unicode lists and glyph_id offsets*/ 954 | static const lv_font_fmt_txt_cmap_t cmaps[] = 955 | { 956 | { 957 | .range_start = 45, .range_length = 62055, .glyph_id_start = 1, 958 | .unicode_list = unicode_list_0, .glyph_id_ofs_list = NULL, .list_length = 6, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY 959 | }, 960 | { 961 | .range_start = 983258, .range_length = 3097, .glyph_id_start = 7, 962 | .unicode_list = unicode_list_1, .glyph_id_ofs_list = NULL, .list_length = 16, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY 963 | } 964 | }; 965 | 966 | 967 | 968 | /*-------------------- 969 | * ALL CUSTOM DATA 970 | *--------------------*/ 971 | 972 | #if LVGL_VERSION_MAJOR == 8 973 | /*Store all the custom data of the font*/ 974 | static lv_font_fmt_txt_glyph_cache_t cache; 975 | #endif 976 | 977 | #if LVGL_VERSION_MAJOR >= 8 978 | static const lv_font_fmt_txt_dsc_t font_dsc = { 979 | #else 980 | static lv_font_fmt_txt_dsc_t font_dsc = { 981 | #endif 982 | .glyph_bitmap = glyph_bitmap, 983 | .glyph_dsc = glyph_dsc, 984 | .cmaps = cmaps, 985 | .kern_dsc = NULL, 986 | .kern_scale = 0, 987 | .cmap_num = 2, 988 | .bpp = 4, 989 | .kern_classes = 0, 990 | .bitmap_format = 0, 991 | #if LVGL_VERSION_MAJOR == 8 992 | .cache = &cache 993 | #endif 994 | 995 | }; 996 | 997 | extern const lv_font_t lv_font_montserrat_40; 998 | 999 | 1000 | /*----------------- 1001 | * PUBLIC FONT 1002 | *----------------*/ 1003 | 1004 | /*Initialize a public general font descriptor*/ 1005 | #if LVGL_VERSION_MAJOR >= 8 1006 | const lv_font_t NerdFonts_Regular_40 = { 1007 | #else 1008 | lv_font_t NerdFonts_Regular_40 = { 1009 | #endif 1010 | .get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/ 1011 | .get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/ 1012 | .line_height = 38, /*The maximum line height required by the font*/ 1013 | .base_line = 5, /*Baseline measured from the bottom of the line*/ 1014 | #if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0) 1015 | .subpx = LV_FONT_SUBPX_NONE, 1016 | #endif 1017 | #if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8 1018 | .underline_position = -6, 1019 | .underline_thickness = 2, 1020 | #endif 1021 | .dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */ 1022 | #if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9 1023 | .fallback = &lv_font_montserrat_40, 1024 | #endif 1025 | .user_data = NULL, 1026 | }; 1027 | 1028 | 1029 | 1030 | #endif /*#if NERDFONTS_REGULAR_40*/ 1031 | --------------------------------------------------------------------------------