diff --git a/boards/shields/kassio_view/CMakeLists.txt b/boards/shields/kassio_view/CMakeLists.txt new file mode 100644 index 0000000..da8674f --- /dev/null +++ b/boards/shields/kassio_view/CMakeLists.txt @@ -0,0 +1,6 @@ +zephyr_library_include_directories(${CMAKE_SOURCE_DIR}/include) +zephyr_library_sources(custom_status_screen.c) +zephyr_library_sources(widgets/bolt.c) +zephyr_library_sources(widgets/util.c) +zephyr_library_sources(widgets/art.c) +zephyr_library_sources(widgets/peripheral_status.c) diff --git a/boards/shields/kassio_view/Kconfig.defconfig b/boards/shields/kassio_view/Kconfig.defconfig new file mode 100644 index 0000000..53edc1c --- /dev/null +++ b/boards/shields/kassio_view/Kconfig.defconfig @@ -0,0 +1,52 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +if SHIELD_NICE_VIEW + +config LV_Z_VDB_SIZE + default 100 + +config LV_Z_DPI + default 161 + +config LV_Z_BITS_PER_PIXEL + default 1 + +choice LV_COLOR_DEPTH + default LV_COLOR_DEPTH_1 +endchoice + +choice ZMK_DISPLAY_WORK_QUEUE + default ZMK_DISPLAY_WORK_QUEUE_DEDICATED +endchoice + +choice ZMK_DISPLAY_STATUS_SCREEN + default ZMK_DISPLAY_STATUS_SCREEN_CUSTOM +endchoice + +config ZMK_DISPLAY_STATUS_SCREEN_CUSTOM + imply NICE_VIEW_WIDGET_STATUS + +config NICE_VIEW_WIDGET_STATUS + bool "Custom nice!view status widget" + select LV_FONT_MONTSERRAT_16 + select LV_USE_IMG + select LV_USE_CANVAS + +config NICE_VIEW_WIDGET_INVERTED + bool "Invert custom status widget colors" + +if !ZMK_SPLIT || ZMK_SPLIT_ROLE_CENTRAL + +config NICE_VIEW_WIDGET_STATUS + select LV_FONT_MONTSERRAT_18 + select LV_FONT_MONTSERRAT_14 + select LV_FONT_UNSCII_8 + select ZMK_WPM + +endif # !ZMK_SPLIT || ZMK_SPLIT_ROLE_CENTRAL + +config ZMK_DISPLAY_STATUS_SCREEN_BUILT_IN + select LV_FONT_MONTSERRAT_26 + +endif # SHIELD_NICE_VIEW diff --git a/boards/shields/kassio_view/Kconfig.shield b/boards/shields/kassio_view/Kconfig.shield new file mode 100644 index 0000000..ea38bb3 --- /dev/null +++ b/boards/shields/kassio_view/Kconfig.shield @@ -0,0 +1,5 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config SHIELD_NICE_VIEW + def_bool $(shields_list_contains,kassio_view) diff --git a/boards/shields/kassio_view/README.md b/boards/shields/kassio_view/README.md new file mode 100644 index 0000000..00abfbf --- /dev/null +++ b/boards/shields/kassio_view/README.md @@ -0,0 +1,15 @@ +# nice!view + +The nice!view is a low-power, high refresh rate display meant to replace I2C OLEDs traditionally used. + +This shield requires that an `&nice_view_spi` labeled SPI bus is provided with _at least_ MOSI, SCK, and CS pins defined. + +## Disable custom widget + +The nice!view shield includes a custom vertical widget. To use the built-in ZMK one, add the following item to your `.conf` file: + +``` +CONFIG_ZMK_DISPLAY_STATUS_SCREEN_BUILT_IN=y +CONFIG_ZMK_LV_FONT_DEFAULT_SMALL_MONTSERRAT_26=y +CONFIG_LV_FONT_DEFAULT_MONTSERRAT_26=y +``` diff --git a/boards/shields/kassio_view/custom_status_screen.c b/boards/shields/kassio_view/custom_status_screen.c new file mode 100644 index 0000000..c08da0e --- /dev/null +++ b/boards/shields/kassio_view/custom_status_screen.c @@ -0,0 +1,28 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#include "widgets/status.h" + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if IS_ENABLED(CONFIG_NICE_VIEW_WIDGET_STATUS) +static struct zmk_widget_status status_widget; +#endif + +lv_obj_t *zmk_display_status_screen() { + + lv_obj_t *screen; + screen = lv_obj_create(NULL); + +#if IS_ENABLED(CONFIG_NICE_VIEW_WIDGET_STATUS) + zmk_widget_status_init(&status_widget, screen); + lv_obj_align(zmk_widget_status_obj(&status_widget), LV_ALIGN_TOP_LEFT, 0, 0); +#endif + + return screen; +} diff --git a/boards/shields/kassio_view/kassio_view.conf b/boards/shields/kassio_view/kassio_view.conf new file mode 100644 index 0000000..e6f9158 --- /dev/null +++ b/boards/shields/kassio_view/kassio_view.conf @@ -0,0 +1,4 @@ +# Enable nice!view +CONFIG_ZMK_DISPLAY=y +# Disable idle blanking +CONFIG_ZMK_DISPLAY_BLANK_ON_IDLE=n diff --git a/boards/shields/kassio_view/kassio_view.overlay b/boards/shields/kassio_view/kassio_view.overlay new file mode 100644 index 0000000..2afbccf --- /dev/null +++ b/boards/shields/kassio_view/kassio_view.overlay @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +&nice_view_spi { + status = "okay"; + kassio_view: ls0xx@0 { + compatible = "sharp,ls0xx"; + spi-max-frequency = <1000000>; + reg = <0>; + width = <160>; + height = <68>; + }; +}; + +/ { + chosen { + zephyr,display = &kassio_view; + }; +}; diff --git a/boards/shields/kassio_view/kassio_view.zmk.yml b/boards/shields/kassio_view/kassio_view.zmk.yml new file mode 100644 index 0000000..2bdcf67 --- /dev/null +++ b/boards/shields/kassio_view/kassio_view.zmk.yml @@ -0,0 +1,8 @@ +file_format: "1" +id: kassio_view +name: nice!view +type: shield +url: https://nicekeyboards.com/nice-view +requires: [nice_view_header] +features: + - display diff --git a/boards/shields/kassio_view/widgets/art.c b/boards/shields/kassio_view/widgets/art.c new file mode 100644 index 0000000..70117da --- /dev/null +++ b/boards/shields/kassio_view/widgets/art.c @@ -0,0 +1,117 @@ +/* + * + * Copyright (c) 2023 Collin Hodge + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_LOGO +#define LV_ATTRIBUTE_IMG_LOGO +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LOGO uint8_t logo_map[] = { +#if CONFIG_NICE_VIEW_WIDGET_INVERTED + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ +#else + 0x00, 0x00, 0x00, 0xff, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ +#endif + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xf0, 0xff, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xf0, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xf0, + 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0xf0, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xff, 0xe0, + 0x7f, 0xf0, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xc0, 0x7f, 0xf0, 0xff, 0xc0, 0x3f, 0xff, 0xff, + 0xff, 0xc0, 0x3f, 0xf0, 0xff, 0xc0, 0x3f, 0xff, 0xff, 0xff, 0xc0, 0x3f, 0xf0, 0xff, 0xc0, 0x1f, + 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xf0, 0xff, 0x80, 0x1f, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xf0, 0xff, + 0x80, 0x0f, 0xff, 0xff, 0xff, 0x80, 0x0f, 0xf0, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x0f, + 0xf0, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, + 0x00, 0x07, 0xf0, 0xfe, 0x00, 0x07, 0xff, 0xff, 0xff, 0x00, 0x07, 0xf0, 0xfe, 0x00, 0x07, 0xff, + 0xff, 0xfe, 0x00, 0x07, 0xf0, 0xfe, 0x00, 0x03, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xf0, 0xfc, 0x00, + 0x03, 0xff, 0xff, 0xfc, 0x00, 0x03, 0xf0, 0xfc, 0x00, 0x03, 0xff, 0xff, 0xfc, 0x00, 0x01, 0xf0, + 0xf8, 0x00, 0x01, 0xff, 0xff, 0xf4, 0x00, 0x01, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xe0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xf0, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0xf0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf0, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0xf0, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf0, 0xff, 0xf8, 0x00, 0x00, 0x50, 0x00, + 0x00, 0xff, 0xf0, 0xff, 0xf8, 0x00, 0x00, 0x84, 0x00, 0x03, 0xff, 0xf0, 0xff, 0xfe, 0x00, 0x01, + 0x28, 0x00, 0x07, 0xff, 0xf0, 0xff, 0xff, 0x00, 0x04, 0x95, 0x00, 0x0f, 0xff, 0xf0, 0xff, 0xff, + 0xc0, 0x09, 0x28, 0x80, 0x1f, 0xff, 0xf0, 0xff, 0xff, 0xe0, 0x24, 0x95, 0x20, 0x7f, 0xff, 0xf0, + 0xff, 0xff, 0xf8, 0x49, 0x22, 0x40, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xfd, 0x24, 0x95, 0x2b, 0xff, + 0xff, 0xf0, 0xff, 0xff, 0xff, 0x49, 0x22, 0x47, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xa4, 0x95, + 0x2f, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xc9, 0x22, 0x5f, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, + 0xf4, 0x95, 0x7f, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xf9, 0x2a, 0xff, 0xff, 0xff, 0xf0, 0xff, + 0xff, 0xff, 0xfc, 0x93, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, +}; + +const lv_img_dsc_t logo = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 68, + .header.h = 140, + .data_size = 1268, + .data = logo_map, +}; diff --git a/boards/shields/kassio_view/widgets/bolt.c b/boards/shields/kassio_view/widgets/bolt.c new file mode 100644 index 0000000..74dcc2b --- /dev/null +++ b/boards/shields/kassio_view/widgets/bolt.c @@ -0,0 +1,45 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BOLT +#define LV_ATTRIBUTE_IMG_BOLT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BOLT uint8_t bolt_map[] = { +#if CONFIG_NICE_VIEW_WIDGET_INVERTED + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 2*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ +#else + 0x00, 0x00, 0x00, 0x00, /*Color of index 0*/ + 0xff, 0xff, 0xff, 0xff, /*Color of index 1*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 2*/ + 0x00, 0x00, 0x00, 0x00, /*Color of index 3*/ +#endif + + 0x00, 0x14, 0x00, 0x00, 0x64, 0x00, 0x00, 0x64, 0x00, 0x01, 0xa4, 0x00, 0x01, 0xa4, + 0x00, 0x06, 0xa4, 0x00, 0x06, 0xa4, 0x00, 0x1a, 0xa5, 0x54, 0x1a, 0xaa, 0xa4, 0x6a, + 0xaa, 0x90, 0x55, 0x6a, 0x90, 0x00, 0x6a, 0x40, 0x00, 0x6a, 0x40, 0x00, 0x69, 0x00, + 0x00, 0x69, 0x00, 0x00, 0x64, 0x00, 0x00, 0x64, 0x00, 0x00, 0x50, 0x00, +}; + +const lv_img_dsc_t bolt = { + .header.cf = LV_IMG_CF_INDEXED_2BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 11, + .header.h = 18, + .data_size = 70, + .data = bolt_map, +}; diff --git a/boards/shields/kassio_view/widgets/peripheral_status.c b/boards/shields/kassio_view/widgets/peripheral_status.c new file mode 100644 index 0000000..eb98993 --- /dev/null +++ b/boards/shields/kassio_view/widgets/peripheral_status.c @@ -0,0 +1,127 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#include +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "peripheral_status.h" +#include +#include +#include +#include +#include +#include +#include + +LV_IMG_DECLARE(logo); + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); + +struct peripheral_status_state { + bool connected; +}; + +static void draw_top(lv_obj_t *widget, lv_color_t cbuf[], const struct status_state *state) { + lv_obj_t *canvas = lv_obj_get_child(widget, 0); + + lv_draw_label_dsc_t label_dsc; + init_label_dsc(&label_dsc, LVGL_FOREGROUND, &lv_font_montserrat_16, LV_TEXT_ALIGN_RIGHT); + lv_draw_rect_dsc_t rect_black_dsc; + init_rect_dsc(&rect_black_dsc, LVGL_BACKGROUND); + + // Fill background + lv_canvas_draw_rect(canvas, 0, 0, CANVAS_SIZE, CANVAS_SIZE, &rect_black_dsc); + + // Draw battery + draw_battery(canvas, state); + + // Draw output status + lv_canvas_draw_text(canvas, 0, 0, CANVAS_SIZE, &label_dsc, + state->connected ? LV_SYMBOL_WIFI : LV_SYMBOL_CLOSE); + + // Rotate canvas + rotate_canvas(canvas, cbuf); +} + +static void set_battery_status(struct zmk_widget_status *widget, + struct battery_status_state state) { +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + widget->state.charging = state.usb_present; +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + + widget->state.battery = state.level; + + draw_top(widget->obj, widget->cbuf, &widget->state); +} + +static void battery_status_update_cb(struct battery_status_state state) { + struct zmk_widget_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_status(widget, state); } +} + +static struct battery_status_state battery_status_get_state(const zmk_event_t *eh) { + return (struct battery_status_state) { + .level = bt_bas_get_battery_level(), +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + .usb_present = zmk_usb_is_powered(), +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + }; +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_battery_status, struct battery_status_state, + battery_status_update_cb, battery_status_get_state) + +ZMK_SUBSCRIPTION(widget_battery_status, zmk_battery_state_changed); +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) +ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + +static struct peripheral_status_state get_state(const zmk_event_t *_eh) { + return (struct peripheral_status_state){.connected = zmk_split_bt_peripheral_is_connected()}; +} + +static void set_connection_status(struct zmk_widget_status *widget, + struct peripheral_status_state state) { + widget->state.connected = state.connected; + + draw_top(widget->obj, widget->cbuf, &widget->state); +} + +static void output_status_update_cb(struct peripheral_status_state state) { + struct zmk_widget_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_connection_status(widget, state); } +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_peripheral_status, struct peripheral_status_state, + output_status_update_cb, get_state) +ZMK_SUBSCRIPTION(widget_peripheral_status, zmk_split_peripheral_status_changed); + +int zmk_widget_status_init(struct zmk_widget_status *widget, lv_obj_t *parent) { + widget->obj = lv_obj_create(parent); + lv_obj_set_size(widget->obj, 160, 68); + lv_obj_t *top = lv_canvas_create(widget->obj); + lv_obj_align(top, LV_ALIGN_TOP_RIGHT, 0, 0); + lv_canvas_set_buffer(top, widget->cbuf, CANVAS_SIZE, CANVAS_SIZE, LV_IMG_CF_TRUE_COLOR); + + lv_obj_t *art = lv_img_create(widget->obj); + bool random = sys_rand32_get() & 1; + lv_img_set_src(art, &logo); + lv_obj_align(art, LV_ALIGN_TOP_LEFT, 0, 0); + + sys_slist_append(&widgets, &widget->node); + widget_battery_status_init(); + widget_peripheral_status_init(); + + return 0; +} + +lv_obj_t *zmk_widget_status_obj(struct zmk_widget_status *widget) { return widget->obj; } diff --git a/boards/shields/kassio_view/widgets/peripheral_status.h b/boards/shields/kassio_view/widgets/peripheral_status.h new file mode 100644 index 0000000..8159635 --- /dev/null +++ b/boards/shields/kassio_view/widgets/peripheral_status.h @@ -0,0 +1,22 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#pragma once + +#include +#include +#include "util.h" + +struct zmk_widget_status { + sys_snode_t node; + lv_obj_t *obj; + lv_color_t cbuf[CANVAS_SIZE * CANVAS_SIZE]; + struct status_state state; +}; + +int zmk_widget_status_init(struct zmk_widget_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_status_obj(struct zmk_widget_status *widget); diff --git a/boards/shields/kassio_view/widgets/status.c b/boards/shields/kassio_view/widgets/status.c new file mode 100644 index 0000000..96b7d45 --- /dev/null +++ b/boards/shields/kassio_view/widgets/status.c @@ -0,0 +1,331 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include "status.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); + +struct output_status_state { + struct zmk_endpoint_instance selected_endpoint; + int active_profile_index; + bool active_profile_connected; + bool active_profile_bonded; +}; + +struct layer_status_state { + uint8_t index; + const char *label; +}; + +struct wpm_status_state { + uint8_t wpm; +}; + +static void draw_top(lv_obj_t *widget, lv_color_t cbuf[], const struct status_state *state) { + lv_obj_t *canvas = lv_obj_get_child(widget, 0); + + lv_draw_label_dsc_t label_dsc; + init_label_dsc(&label_dsc, LVGL_FOREGROUND, &lv_font_montserrat_16, LV_TEXT_ALIGN_RIGHT); + lv_draw_label_dsc_t label_dsc_wpm; + init_label_dsc(&label_dsc_wpm, LVGL_FOREGROUND, &lv_font_unscii_8, LV_TEXT_ALIGN_RIGHT); + lv_draw_rect_dsc_t rect_black_dsc; + init_rect_dsc(&rect_black_dsc, LVGL_BACKGROUND); + lv_draw_rect_dsc_t rect_white_dsc; + init_rect_dsc(&rect_white_dsc, LVGL_FOREGROUND); + lv_draw_line_dsc_t line_dsc; + init_line_dsc(&line_dsc, LVGL_FOREGROUND, 1); + + // Fill background + lv_canvas_draw_rect(canvas, 0, 0, CANVAS_SIZE, CANVAS_SIZE, &rect_black_dsc); + + // Draw battery + draw_battery(canvas, state); + + // Draw output status + char output_text[10] = {}; + + switch (state->selected_endpoint.transport) { + case ZMK_TRANSPORT_USB: + strcat(output_text, LV_SYMBOL_USB); + break; + case ZMK_TRANSPORT_BLE: + if (state->active_profile_bonded) { + if (state->active_profile_connected) { + strcat(output_text, LV_SYMBOL_WIFI); + } else { + strcat(output_text, LV_SYMBOL_CLOSE); + } + } else { + strcat(output_text, LV_SYMBOL_SETTINGS); + } + break; + } + + lv_canvas_draw_text(canvas, 0, 0, CANVAS_SIZE, &label_dsc, output_text); + + // Draw WPM + lv_canvas_draw_rect(canvas, 0, 21, 68, 42, &rect_white_dsc); + lv_canvas_draw_rect(canvas, 1, 22, 66, 40, &rect_black_dsc); + + char wpm_text[6] = {}; + snprintf(wpm_text, sizeof(wpm_text), "%d", state->wpm[9]); + lv_canvas_draw_text(canvas, 42, 52, 24, &label_dsc_wpm, wpm_text); + + int max = 0; + int min = 256; + + for (int i = 0; i < 10; i++) { + if (state->wpm[i] > max) { + max = state->wpm[i]; + } + if (state->wpm[i] < min) { + min = state->wpm[i]; + } + } + + int range = max - min; + if (range == 0) { + range = 1; + } + + lv_point_t points[10]; + for (int i = 0; i < 10; i++) { + points[i].x = 2 + i * 7; + points[i].y = 60 - (state->wpm[i] - min) * 36 / range; + } + lv_canvas_draw_line(canvas, points, 10, &line_dsc); + + // Rotate canvas + rotate_canvas(canvas, cbuf); +} + +static void draw_middle(lv_obj_t *widget, lv_color_t cbuf[], const struct status_state *state) { + lv_obj_t *canvas = lv_obj_get_child(widget, 1); + + lv_draw_rect_dsc_t rect_black_dsc; + init_rect_dsc(&rect_black_dsc, LVGL_BACKGROUND); + lv_draw_rect_dsc_t rect_white_dsc; + init_rect_dsc(&rect_white_dsc, LVGL_FOREGROUND); + lv_draw_arc_dsc_t arc_dsc; + init_arc_dsc(&arc_dsc, LVGL_FOREGROUND, 2); + lv_draw_arc_dsc_t arc_dsc_filled; + init_arc_dsc(&arc_dsc_filled, LVGL_FOREGROUND, 9); + lv_draw_label_dsc_t label_dsc; + init_label_dsc(&label_dsc, LVGL_FOREGROUND, &lv_font_montserrat_18, LV_TEXT_ALIGN_CENTER); + lv_draw_label_dsc_t label_dsc_black; + init_label_dsc(&label_dsc_black, LVGL_BACKGROUND, &lv_font_montserrat_18, LV_TEXT_ALIGN_CENTER); + + // Fill background + lv_canvas_draw_rect(canvas, 0, 0, CANVAS_SIZE, CANVAS_SIZE, &rect_black_dsc); + + // Draw circles + int circle_offsets[5][2] = { + {13, 13}, {55, 13}, {34, 34}, {13, 55}, {55, 55}, + }; + + for (int i = 0; i < 5; i++) { + bool selected = i == state->active_profile_index; + + lv_canvas_draw_arc(canvas, circle_offsets[i][0], circle_offsets[i][1], 13, 0, 359, + &arc_dsc); + + if (selected) { + lv_canvas_draw_arc(canvas, circle_offsets[i][0], circle_offsets[i][1], 9, 0, 359, + &arc_dsc_filled); + } + + char label[2]; + snprintf(label, sizeof(label), "%d", i + 1); + lv_canvas_draw_text(canvas, circle_offsets[i][0] - 8, circle_offsets[i][1] - 10, 16, + (selected ? &label_dsc_black : &label_dsc), label); + } + + // Rotate canvas + rotate_canvas(canvas, cbuf); +} + +static void draw_bottom(lv_obj_t *widget, lv_color_t cbuf[], const struct status_state *state) { + lv_obj_t *canvas = lv_obj_get_child(widget, 2); + + lv_draw_rect_dsc_t rect_black_dsc; + init_rect_dsc(&rect_black_dsc, LVGL_BACKGROUND); + lv_draw_label_dsc_t label_dsc; + init_label_dsc(&label_dsc, LVGL_FOREGROUND, &lv_font_montserrat_14, LV_TEXT_ALIGN_CENTER); + + // Fill background + lv_canvas_draw_rect(canvas, 0, 0, CANVAS_SIZE, CANVAS_SIZE, &rect_black_dsc); + + // Draw layer + if (state->layer_label == NULL) { + char text[9] = {}; + + sprintf(text, "LAYER %i", state->layer_index); + + lv_canvas_draw_text(canvas, 0, 5, 68, &label_dsc, text); + } else { + lv_canvas_draw_text(canvas, 0, 5, 68, &label_dsc, state->layer_label); + } + + // Rotate canvas + rotate_canvas(canvas, cbuf); +} + +static void set_battery_status(struct zmk_widget_status *widget, + struct battery_status_state state) { +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + widget->state.charging = state.usb_present; +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + + widget->state.battery = state.level; + + draw_top(widget->obj, widget->cbuf, &widget->state); +} + +static void battery_status_update_cb(struct battery_status_state state) { + struct zmk_widget_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_battery_status(widget, state); } +} + +static struct battery_status_state battery_status_get_state(const zmk_event_t *eh) { + return (struct battery_status_state) { + .level = bt_bas_get_battery_level(), +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + .usb_present = zmk_usb_is_powered(), +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + }; +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_battery_status, struct battery_status_state, + battery_status_update_cb, battery_status_get_state) + +ZMK_SUBSCRIPTION(widget_battery_status, zmk_battery_state_changed); +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) +ZMK_SUBSCRIPTION(widget_battery_status, zmk_usb_conn_state_changed); +#endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK) */ + +static void set_output_status(struct zmk_widget_status *widget, + const struct output_status_state *state) { + widget->state.selected_endpoint = state->selected_endpoint; + widget->state.active_profile_index = state->active_profile_index; + widget->state.active_profile_connected = state->active_profile_connected; + widget->state.active_profile_bonded = state->active_profile_bonded; + + draw_top(widget->obj, widget->cbuf, &widget->state); + draw_middle(widget->obj, widget->cbuf2, &widget->state); +} + +static void output_status_update_cb(struct output_status_state state) { + struct zmk_widget_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_output_status(widget, &state); } +} + +static struct output_status_state output_status_get_state(const zmk_event_t *_eh) { + return (struct output_status_state){ + .selected_endpoint = zmk_endpoints_selected(), + .active_profile_index = zmk_ble_active_profile_index(), + .active_profile_connected = zmk_ble_active_profile_is_connected(), + .active_profile_bonded = !zmk_ble_active_profile_is_open(), + }; +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_output_status, struct output_status_state, + output_status_update_cb, output_status_get_state) +ZMK_SUBSCRIPTION(widget_output_status, zmk_endpoint_changed); + +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) +ZMK_SUBSCRIPTION(widget_output_status, zmk_usb_conn_state_changed); +#endif +#if defined(CONFIG_ZMK_BLE) +ZMK_SUBSCRIPTION(widget_output_status, zmk_ble_active_profile_changed); +#endif + +static void set_layer_status(struct zmk_widget_status *widget, struct layer_status_state state) { + widget->state.layer_index = state.index; + widget->state.layer_label = state.label; + + draw_bottom(widget->obj, widget->cbuf3, &widget->state); +} + +static void layer_status_update_cb(struct layer_status_state state) { + struct zmk_widget_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_layer_status(widget, state); } +} + +static struct layer_status_state layer_status_get_state(const zmk_event_t *eh) { + uint8_t index = zmk_keymap_highest_layer_active(); + return (struct layer_status_state){.index = index, .label = zmk_keymap_layer_name(index)}; +} + +ZMK_DISPLAY_WIDGET_LISTENER(widget_layer_status, struct layer_status_state, layer_status_update_cb, + layer_status_get_state) + +ZMK_SUBSCRIPTION(widget_layer_status, zmk_layer_state_changed); + +static void set_wpm_status(struct zmk_widget_status *widget, struct wpm_status_state state) { + for (int i = 0; i < 9; i++) { + widget->state.wpm[i] = widget->state.wpm[i + 1]; + } + widget->state.wpm[9] = state.wpm; + + draw_top(widget->obj, widget->cbuf, &widget->state); +} + +static void wpm_status_update_cb(struct wpm_status_state state) { + struct zmk_widget_status *widget; + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_wpm_status(widget, state); } +} + +struct wpm_status_state wpm_status_get_state(const zmk_event_t *eh) { + return (struct wpm_status_state){.wpm = zmk_wpm_get_state()}; +}; + +ZMK_DISPLAY_WIDGET_LISTENER(widget_wpm_status, struct wpm_status_state, wpm_status_update_cb, + wpm_status_get_state) +ZMK_SUBSCRIPTION(widget_wpm_status, zmk_wpm_state_changed); + +int zmk_widget_status_init(struct zmk_widget_status *widget, lv_obj_t *parent) { + widget->obj = lv_obj_create(parent); + lv_obj_set_size(widget->obj, 160, 68); + lv_obj_t *top = lv_canvas_create(widget->obj); + lv_obj_align(top, LV_ALIGN_TOP_RIGHT, 0, 0); + lv_canvas_set_buffer(top, widget->cbuf, CANVAS_SIZE, CANVAS_SIZE, LV_IMG_CF_TRUE_COLOR); + lv_obj_t *middle = lv_canvas_create(widget->obj); + lv_obj_align(middle, LV_ALIGN_TOP_LEFT, 24, 0); + lv_canvas_set_buffer(middle, widget->cbuf2, CANVAS_SIZE, CANVAS_SIZE, LV_IMG_CF_TRUE_COLOR); + lv_obj_t *bottom = lv_canvas_create(widget->obj); + lv_obj_align(bottom, LV_ALIGN_TOP_LEFT, -44, 0); + lv_canvas_set_buffer(bottom, widget->cbuf3, CANVAS_SIZE, CANVAS_SIZE, LV_IMG_CF_TRUE_COLOR); + + sys_slist_append(&widgets, &widget->node); + widget_battery_status_init(); + widget_output_status_init(); + widget_layer_status_init(); + widget_wpm_status_init(); + + return 0; +} + +lv_obj_t *zmk_widget_status_obj(struct zmk_widget_status *widget) { return widget->obj; } diff --git a/boards/shields/kassio_view/widgets/status.h b/boards/shields/kassio_view/widgets/status.h new file mode 100644 index 0000000..53a2251 --- /dev/null +++ b/boards/shields/kassio_view/widgets/status.h @@ -0,0 +1,24 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#pragma once + +#include +#include +#include "util.h" + +struct zmk_widget_status { + sys_snode_t node; + lv_obj_t *obj; + lv_color_t cbuf[CANVAS_SIZE * CANVAS_SIZE]; + lv_color_t cbuf2[CANVAS_SIZE * CANVAS_SIZE]; + lv_color_t cbuf3[CANVAS_SIZE * CANVAS_SIZE]; + struct status_state state; +}; + +int zmk_widget_status_init(struct zmk_widget_status *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_status_obj(struct zmk_widget_status *widget); diff --git a/boards/shields/kassio_view/widgets/util.c b/boards/shields/kassio_view/widgets/util.c new file mode 100644 index 0000000..b4915ab --- /dev/null +++ b/boards/shields/kassio_view/widgets/util.c @@ -0,0 +1,69 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#include +#include "util.h" + +LV_IMG_DECLARE(bolt); + +void rotate_canvas(lv_obj_t *canvas, lv_color_t cbuf[]) { + static lv_color_t cbuf_tmp[CANVAS_SIZE * CANVAS_SIZE]; + memcpy(cbuf_tmp, cbuf, sizeof(cbuf_tmp)); + lv_img_dsc_t img; + img.data = (void *)cbuf_tmp; + img.header.cf = LV_IMG_CF_TRUE_COLOR; + img.header.w = CANVAS_SIZE; + img.header.h = CANVAS_SIZE; + + lv_canvas_fill_bg(canvas, LVGL_BACKGROUND, LV_OPA_COVER); + lv_canvas_transform(canvas, &img, 900, LV_IMG_ZOOM_NONE, -1, 0, CANVAS_SIZE / 2, + CANVAS_SIZE / 2, true); +} + +void draw_battery(lv_obj_t *canvas, const struct status_state *state) { + lv_draw_rect_dsc_t rect_black_dsc; + init_rect_dsc(&rect_black_dsc, LVGL_BACKGROUND); + lv_draw_rect_dsc_t rect_white_dsc; + init_rect_dsc(&rect_white_dsc, LVGL_FOREGROUND); + + lv_canvas_draw_rect(canvas, 0, 2, 29, 12, &rect_white_dsc); + lv_canvas_draw_rect(canvas, 1, 3, 27, 10, &rect_black_dsc); + lv_canvas_draw_rect(canvas, 2, 4, (state->battery + 2) / 4, 8, &rect_white_dsc); + lv_canvas_draw_rect(canvas, 30, 5, 3, 6, &rect_white_dsc); + lv_canvas_draw_rect(canvas, 31, 6, 1, 4, &rect_black_dsc); + + if (state->charging) { + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + lv_canvas_draw_img(canvas, 9, -1, &bolt, &img_dsc); + } +} + +void init_label_dsc(lv_draw_label_dsc_t *label_dsc, lv_color_t color, const lv_font_t *font, + lv_text_align_t align) { + lv_draw_label_dsc_init(label_dsc); + label_dsc->color = color; + label_dsc->font = font; + label_dsc->align = align; +} + +void init_rect_dsc(lv_draw_rect_dsc_t *rect_dsc, lv_color_t bg_color) { + lv_draw_rect_dsc_init(rect_dsc); + rect_dsc->bg_color = bg_color; +} + +void init_line_dsc(lv_draw_line_dsc_t *line_dsc, lv_color_t color, uint8_t width) { + lv_draw_line_dsc_init(line_dsc); + line_dsc->color = color; + line_dsc->width = width; +} + +void init_arc_dsc(lv_draw_arc_dsc_t *arc_dsc, lv_color_t color, uint8_t width) { + lv_draw_arc_dsc_init(arc_dsc); + arc_dsc->color = color; + arc_dsc->width = width; +} diff --git a/boards/shields/kassio_view/widgets/util.h b/boards/shields/kassio_view/widgets/util.h new file mode 100644 index 0000000..fbcb616 --- /dev/null +++ b/boards/shields/kassio_view/widgets/util.h @@ -0,0 +1,47 @@ +/* + * + * Copyright (c) 2023 The ZMK Contributors + * SPDX-License-Identifier: MIT + * + */ + +#include +#include + +#define CANVAS_SIZE 68 + +#define LVGL_BACKGROUND \ + IS_ENABLED(CONFIG_NICE_VIEW_WIDGET_INVERTED) ? lv_color_black() : lv_color_white() +#define LVGL_FOREGROUND \ + IS_ENABLED(CONFIG_NICE_VIEW_WIDGET_INVERTED) ? lv_color_white() : lv_color_black() + +struct status_state { + uint8_t battery; + bool charging; +#if !IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) + struct zmk_endpoint_instance selected_endpoint; + int active_profile_index; + bool active_profile_connected; + bool active_profile_bonded; + uint8_t layer_index; + const char *layer_label; + uint8_t wpm[10]; +#else + bool connected; +#endif +}; + +struct battery_status_state { + uint8_t level; +#if IS_ENABLED(CONFIG_USB_DEVICE_STACK) + bool usb_present; +#endif +}; + +void rotate_canvas(lv_obj_t *canvas, lv_color_t cbuf[]); +void draw_battery(lv_obj_t *canvas, const struct status_state *state); +void init_label_dsc(lv_draw_label_dsc_t *label_dsc, lv_color_t color, const lv_font_t *font, + lv_text_align_t align); +void init_rect_dsc(lv_draw_rect_dsc_t *rect_dsc, lv_color_t bg_color); +void init_line_dsc(lv_draw_line_dsc_t *line_dsc, lv_color_t color, uint8_t width); +void init_arc_dsc(lv_draw_arc_dsc_t *arc_dsc, lv_color_t color, uint8_t width); diff --git a/build.yaml b/build.yaml index cfcd4fb..6b1365c 100644 --- a/build.yaml +++ b/build.yaml @@ -1,6 +1,6 @@ --- include: - board: nice_nano_v2 - shield: helix_left nice_view_adapter nice_view + shield: helix_left nice_view_adapter kassio_view - board: nice_nano_v2 - shield: helix_right nice_view_adapter nice_view + shield: helix_right nice_view_adapter kassio_view