diff --git a/app/include/zmk/display/widgets/bongo_cat.h b/app/include/zmk/display/widgets/bongo_cat.h new file mode 100644 index 00000000000..28fb9431375 --- /dev/null +++ b/app/include/zmk/display/widgets/bongo_cat.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +struct zmk_widget_bongo_cat { + sys_snode_t node; + lv_obj_t *obj; + bool is_right; +}; + +int zmk_widget_bongo_cat_init(struct zmk_widget_bongo_cat *widget, lv_obj_t *parent); +lv_obj_t *zmk_widget_bongo_cat_obj(struct zmk_widget_bongo_cat *widget); diff --git a/app/src/display/status_screen.c b/app/src/display/status_screen.c index 6ace1925af8..50428f955d7 100644 --- a/app/src/display/status_screen.c +++ b/app/src/display/status_screen.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,10 @@ static struct zmk_widget_layer_status layer_status_widget; static struct zmk_widget_wpm_status wpm_status_widget; #endif +#if IS_ENABLED(CONFIG_ZMK_WIDGET_BONGO_CAT) +static struct zmk_widget_bongo_cat bongo_cat_widget; +#endif + lv_obj_t *zmk_display_status_screen() { lv_obj_t *screen; @@ -70,6 +75,10 @@ lv_obj_t *zmk_display_status_screen() { zmk_widget_wpm_status_init(&wpm_status_widget, screen); lv_obj_align(zmk_widget_wpm_status_obj(&wpm_status_widget), NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -12, 0); +#endif +#if IS_ENABLED(CONFIG_ZMK_WIDGET_BONGO_CAT) + zmk_widget_bongo_cat_init(&bongo_cat_widget, screen); + lv_obj_align(zmk_widget_bongo_cat_obj(&bongo_cat_widget), NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); #endif return screen; } diff --git a/app/src/display/widgets/CMakeLists.txt b/app/src/display/widgets/CMakeLists.txt index fbf07072768..c7684896c46 100644 --- a/app/src/display/widgets/CMakeLists.txt +++ b/app/src/display/widgets/CMakeLists.txt @@ -6,3 +6,5 @@ target_sources_ifdef(CONFIG_ZMK_WIDGET_OUTPUT_STATUS app PRIVATE output_status.c target_sources_ifdef(CONFIG_ZMK_WIDGET_PERIPHERAL_STATUS app PRIVATE peripheral_status.c) target_sources_ifdef(CONFIG_ZMK_WIDGET_LAYER_STATUS app PRIVATE layer_status.c) target_sources_ifdef(CONFIG_ZMK_WIDGET_WPM_STATUS app PRIVATE wpm_status.c) +target_sources_ifdef(CONFIG_ZMK_WIDGET_BONGO_CAT app PRIVATE bongo_cat.c) +target_sources_ifdef(CONFIG_ZMK_WIDGET_BONGO_CAT app PRIVATE bongo_img.c) diff --git a/app/src/display/widgets/Kconfig b/app/src/display/widgets/Kconfig index 96e7e16d309..b8a1e484125 100644 --- a/app/src/display/widgets/Kconfig +++ b/app/src/display/widgets/Kconfig @@ -33,4 +33,10 @@ config ZMK_WIDGET_WPM_STATUS select LVGL_USE_LABEL select ZMK_WPM +config ZMK_WIDGET_BONGO_CAT + bool "Widget for displaying bongo cat" + depends on !ZMK_SPLIT || ZMK_SPLIT_BLE_ROLE_CENTRAL + select LVGL_USE_LABEL + select LVGL_USE_IMG + endmenu diff --git a/app/src/display/widgets/bongo_cat.c b/app/src/display/widgets/bongo_cat.c new file mode 100644 index 00000000000..a911350053c --- /dev/null +++ b/app/src/display/widgets/bongo_cat.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include + +static sys_slist_t widgets = SYS_SLIST_STATIC_INIT(&widgets); + +enum bongo_state { + bongo_state_none, /* no hands down */ + bongo_state_left, /* left hand down */ + bongo_state_right, /* right hand down */ +} current_bongo_state; + +LV_IMG_DECLARE(none); +LV_IMG_DECLARE(left); +LV_IMG_DECLARE(right); +LV_IMG_DECLARE(both); + +const void *images[] = { + &none, + &left, + &right, + &both +}; + +int zmk_widget_bongo_cat_init(struct zmk_widget_bongo_cat *widget, lv_obj_t *parent) { + widget->obj = lv_img_create(parent, NULL); + lv_img_set_src(widget->obj, &none); + current_bongo_state = bongo_state_none; + + sys_slist_append(&widgets, &widget->node); + + return 0; +} + +lv_obj_t *zmk_widget_bongo_cat_obj(struct zmk_widget_bongo_cat *widget) { + return widget->obj; +} + +void set_bongo_state(struct zmk_widget_bongo_cat *widget, struct zmk_position_state_changed *ev) { + if (ev == NULL) { + return; + } + + uint8_t tmp = bongo_state_left << widget->is_right; + if (ev->state) { + if (current_bongo_state & (bongo_state_left | bongo_state_right)) { + tmp = bongo_state_left | bongo_state_right; + } + } else { + if (current_bongo_state ^ (bongo_state_left | bongo_state_right)) { + tmp = bongo_state_none; + widget->is_right = !widget->is_right; /* switch hand when return to none state */ + } + } + + if (current_bongo_state == tmp) { + return; + } + + current_bongo_state = tmp; + lv_img_set_src(widget->obj, images[current_bongo_state]); +} + +int bongo_cat_listener(const zmk_event_t *eh) { + struct zmk_widget_bongo_cat *widget; + struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); + SYS_SLIST_FOR_EACH_CONTAINER(&widgets, widget, node) { set_bongo_state(widget, ev); } + return ZMK_EV_EVENT_BUBBLE; +} + +ZMK_LISTENER(widget_bongo_cat, bongo_cat_listener) +ZMK_SUBSCRIPTION(widget_bongo_cat, zmk_position_state_changed); diff --git a/app/src/display/widgets/bongo_img.c b/app/src/display/widgets/bongo_img.c new file mode 100644 index 00000000000..f7b3e0325bc --- /dev/null +++ b/app/src/display/widgets/bongo_img.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/* + * This is a pixel redraw of the redraw of the original Bongo Cat + * https://twitter.com/strayrogue/status/992994454058381312 + * The redraw is distributed under CC BY-SA 4.0 + * https://commons.wikimedia.org/wiki/File:Bongo_Cat_Redraw.png + */ + +#include + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_NONE +#define LV_ATTRIBUTE_IMG_NONE +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_NONE uint8_t none_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x80, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x38, 0x80, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x00, 0x0c, 0xc8, 0x00, + 0x00, 0x82, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, + 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x80, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x80, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, + 0xf0, 0x80, 0x00, 0x48, 0x00, 0x00, 0x10, 0x00, + 0x0f, 0x80, 0x00, 0x31, 0x00, 0x70, 0x10, 0x00, + 0x00, 0xf0, 0x00, 0x06, 0x30, 0x88, 0x20, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x31, 0x04, 0x20, 0x00, + 0x00, 0x00, 0xf0, 0x00, 0x01, 0x04, 0x20, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t none = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 60, + .header.h = 31, + .data_size = 256, + .data = none_map, +}; + +#ifndef LV_ATTRIBUTE_IMG_LEFT +#define LV_ATTRIBUTE_IMG_LEFT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_LEFT uint8_t left_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x80, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x38, 0x80, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x45, 0x00, 0x00, 0x00, 0x0c, 0xc8, 0x00, + 0x00, 0x82, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, + 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x80, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x80, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, + 0xf0, 0x80, 0x00, 0x48, 0x00, 0x00, 0x10, 0x00, + 0x0f, 0x80, 0x00, 0x31, 0x00, 0x00, 0x10, 0x00, + 0x00, 0xf0, 0x00, 0x06, 0x30, 0x00, 0x20, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x30, 0x00, 0x20, 0x00, + 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0xf0, 0x02, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x04, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x0f, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0xf4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xc0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t left = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 60, + .header.h = 31, + .data_size = 256, + .data = left_map, +}; + +#ifndef LV_ATTRIBUTE_IMG_RIGHT +#define LV_ATTRIBUTE_IMG_RIGHT +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_RIGHT uint8_t right_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x80, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0xc8, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x10, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x20, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, + 0xf0, 0x40, 0x00, 0x48, 0x00, 0x00, 0x10, 0x00, + 0x0f, 0x40, 0x00, 0x31, 0x00, 0x70, 0x10, 0x00, + 0x00, 0x80, 0x20, 0x06, 0x30, 0x88, 0x20, 0x00, + 0x00, 0x80, 0x40, 0x00, 0x31, 0x04, 0x20, 0x00, + 0x01, 0x00, 0xf0, 0x00, 0x01, 0x04, 0x20, 0x00, + 0x01, 0x01, 0x0f, 0x00, 0x01, 0x00, 0x10, 0x00, + 0x01, 0x02, 0x00, 0xf0, 0x01, 0x00, 0x10, 0x00, + 0x00, 0x8c, 0x00, 0x0f, 0x01, 0x00, 0x08, 0x00, + 0x00, 0x70, 0x00, 0x00, 0xf1, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t right = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 60, + .header.h = 31, + .data_size = 256, + .data = right_map, +}; + +#ifndef LV_ATTRIBUTE_IMG_BOTH +#define LV_ATTRIBUTE_IMG_BOTH +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BOTH uint8_t both_map[] = { + 0xff, 0xff, 0xff, 0xff, /*Color of index 0*/ + 0x00, 0x00, 0x00, 0xff, /*Color of index 1*/ + + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x80, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x01, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0xc8, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x08, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x10, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x20, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, + 0xf0, 0x40, 0x00, 0x48, 0x00, 0x00, 0x10, 0x00, + 0x0f, 0x40, 0x00, 0x31, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x80, 0x20, 0x06, 0x30, 0x00, 0x20, 0x00, + 0x00, 0x80, 0x40, 0x00, 0x30, 0x00, 0x20, 0x00, + 0x01, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x01, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x01, 0x02, 0x00, 0xf0, 0x02, 0x00, 0x10, 0x00, + 0x00, 0x8c, 0x00, 0x0f, 0x04, 0x00, 0x08, 0x00, + 0x00, 0x70, 0x00, 0x00, 0xf8, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x0f, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0xf4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0xc0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t both = { + .header.cf = LV_IMG_CF_INDEXED_1BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 60, + .header.h = 31, + .data_size = 256, + .data = both_map, +}; diff --git a/app/src/usb.c b/app/src/usb.c index 5f170ee665a..aa2d3e754dc 100644 --- a/app/src/usb.c +++ b/app/src/usb.c @@ -33,6 +33,7 @@ enum zmk_usb_conn_state zmk_usb_get_conn_state() { switch (usb_status) { case USB_DC_SUSPEND: case USB_DC_CONFIGURED: + case USB_DC_RESUME: return ZMK_USB_CONN_HID; case USB_DC_DISCONNECTED: diff --git a/docs/docs/behaviors/hold-tap.md b/docs/docs/behaviors/hold-tap.md index 528b9aec742..667e5df88f7 100644 --- a/docs/docs/behaviors/hold-tap.md +++ b/docs/docs/behaviors/hold-tap.md @@ -28,7 +28,7 @@ We call this the 'hold-preferred' flavor of hold-taps. While this flavor may wor - The 'hold-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired or another key is pressed. - The 'balanced' flavor will trigger the hold behavior when the `tapping-term-ms` has expired or another key is pressed and released. -- The 'tap-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired. It triggers the tap behavior when another key is pressed. +- The 'tap-preferred' flavor triggers the hold behavior when the `tapping-term-ms` has expired. Pressing another key within `tapping-term-ms` does not affect the decision. - The 'tap-unless-interrupted' flavor triggers a hold behavior only when another key is pressed before `tapping-term-ms` has expired. It triggers the tap behavior in all other situations. When the hold-tap key is released and the hold behavior has not been triggered, the tap behavior will trigger.