events: Remove events unrelated to sliders
What changed, and why it matters
This commit rewrites how on-screen buttons and gestures in the BitBox02 hardware wallet talk to each other. Previously, pressing a button or completing a gesture posted a global 'event' that other parts of the screen had to listen for, which the developer describes as 'racy.' The change replaces that with direct function callbacks, so a button press immediately calls the intended action in a predictable order. This is a code-quality and robustness improvement rather than a fix for a known exploitable bug, but race-prone event systems can in principle lead to wrong buttons being acted upon or actions happening twice.
Treat this as a defensive hardening change. Review that all callback user_data pointers are correctly initialized and that no component still expects the removed events. Verify that the remaining external event handling still correctly filters events by slider location and does not introduce new NULL callback dereferences. No urgent patch is required unless further review finds a concrete race or use-after-free.
Security signals we found
Elimination of a global event bus for internal UI actions
Replacement of emit_event()/on_event dispatch with direct callbacks
Developer note describing the old pattern as 'racy'
Reduction in event IDs in src/ui/event.h
Callback user_data is consistently the parent component pointer, narrowing scope
Evidence from the diff
The patch removes internal UI events (EVENT_CONFIRM, EVENT_FORWARD, EVENT_BACKWARD, EVENT_TOGGLE_ALPHANUMERIC, EVENT_UPDATE_ALPHANUMERIC) from src/ui/event.h and converts the components that emitted or handled them to use registered callbacks with user_data. Affected components include confirm, confirm_gesture, confirm_transaction, icon_button, keyboard_switch, left_arrow, menu, right_arrow, sdcard, and trinary_input_string. The event loop still processes external slider/touch events (slides, taps, continuous taps), but intra-screen communication is now synchronous and scoped to the parent/child relationship. Unit tests are updated to pass callback flags instead of checking emitted events.
Changed components
src/ui/components/confirm.csrc/ui/components/confirm_gesture.csrc/ui/components/confirm_transaction.csrc/ui/components/icon_button.csrc/ui/components/keyboard_switch.csrc/ui/components/left_arrow.csrc/ui/components/menu.csrc/ui/components/right_arrow.csrc/ui/components/sdcard.csrc/ui/components/trinary_input_string.csrc/ui/event.htest/unit-test/test_ui_component_gestures.ctest/unit-test/test_ui_components.cInspect captured patch +216 / −222
diff --git a/src/ui/components/confirm.c b/src/ui/components/confirm.c
index 5ce723a..b852b54 100644
--- a/src/ui/components/confirm.c
+++ b/src/ui/components/confirm.c
@@ -31,8 +31,9 @@ typedef struct {
void* user_data;
} data_t;
-static void _dispatch_confirm(component_t* self)
+static void _on_confirm(void* user_data)
{
+ component_t* self = (component_t*)user_data;
data_t* data = (data_t*)self->data;
if (data->callback) {
data->callback(true, data->user_data);
@@ -40,22 +41,9 @@ static void _dispatch_confirm(component_t* self)
}
}
-static void _on_event(const event_t* event, component_t* component)
+static void _on_cancel(void* user_data)
{
- if (event->id == EVENT_CONFIRM) {
- _dispatch_confirm(component);
- }
-}
-
-static void _on_confirm(component_t* component)
-{
- component_t* self = component->parent;
- _dispatch_confirm(self);
-}
-
-static void _on_cancel(component_t* component)
-{
- component_t* self = component->parent;
+ component_t* self = (component_t*)user_data;
data_t* data = (data_t*)self->data;
if (data->callback) {
data->callback(false, data->user_data);
@@ -71,7 +59,7 @@ static void _on_cancel(component_t* component)
static const component_functions_t _component_functions = {
.cleanup = ui_util_component_cleanup,
.render = ui_util_component_render_subcomponents,
- .on_event = _on_event,
+ .on_event = NULL,
};
/********************************** Create Instance **********************************/
@@ -154,17 +142,18 @@ component_t* confirm_create(
// Create buttons
if (!params->accept_only) {
ui_util_add_sub_component(
- confirm, icon_button_create(slider_position, ICON_BUTTON_CROSS, _on_cancel));
+ confirm, icon_button_create(slider_position, ICON_BUTTON_CROSS, _on_cancel, confirm));
}
if (params->longtouch) {
- ui_util_add_sub_component(confirm, confirm_gesture_create());
+ ui_util_add_sub_component(confirm, confirm_gesture_create(_on_confirm, confirm));
} else {
ui_util_add_sub_component(
confirm,
icon_button_create(
slider_position,
params->accept_is_nextarrow ? ICON_BUTTON_NEXT : ICON_BUTTON_CHECK,
- _on_confirm));
+ _on_confirm,
+ confirm));
}
return confirm;
diff --git a/src/ui/components/confirm_gesture.c b/src/ui/components/confirm_gesture.c
index 5044c44..cfd650a 100644
--- a/src/ui/components/confirm_gesture.c
+++ b/src/ui/components/confirm_gesture.c
@@ -36,6 +36,8 @@ typedef struct {
bool confirmed; // Confirm event occurred
uint16_t active_count; // Start at an offset to allow movement on first touch
uint16_t bottom_arrow_slidein; // from zero to arrow height * SCALE
+ void (*callback)(void* user_data);
+ void* user_data;
} confirm_data_t;
bool confirm_gesture_is_active(component_t* component)
@@ -84,9 +86,9 @@ static void _render(component_t* component)
// The user confirms when the top and bottom arrows touch
if (y0 + arrow_height > y1 && !data->confirmed) {
- event_t event;
- event.id = EVENT_CONFIRM;
- emit_event(&event);
+ if (data->callback) {
+ data->callback(data->user_data);
+ }
data->confirmed = true;
}
}
@@ -155,7 +157,7 @@ static component_functions_t _component_functions = {
/**
* Creates a confirm_gesture component on the top slider.
*/
-component_t* confirm_gesture_create(void)
+component_t* confirm_gesture_create(void (*callback)(void*), void* user_data)
{
confirm_data_t* data = malloc(sizeof(confirm_data_t));
if (!data) {
@@ -167,6 +169,8 @@ component_t* confirm_gesture_create(void)
data->confirmed = false;
data->active_count = SCALE - 1;
data->bottom_arrow_slidein = 0;
+ data->callback = callback;
+ data->user_data = user_data;
component_t* confirm_gesture = malloc(sizeof(component_t));
if (!confirm_gesture) {
diff --git a/src/ui/components/confirm_gesture.h b/src/ui/components/confirm_gesture.h
index 167be8e..2db27a4 100644
--- a/src/ui/components/confirm_gesture.h
+++ b/src/ui/components/confirm_gesture.h
@@ -24,7 +24,7 @@
* Creates a confirm_gesture component on the top slider.
* @param[in] parent The parent component.
*/
-component_t* confirm_gesture_create(void);
+component_t* confirm_gesture_create(void (*callback)(void* user_data), void* user_data);
bool confirm_gesture_is_active(component_t* component);
diff --git a/src/ui/components/confirm_transaction.c b/src/ui/components/confirm_transaction.c
index ce73dda..e72a7ba 100644
--- a/src/ui/components/confirm_transaction.c
+++ b/src/ui/components/confirm_transaction.c
@@ -50,31 +50,20 @@ static void _render(component_t* component)
}
}
-static void _on_event(const event_t* event, component_t* component)
+static void _cancel_cb(void* user_data)
{
- if (event->id == EVENT_CONFIRM) {
- data_t* data = (data_t*)component->data;
- if (data->callback) {
- data->callback(true, data->user_data);
- data->callback = NULL;
- }
- }
-}
-
-static void _cancel(component_t* cancel_button)
-{
- component_t* component = cancel_button->parent;
- data_t* data = (data_t*)component->data;
+ component_t* self = (component_t*)user_data;
+ data_t* data = (data_t*)self->data;
if (data->callback != NULL) {
data->callback(false, data->user_data);
data->callback = NULL;
}
}
-static void _confirm_button_cb(component_t* confirm_button)
+static void _confirm_cb(void* user_data)
{
- component_t* component = confirm_button->parent;
- data_t* data = (data_t*)component->data;
+ component_t* self = (component_t*)user_data;
+ data_t* data = (data_t*)self->data;
if (data->callback) {
data->callback(true, data->user_data);
data->callback = NULL;
@@ -89,7 +78,7 @@ static void _confirm_button_cb(component_t* confirm_button)
static const component_functions_t _component_functions = {
.cleanup = ui_util_component_cleanup,
.render = _render,
- .on_event = _on_event,
+ .on_event = NULL,
};
/********************************** Create Instance **********************************/
@@ -128,13 +117,14 @@ static component_t* _confirm_transaction_create(
confirm->dimension.width = SCREEN_WIDTH;
confirm->dimension.height = SCREEN_HEIGHT;
- ui_util_add_sub_component(confirm, icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel));
+ ui_util_add_sub_component(
+ confirm, icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel_cb, confirm));
if (longtouch) {
- ui_util_add_sub_component(confirm, confirm_gesture_create());
+ ui_util_add_sub_component(confirm, confirm_gesture_create(_confirm_cb, confirm));
} else {
ui_util_add_sub_component(
- confirm, icon_button_create(top_slider, ICON_BUTTON_NEXT, _confirm_button_cb));
+ confirm, icon_button_create(top_slider, ICON_BUTTON_NEXT, _confirm_cb, confirm));
}
if (data->has_address) {
diff --git a/src/ui/components/icon_button.c b/src/ui/components/icon_button.c
index 98d8ed2..f08a65c 100644
--- a/src/ui/components/icon_button.c
+++ b/src/ui/components/icon_button.c
@@ -34,7 +34,8 @@ typedef struct {
bool active; // Marker is 'active', i.e., touched
uint16_t active_count;
icon_button_type_t type;
- void (*callback)(component_t* component);
+ void (*callback)(void* user_data);
+ void* user_data;
} data_t;
/**
@@ -153,7 +154,7 @@ static void _on_event(const event_t* event, component_t* component)
case EVENT_TOP_SHORT_TAP:
case EVENT_BOTTOM_SHORT_TAP:
if (data->callback) {
- data->callback(component);
+ data->callback(data->user_data);
}
break;
default:
@@ -175,7 +176,8 @@ static component_functions_t _component_functions = {
component_t* icon_button_create(
slider_location_t location,
icon_button_type_t type,
- void (*callback)(component_t* component))
+ void (*callback)(void* user_data),
+ void* user_data)
{
component_t* icon_button = malloc(sizeof(component_t));
if (!icon_button) {
@@ -192,6 +194,7 @@ component_t* icon_button_create(
data->active_count = SCALE - 1; // Start at an offset to allow movement on first touch
data->type = type;
data->callback = callback;
+ data->user_data = user_data;
icon_button->data = data;
icon_button->f = &_component_functions;
icon_button->parent = NULL; // Gets set by ui_util_add_sub_component() if `icon_button` is
diff --git a/src/ui/components/icon_button.h b/src/ui/components/icon_button.h
index a126c4b..80a27e1 100644
--- a/src/ui/components/icon_button.h
+++ b/src/ui/components/icon_button.h
@@ -32,6 +32,7 @@ typedef enum {
component_t* icon_button_create(
slider_location_t location,
icon_button_type_t type,
- void (*callback)(component_t* component));
+ void (*callback)(void* user_data),
+ void* user_data);
#endif
diff --git a/src/ui/components/keyboard_switch.c b/src/ui/components/keyboard_switch.c
index 61855db..4c6b56d 100644
--- a/src/ui/components/keyboard_switch.c
+++ b/src/ui/components/keyboard_switch.c
@@ -34,6 +34,8 @@ typedef struct {
bool active; // Marker is 'active', i.e., touched
// if true, the special chars keyboard mode is available.
bool special_chars;
+ void (*on_keyboard_switch_cb)(keyboard_mode_t mode, void* user_data);
+ void* user_data;
} keyboard_switch_data_t;
/**
@@ -90,29 +92,6 @@ static void _on_event(const event_t* event, component_t* component)
keyboard_switch_data_t* ks_data = (keyboard_switch_data_t*)component->data;
const gestures_slider_data_t* slider_data = (const gestures_slider_data_t*)event->data;
switch (event->id) {
- case EVENT_TOGGLE_ALPHANUMERIC:
- switch (ks_data->mode) {
- case LOWER_CASE:
- ks_data->mode = UPPER_CASE;
- break;
- case UPPER_CASE:
- ks_data->mode = DIGITS;
- break;
- case DIGITS:
- ks_data->mode = ks_data->special_chars ? SPECIAL_CHARS : LOWER_CASE;
- break;
- case SPECIAL_CHARS:
- ks_data->mode = LOWER_CASE;
- break;
- default:
- Abort("Keyboard mode unrecognized");
- break;
- }
- break;
-
- case EVENT_UPDATE_ALPHANUMERIC:
- ks_data->mode = *(const keyboard_mode_t*)event->data;
- break;
case EVENT_TOP_CONTINUOUS_TAP:
if (ks_data->location == top_slider && slider_data->position > SLIDER_POSITION_ONE_THIRD &&
slider_data->position <= SLIDER_POSITION_TWO_THIRD) {
@@ -124,9 +103,24 @@ static void _on_event(const event_t* event, component_t* component)
if (ks_data->location == top_slider && slider_data->position > SLIDER_POSITION_ONE_THIRD &&
slider_data->position <= SLIDER_POSITION_TWO_THIRD) {
ks_data->active = false;
- event_t e;
- e.id = EVENT_TOGGLE_ALPHANUMERIC;
- emit_event(&e);
+ switch (ks_data->mode) {
+ case LOWER_CASE:
+ ks_data->mode = UPPER_CASE;
+ break;
+ case UPPER_CASE:
+ ks_data->mode = DIGITS;
+ break;
+ case DIGITS:
+ ks_data->mode = ks_data->special_chars ? SPECIAL_CHARS : LOWER_CASE;
+ break;
+ case SPECIAL_CHARS:
+ ks_data->mode = LOWER_CASE;
+ break;
+ default:
+ Abort("Keyboard mode unrecognized");
+ break;
+ }
+ ks_data->on_keyboard_switch_cb(ks_data->mode, ks_data->user_data);
break;
}
/* FALLTHROUGH */
@@ -153,7 +147,9 @@ component_t* keyboard_switch_create(
slider_location_t location,
bool special_chars,
bool default_to_digits,
- component_t* parent)
+ component_t* parent,
+ void (*on_keyboard_switch_cb)(keyboard_mode_t mode, void* user_data),
+ void* user_data)
{
component_t* keyboard_switch = malloc(sizeof(component_t));
if (!keyboard_switch) {
@@ -171,6 +167,8 @@ component_t* keyboard_switch_create(
ks_data->mode = default_to_digits ? DIGITS : LOWER_CASE;
ks_data->active = false;
ks_data->special_chars = special_chars;
+ ks_data->on_keyboard_switch_cb = on_keyboard_switch_cb;
+ ks_data->user_data = user_data;
keyboard_switch->data = ks_data;
keyboard_switch->f = &_component_functions;
diff --git a/src/ui/components/keyboard_switch.h b/src/ui/components/keyboard_switch.h
index 49645c7..0e3ddbf 100644
--- a/src/ui/components/keyboard_switch.h
+++ b/src/ui/components/keyboard_switch.h
@@ -33,7 +33,9 @@ component_t* keyboard_switch_create(
slider_location_t location,
bool special_chars,
bool default_to_digits,
- component_t* parent);
+ component_t* parent,
+ void (*on_keyboard_switch_cb)(keyboard_mode_t mode, void* user_data),
+ void* user_data);
/**
* @return the currently selected keyboard
diff --git a/src/ui/components/left_arrow.c b/src/ui/components/left_arrow.c
index 93c79a2..e9c9053 100644
--- a/src/ui/components/left_arrow.c
+++ b/src/ui/components/left_arrow.c
@@ -33,6 +33,8 @@
typedef struct {
uint8_t location;
bool active; // Marker is 'active', i.e., touched
+ void (*callback)(void* user_data);
+ void* user_data;
} left_arrow_data_t;
/**
@@ -70,9 +72,9 @@ static void _on_event(const event_t* event, component_t* component)
}
if (slider_data->position <= SLIDER_POSITION_ONE_THIRD) {
data->active = false;
- event_t e;
- e.id = EVENT_BACKWARD;
- emit_event(&e);
+ if (data->callback) {
+ data->callback(data->user_data);
+ }
break;
}
/* FALLTHROUGH */
@@ -111,7 +113,11 @@ static component_functions_t _component_functions = {
* @param[in] location whether the arrow should be rendered on top or bottom (top/bottom slider)
* @param[in] parent The parent component.
*/
-component_t* left_arrow_create(slider_location_t location, component_t* parent)
+component_t* left_arrow_create(
+ slider_location_t location,
+ component_t* parent,
+ void (*callback)(void*),
+ void* user_data)
{
left_arrow_data_t* data = malloc(sizeof(left_arrow_data_t));
if (!data) {
@@ -120,6 +126,8 @@ component_t* left_arrow_create(slider_location_t location, component_t* parent)
memset(data, 0, sizeof(left_arrow_data_t));
data->location = location;
data->active = false;
+ data->callback = callback;
+ data->user_data = user_data;
component_t* left_arrow = malloc(sizeof(component_t));
if (!left_arrow) {
diff --git a/src/ui/components/left_arrow.h b/src/ui/components/left_arrow.h
index 7ae366d..dcb114e 100644
--- a/src/ui/components/left_arrow.h
+++ b/src/ui/components/left_arrow.h
@@ -24,6 +24,10 @@
* Creates a left arrow component.
* param[in] location whether the arrow should be rendered on top or bottom (UPPER/LOWER slider)
*/
-component_t* left_arrow_create(slider_location_t location, component_t* parent);
+component_t* left_arrow_create(
+ slider_location_t location,
+ component_t* parent,
+ void (*callback)(void*),
+ void* user_data);
#endif
diff --git a/src/ui/components/menu.c b/src/ui/components/menu.c
index 198c352..043421d 100644
--- a/src/ui/components/menu.c
+++ b/src/ui/components/menu.c
@@ -66,9 +66,10 @@ static void _select(component_t* button)
}
}
-static void _cancel(component_t* component)
+static void _cancel(void* user_data)
{
- menu_data_t* data = (menu_data_t*)component->parent->data;
+ component_t* self = (component_t*)user_data;
+ menu_data_t* data = (menu_data_t*)self->data;
if (data->cancel_cb != NULL) {
data->cancel_cb(data->cancel_cb_param);
}
@@ -142,26 +143,28 @@ static void _update_arrow_visibility(menu_data_t* data, uint8_t new_index)
}
}
-static void _back(component_t* component)
+static void _back(void* user_data)
{
- menu_data_t* data = (menu_data_t*)component->data;
+ component_t* self = (component_t*)user_data;
+ menu_data_t* data = (menu_data_t*)self->data;
uint8_t new_index = data->index > 0 ? data->index - 1 : data->index;
int32_t diff_to_middle = (data->labels[new_index]->position.left +
data->labels[new_index]->dimension.width / 2 - SCREEN_WIDTH / 2) *
-1;
_update_arrow_visibility(data, new_index);
- _update_positions(component, diff_to_middle);
+ _update_positions(self, diff_to_middle);
}
-static void _forward(component_t* component)
+static void _forward(void* user_data)
{
- menu_data_t* data = (menu_data_t*)component->data;
+ component_t* self = (component_t*)user_data;
+ menu_data_t* data = (menu_data_t*)self->data;
uint8_t new_index = data->index < (data->length - 1) ? data->index + 1 : data->index;
int32_t diff_to_middle = (data->labels[new_index]->position.left +
data->labels[new_index]->dimension.width / 2 - SCREEN_WIDTH / 2) *
-1;
_update_arrow_visibility(data, new_index);
- _update_positions(component, diff_to_middle);
+ _update_positions(self, diff_to_middle);
}
/**
@@ -180,21 +183,6 @@ static void _render(component_t* component)
ui_util_component_render_subcomponents(component);
}
-static void _on_event(const event_t* event, component_t* component)
-{
- // gestures_slider_data_t* slider_data = (gestures_slider_data_t*)event->data;
- switch (event->id) {
- case EVENT_BACKWARD:
- _back(component);
- break;
- case EVENT_FORWARD:
- _forward(component);
- break;
- default:
- break;
- }
-}
-
/**
* Clean-up the component.
*/
@@ -214,7 +202,7 @@ static void _cleanup(component_t* component)
static const component_functions_t _component_functions = {
.cleanup = _cleanup,
.render = _render,
- .on_event = _on_event,
+ .on_event = NULL,
};
/********************************** Create Instance **********************************/
@@ -297,13 +285,14 @@ component_t* menu_create(
}
if (cancel_cb != NULL) {
- ui_util_add_sub_component(menu, icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel));
+ ui_util_add_sub_component(
+ menu, icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel, menu));
}
- data->back_arrow = left_arrow_create(bottom_slider, menu);
+ data->back_arrow = left_arrow_create(bottom_slider, menu, _back, menu);
ui_util_add_sub_component(menu, data->back_arrow);
- data->forward_arrow = right_arrow_create(bottom_slider, menu);
+ data->forward_arrow = right_arrow_create(bottom_slider, menu, _forward, menu);
ui_util_add_sub_component(menu, data->forward_arrow);
_update_arrow_visibility(data, 0);
diff --git a/src/ui/components/right_arrow.c b/src/ui/components/right_arrow.c
index 0f06696..15b637f 100644
--- a/src/ui/components/right_arrow.c
+++ b/src/ui/components/right_arrow.c
@@ -33,6 +33,8 @@
typedef struct {
uint8_t location;
bool active; // Marker is 'active', i.e., touched
+ void (*callback)(void* user_data);
+ void* user_data;
} right_arrow_data_t;
/**
@@ -71,9 +73,9 @@ static void _on_event(const event_t* event, component_t* component)
if (slider_data->position > SLIDER_POSITION_TWO_THIRD &&
slider_data->position <= MAX_SLIDER_POS) {
data->active = false;
- event_t e;
- e.id = EVENT_FORWARD;
- emit_event(&e);
+ if (data->callback) {
+ data->callback(data->user_data);
+ }
break;
}
/* FALLTHROUGH */
@@ -116,7 +118,11 @@ static component_functions_t _component_functions = {
* @param[in] location whether the arrow should be rendered on top or bottom (top/bottom slider)
* @param[in] parent The parent component.
*/
-component_t* right_arrow_create(slider_location_t location, component_t* parent)
+component_t* right_arrow_create(
+ slider_location_t location,
+ component_t* parent,
+ void (*callback)(void*),
+ void* user_data)
{
right_arrow_data_t* data = malloc(sizeof(right_arrow_data_t));
if (!data) {
@@ -124,6 +130,8 @@ component_t* right_arrow_create(slider_location_t location, component_t* parent)
}
memset(data, 0, sizeof(right_arrow_data_t));
data->location = location;
+ data->callback = callback;
+ data->user_data = user_data;
component_t* right_arrow = malloc(sizeof(component_t));
if (!right_arrow) {
diff --git a/src/ui/components/right_arrow.h b/src/ui/components/right_arrow.h
index 817e9cc..b829993 100644
--- a/src/ui/components/right_arrow.h
+++ b/src/ui/components/right_arrow.h
@@ -24,6 +24,10 @@
* Creates a right arrow component.
* param[in] location whether the arrow should be rendered on top or bottom (UPPER/LOWER slider)
*/
-component_t* right_arrow_create(slider_location_t location, component_t* parent);
+component_t* right_arrow_create(
+ slider_location_t location,
+ component_t* parent,
+ void (*callback)(void*),
+ void* user_data);
#endif
diff --git a/src/ui/components/sdcard.c b/src/ui/components/sdcard.c
index 829c5e7..0dc7b04 100644
--- a/src/ui/components/sdcard.c
+++ b/src/ui/components/sdcard.c
@@ -64,9 +64,10 @@ static const component_functions_t _component_functions = {
/********************************** Create Instance **********************************/
-static void _cancel_callback(component_t* component)
+static void _cancel_callback(void* user_data)
{
- data_t* data = (data_t*)component->parent->data;
+ component_t* self = (component_t*)user_data;
+ data_t* data = (data_t*)self->data;
if (data->callback) {
data->callback(false, data->user_data);
data->callback = NULL;
@@ -103,6 +104,6 @@ component_t* sdcard_create(void (*callback)(bool inserted, void* user_data), voi
screen_is_upside_down() ? RIGHT_CENTER : LEFT_CENTER,
component));
ui_util_add_sub_component(
- component, icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel_callback));
+ component, icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel_callback, component));
return component;
}
diff --git a/src/ui/components/trinary_input_string.c b/src/ui/components/trinary_input_string.c
index 0ceb177..2726928 100644
--- a/src/ui/components/trinary_input_string.c
+++ b/src/ui/components/trinary_input_string.c
@@ -247,6 +247,26 @@ static void _render(component_t* component)
}
}
+static void _input_char_set_alphabet(component_t* trinary_char, keyboard_mode_t mode)
+{
+ switch (mode) {
+ case LOWER_CASE:
+ trinary_input_char_set_alphabet(trinary_char, _alphabet_lowercase, 1);
+ break;
+ case UPPER_CASE:
+ trinary_input_char_set_alphabet(trinary_char, _alphabet_uppercase, 1);
+ break;
+ case DIGITS:
+ trinary_input_char_set_alphabet(trinary_char, _digits, 1);
+ break;
+ case SPECIAL_CHARS:
+ trinary_input_char_set_alphabet(trinary_char, _special_chars, 2);
+ break;
+ default:
+ break;
+ }
+}
+
// maybe_autocomplete: if the current input uniquely identifies a word from the wordlist by prefix,
// we autocomplete the word.
static void _set_alphabet(component_t* trinary_input_string, bool maybe_autocomplete)
@@ -315,96 +335,66 @@ static void _set_alphabet(component_t* trinary_input_string, bool maybe_autocomp
trinary_input_char_set_alphabet(trinary_char, charset, 1);
} else if (data->number_input) {
trinary_input_char_set_alphabet(trinary_char, _digits, 1);
- } else {
+ } else if (data->keyboard_switch_component != NULL) {
// Otherwise set the input charset based on the user selected keyboard mode.
keyboard_mode_t keyboard_mode = keyboard_current_mode(data->keyboard_switch_component);
- switch (keyboard_mode) {
- case LOWER_CASE:
- trinary_input_char_set_alphabet(trinary_char, _alphabet_lowercase, 1);
- break;
- case UPPER_CASE:
- trinary_input_char_set_alphabet(trinary_char, _alphabet_uppercase, 1);
- break;
- case DIGITS:
- trinary_input_char_set_alphabet(trinary_char, _digits, 1);
- break;
- case SPECIAL_CHARS:
- trinary_input_char_set_alphabet(trinary_char, _special_chars, 2);
- break;
- default:
- break;
- }
+ _input_char_set_alphabet(trinary_char, keyboard_mode);
}
}
-static void _on_event(const event_t* event, component_t* component)
+static void _on_keyboard_switch_cb(keyboard_mode_t mode, void* user_data)
{
- data_t* data = (data_t*)component->data;
+ component_t* self = (component_t*)user_data;
+ data_t* data = (data_t*)self->data;
+ _input_char_set_alphabet(data->trinary_char_component, mode);
+}
- if (event->id == EVENT_CONFIRM && data->can_confirm) {
+static void _confirm_button_cb(void* user_data)
+{
+ component_t* self = (component_t*)user_data;
+ data_t* data = self->data;
+ if (data->can_confirm) {
if (data->confirm_cb) {
data->confirm_cb(data->string, data->confirm_user_data);
data->confirm_cb = NULL;
}
- return;
}
+}
- // Other gestures deactivated during confirming.
- if (data->longtouch && confirm_gesture_is_active(data->confirm_component)) {
+static void _back(void* user_data)
+{
+ component_t* self = (component_t*)user_data;
+ data_t* data = (data_t*)self->data;
+ if (trinary_input_char_in_progress(data->trinary_char_component)) {
+ _set_alphabet(self, false);
return;
}
-
- switch (event->id) {
- case EVENT_TOGGLE_ALPHANUMERIC:
- _set_alphabet(component, false);
- break;
- case EVENT_BACKWARD:
- if (trinary_input_char_in_progress(data->trinary_char_component)) {
- _set_alphabet(component, false);
- break;
- }
- if (data->string_index == 0) {
- // Back button is cancel.
- if (data->cancel_cb != NULL) {
- data->cancel_cb(data->cancel_user_data);
- }
- return;
+ if (data->string_index == 0) {
+ // Back button is cancel.
+ if (data->cancel_cb != NULL) {
+ data->cancel_cb(data->cancel_user_data);
}
- // Move cursor backward and display preceeding character
- if (data->string_index > 0) {
- data->string_index--;
- data->string[data->string_index] = '\0';
- data->show_last_character = false;
- UG_S16 string_width = _constant_string_width(component);
- if (data->target_x < STRING_POS_X_START &&
- data->target_x + string_width < SCROLL_LEFT_PAD) {
- data->target_x = SCROLL_RIGHT_LIMIT - string_width;
- // data->target_x += MIN(SCREEN_WIDTH - SCROLL_RIGHT_LIMIT, string_width);
- }
- }
- _set_alphabet(component, false);
- break;
- default:
- break;
+ return;
}
-}
-
-static void _confirm_button_cb(component_t* confirm_button)
-{
- component_t* component = confirm_button->parent;
- data_t* data = (data_t*)component->data;
- if (data->can_confirm) {
- if (data->confirm_cb) {
- data->confirm_cb(data->string, data->confirm_user_data);
- data->confirm_cb = NULL;
+ // Move cursor backward and display preceeding character
+ if (data->string_index > 0) {
+ data->string_index--;
+ data->string[data->string_index] = '\0';
+ data->show_last_character = false;
+ UG_S16 string_width = _constant_string_width(self);
+ if (data->target_x < STRING_POS_X_START &&
+ data->target_x + string_width < SCROLL_LEFT_PAD) {
+ data->target_x = SCROLL_RIGHT_LIMIT - string_width;
+ // data->target_x += MIN(SCREEN_WIDTH - SCROLL_RIGHT_LIMIT, string_width);
}
}
+ _set_alphabet(self, false);
}
-static void _cancel(component_t* cancel_button)
+static void _cancel(void* user_data)
{
- component_t* component = cancel_button->parent;
- data_t* data = (data_t*)component->data;
+ component_t* self = (component_t*)user_data;
+ data_t* data = (data_t*)self->data;
if (data->cancel_cb != NULL) {
data->cancel_cb(data->cancel_user_data);
}
@@ -431,16 +421,14 @@ static void _letter_chosen(component_t* trinary_char, char chosen)
}
if (data->string_index + 1 >= INPUT_STRING_MAX_SIZE) {
- event_t e;
- e.id = EVENT_CONFIRM;
- emit_event(&e);
+ _confirm_button_cb(trinary_input_string);
}
}
static const component_functions_t component_functions = {
.cleanup = _cleanup,
.render = _render,
- .on_event = _on_event,
+ .on_event = NULL,
};
/********************************** Create Instance **********************************/
@@ -490,23 +478,29 @@ component_t* trinary_input_string_create(
component->position.left = 0;
if (cancel_cb != NULL) {
- data->cancel_component = icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel);
+ data->cancel_component =
+ icon_button_create(top_slider, ICON_BUTTON_CROSS, _cancel, component);
ui_util_add_sub_component(component, data->cancel_component);
}
- data->left_arrow_component = left_arrow_create(top_slider, component);
+ data->left_arrow_component = left_arrow_create(top_slider, component, _back, component);
ui_util_add_sub_component(component, data->left_arrow_component);
if (params->longtouch) {
- data->confirm_component = confirm_gesture_create();
+ data->confirm_component = confirm_gesture_create(_confirm_button_cb, component);
} else {
data->confirm_component =
- icon_button_create(top_slider, ICON_BUTTON_CHECK, _confirm_button_cb);
+ icon_button_create(top_slider, ICON_BUTTON_CHECK, _confirm_button_cb, component);
}
ui_util_add_sub_component(component, data->confirm_component);
if (params->wordlist == NULL && !params->number_input) {
data->keyboard_switch_component = keyboard_switch_create(
- top_slider, params->special_chars, params->default_to_digits, component);
+ top_slider,
+ params->special_chars,
+ params->default_to_digits,
+ component,
+ _on_keyboard_switch_cb,
+ component);
ui_util_add_sub_component(component, data->keyboard_switch_component);
}
diff --git a/src/ui/event.h b/src/ui/event.h
index c3afab6..1ea6f94 100644
--- a/src/ui/event.h
+++ b/src/ui/event.h
@@ -18,11 +18,6 @@
#include <stdint.h>
enum {
- EVENT_CONFIRM,
- EVENT_FORWARD,
- EVENT_BACKWARD,
- EVENT_TOGGLE_ALPHANUMERIC,
- EVENT_UPDATE_ALPHANUMERIC,
EVENT_BOTTOM_SLIDE,
EVENT_TOP_SLIDE,
EVENT_BOTTOM_SLIDE_RELEASED,
diff --git a/test/unit-test/test_ui_component_gestures.c b/test/unit-test/test_ui_component_gestures.c
index c919365..4b2c13b 100644
--- a/test/unit-test/test_ui_component_gestures.c
+++ b/test/unit-test/test_ui_component_gestures.c
@@ -29,14 +29,10 @@
#include "mock_gestures.h"
#include "mock_qtouch.h"
-static uint8_t _correct_event;
-static bool _correct_event_seen = false;
-
-static void test_on_event(const event_t* _event, component_t* _component)
+static void _cb(void* user_data)
{
- if (_event->id == _correct_event) {
- _correct_event_seen = true;
- }
+ bool* flag = (bool*)user_data;
+ *flag = true;
}
static void test_ui_right_arrow_tap(void** state)
@@ -44,26 +40,24 @@ static void test_ui_right_arrow_tap(void** state)
const component_functions_t modified_functions = {
.cleanup = ui_util_component_cleanup,
.render = ui_util_component_render_subcomponents,
- .on_event = test_on_event};
+ .on_event = NULL};
component_t* mock_component = fake_component_create();
mock_component->f = &modified_functions;
ui_screen_stack_push(mock_component);
- component_t* right_arrow = right_arrow_create(top_slider, mock_component);
+ bool flag = false;
+ component_t* right_arrow = right_arrow_create(top_slider, mock_component, _cb, &flag);
assert_non_null(right_arrow);
ui_util_add_sub_component(mock_component, right_arrow);
- _correct_event_seen = false;
- _correct_event = EVENT_FORWARD;
-
mock_gestures_touch_init();
for (int i = 0; i < 11; i++) {
mock_gestures_touch(top_slider, right_arrow->position.left);
}
mock_gestures_touch_release();
- assert_true(_correct_event_seen);
+ assert_true(flag);
mock_component->f->cleanup(mock_component);
}
@@ -73,26 +67,24 @@ static void test_ui_left_arrow_tap(void** state)
const component_functions_t modified_functions = {
.cleanup = ui_util_component_cleanup,
.render = ui_util_component_render_subcomponents,
- .on_event = test_on_event};
+ .on_event = NULL};
component_t* mock_component = fake_component_create();
mock_component->f = &modified_functions;
ui_screen_stack_push(mock_component);
- component_t* left_arrow = left_arrow_create(top_slider, mock_component);
+ bool flag = false;
+ component_t* left_arrow = left_arrow_create(top_slider, mock_component, _cb, &flag);
assert_non_null(left_arrow);
ui_util_add_sub_component(mock_component, left_arrow);
- _correct_event_seen = false;
- _correct_event = EVENT_BACKWARD;
-
mock_gestures_touch_init();
for (int i = 0; i < 11; i++) {
mock_gestures_touch(top_slider, 0);
}
mock_gestures_touch_release();
- assert_true(_correct_event_seen);
+ assert_true(flag);
mock_component->f->cleanup(mock_component);
}
diff --git a/test/unit-test/test_ui_components.c b/test/unit-test/test_ui_components.c
index 17693cf..1cb9a1f 100644
--- a/test/unit-test/test_ui_components.c
+++ b/test/unit-test/test_ui_components.c
@@ -33,6 +33,17 @@
#include "fake_component.h"
#include "mock_qtouch.h"
+static void _cb(void* user_data)
+{
+ (void)user_data;
+}
+
+static void _ks_cb(keyboard_mode_t mode, void* user_data)
+{
+ (void)mode;
+ (void)user_data;
+}
+
static void assert_ui_component_functions(component_t* component)
{
assert_non_null(component->f->render);
@@ -55,7 +66,7 @@ static void test_ui_components_right_arrow(void** state)
{
component_t* mock_component = fake_component_create();
- component_t* right_arrow = right_arrow_create(top_slider, mock_component);
+ component_t* right_arrow = right_arrow_create(top_slider, mock_component, _cb, NULL);
assert_non_null(right_arrow);
assert_ui_component_functions(right_arrow);
right_arrow->f->cleanup(right_arrow);
@@ -67,7 +78,7 @@ static void test_ui_components_left_arrow(void** state)
{
component_t* mock_component = fake_component_create();
- component_t* left_arrow = left_arrow_create(top_slider, mock_component);
+ component_t* left_arrow = left_arrow_create(top_slider, mock_component, _cb, NULL);
assert_non_null(left_arrow);
assert_ui_component_functions(left_arrow);
left_arrow->f->cleanup(left_arrow);
@@ -130,7 +141,8 @@ static void test_ui_components_keyboard_switch(void** state)
{
component_t* mock_component = fake_component_create();
- component_t* keyboard_switch = keyboard_switch_create(top_slider, true, false, mock_component);
+ component_t* keyboard_switch =
+ keyboard_switch_create(top_slider, true, false, mock_component, _ks_cb, NULL);
assert_non_null(keyboard_switch);
assert_ui_component_functions(keyboard_switch);
keyboard_switch->f->cleanup(keyboard_switch);
Why this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.