diff --git a/applications/cli/cli.c b/applications/cli/cli.c index a90cb3d0..9a3ae259 100644 --- a/applications/cli/cli.c +++ b/applications/cli/cli.c @@ -46,7 +46,7 @@ void cli_stdout_callback(void* _cookie, const char* data, size_t size) { furi_hal_vcp_tx((const uint8_t*)data, size); } -void cli_write(Cli* cli, uint8_t* buffer, size_t size) { +void cli_write(Cli* cli, const uint8_t* buffer, size_t size) { return furi_hal_vcp_tx(buffer, size); } diff --git a/applications/cli/cli.h b/applications/cli/cli.h index c7322fdb..b6f524a1 100644 --- a/applications/cli/cli.h +++ b/applications/cli/cli.h @@ -88,7 +88,7 @@ bool cli_cmd_interrupt_received(Cli* cli); * @param size - size of buffer in bytes * @return bytes written */ -void cli_write(Cli* cli, uint8_t* buffer, size_t size); +void cli_write(Cli* cli, const uint8_t* buffer, size_t size); /* Read character * @param cli - Cli instance diff --git a/applications/debug_tools/keypad_test.c b/applications/debug_tools/keypad_test.c index 1323e95e..e390ccd5 100644 --- a/applications/debug_tools/keypad_test.c +++ b/applications/debug_tools/keypad_test.c @@ -22,42 +22,6 @@ typedef struct { EventType type; } KeypadTestEvent; -static const char* keypad_test_get_key_name(InputKey key) { - switch(key) { - case InputKeyOk: - return "Ok"; - case InputKeyBack: - return "Back"; - case InputKeyLeft: - return "Left"; - case InputKeyRight: - return "Right"; - case InputKeyUp: - return "Up"; - case InputKeyDown: - return "Down"; - default: - return "Unknown"; - } -} - -static const char* keypad_test_get_type_name(InputType type) { - switch(type) { - case InputTypePress: - return "Press"; - case InputTypeRelease: - return "Release"; - case InputTypeShort: - return "Short"; - case InputTypeLong: - return "Long"; - case InputTypeRepeat: - return "Repeat"; - default: - return "Unknown"; - } -} - static void keypad_test_reset_state(KeypadTestState* state) { state->left = 0; state->right = 0; @@ -139,8 +103,8 @@ int32_t keypad_test_app(void* p) { FURI_LOG_I( "KeypadTest", "key: %s type: %s", - keypad_test_get_key_name(event.input.key), - keypad_test_get_type_name(event.input.type)); + input_get_key_name(event.input.key), + input_get_type_name(event.input.type)); if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { release_mutex(&state_mutex, state); diff --git a/applications/dialogs/view_holder.c b/applications/dialogs/view_holder.c index 34e36818..2307bcd8 100644 --- a/applications/dialogs/view_holder.c +++ b/applications/dialogs/view_holder.c @@ -12,7 +12,7 @@ struct ViewHolder { BackCallback back_callback; void* back_context; - uint8_t ongoing_input_events_count; + uint8_t ongoing_input; }; static void view_holder_draw_callback(Canvas* canvas, void* context); @@ -94,7 +94,7 @@ void view_holder_start(ViewHolder* view_holder) { } void view_holder_stop(ViewHolder* view_holder) { - while(view_holder->ongoing_input_events_count > 0) osDelay(1); + while(view_holder->ongoing_input) osDelay(1); view_port_enabled_set(view_holder->view_port, false); } @@ -118,12 +118,17 @@ static void view_holder_draw_callback(Canvas* canvas, void* context) { static void view_holder_input_callback(InputEvent* event, void* context) { ViewHolder* view_holder = context; - if(event->type == InputTypeRelease && view_holder->ongoing_input_events_count > 0) { - view_holder->ongoing_input_events_count--; - } else if(event->type == InputTypePress) { - view_holder->ongoing_input_events_count++; - } else if(view_holder->ongoing_input_events_count == 0) { - FURI_LOG_E("ViewHolder", "non-complementary input, discarding"); + uint8_t key_bit = (1 << event->key); + if(event->type == InputTypePress) { + view_holder->ongoing_input |= key_bit; + } else if(event->type == InputTypeRelease) { + view_holder->ongoing_input &= ~key_bit; + } else if(!(view_holder->ongoing_input & key_bit)) { + FURI_LOG_W( + "ViewHolder", + "non-complementary input, discarding key: %s, type: %s", + input_get_key_name(event->key), + input_get_type_name(event->type)); return; } diff --git a/applications/gui/gui.c b/applications/gui/gui.c index 8ad45499..11c6b5f8 100644 --- a/applications/gui/gui.c +++ b/applications/gui/gui.c @@ -216,20 +216,46 @@ void gui_input(Gui* gui, InputEvent* input_event) { furi_assert(gui); furi_assert(input_event); + // Check input complementarity + uint8_t key_bit = (1 << input_event->key); + if(input_event->type == InputTypeRelease) { + gui->ongoing_input &= ~key_bit; + } else if(input_event->type == InputTypePress) { + gui->ongoing_input |= key_bit; + } else if(!(gui->ongoing_input & key_bit)) { + FURI_LOG_W( + "Gui", + "non-complementary input, discarding key %s type %s", + input_get_key_name(input_event->key), + input_get_type_name(input_event->type)); + return; + } + gui_lock(gui); - ViewPort* view_port; - - view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); + ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerMain]); if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]); - if(view_port) { - if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { - gui_rotate_buttons(input_event); - } + if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) { + gui->ongoing_input_view_port = view_port; + } - view_port_input(view_port, input_event); + if(view_port) { + if(view_port == gui->ongoing_input_view_port) { + if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { + gui_rotate_buttons(input_event); + } + view_port_input(view_port, input_event); + } else { + FURI_LOG_W( + "Gui", + "ViewPort change while key press %x -> %x. Discarding key: %s, type: %s", + gui->ongoing_input_view_port, + view_port, + input_get_key_name(input_event->key), + input_get_type_name(input_event->type)); + } } gui_unlock(gui); @@ -251,7 +277,7 @@ void gui_cli_screen_stream_callback(uint8_t* data, size_t size, void* context) { furi_assert(context); Gui* gui = context; - uint8_t magic[] = {0xF0, 0xE1, 0xD2, 0xC3}; + const uint8_t magic[] = {0xF0, 0xE1, 0xD2, 0xC3}; cli_write(gui->cli, magic, sizeof(magic)); cli_write(gui->cli, data, size); } diff --git a/applications/gui/gui_i.h b/applications/gui/gui_i.h index 441722c7..d67b4599 100644 --- a/applications/gui/gui_i.h +++ b/applications/gui/gui_i.h @@ -35,14 +35,19 @@ struct Gui { // Thread and lock osThreadId_t thread; osMutexId_t mutex; + // Layers and Canvas ViewPortArray_t layers[GuiLayerMAX]; Canvas* canvas; GuiCanvasCommitCallback canvas_callback; void* canvas_callback_context; + // Input osMessageQueueId_t input_queue; PubSub* input_events; + uint8_t ongoing_input; + ViewPort* ongoing_input_view_port; + // Cli Cli* cli; }; diff --git a/applications/gui/view_dispatcher.c b/applications/gui/view_dispatcher.c index d063a38a..ca80dcf0 100644 --- a/applications/gui/view_dispatcher.c +++ b/applications/gui/view_dispatcher.c @@ -93,13 +93,14 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) { } // Wait till all input events delivered - while(view_dispatcher->ongoing_input_events_count > 0) { + while(view_dispatcher->ongoing_input) { osMessageQueueGet(view_dispatcher->queue, &message, NULL, osWaitForever); if(message.type == ViewDispatcherMessageTypeInput) { + uint8_t key_bit = (1 << message.input.key); if(message.input.type == InputTypePress) { - view_dispatcher->ongoing_input_events_count++; + view_dispatcher->ongoing_input |= key_bit; } else if(message.input.type == InputTypeRelease) { - view_dispatcher->ongoing_input_events_count--; + view_dispatcher->ongoing_input &= ~key_bit; } } } @@ -168,7 +169,7 @@ void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t vi } else { View** view_pp = ViewDict_get(view_dispatcher->views, view_id); furi_check(view_pp != NULL); - if(view_dispatcher->ongoing_input_events_count > 0) { + if(view_dispatcher->ongoing_input) { view_dispatcher->delayed_next_view = *view_pp; } else { view_dispatcher->delayed_next_view = NULL; @@ -217,13 +218,18 @@ void view_dispatcher_input_callback(InputEvent* event, void* context) { } void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event) { - // Ongoing input events counting - if(event->type == InputTypeRelease && view_dispatcher->ongoing_input_events_count > 0) { - view_dispatcher->ongoing_input_events_count--; - } else if(event->type == InputTypePress) { - view_dispatcher->ongoing_input_events_count++; - } else if(view_dispatcher->ongoing_input_events_count == 0) { - FURI_LOG_E("ViewDispatcher", "non-complementary input, discarding"); + // Check input complementarity + uint8_t key_bit = (1 << event->key); + if(event->type == InputTypePress) { + view_dispatcher->ongoing_input |= key_bit; + } else if(event->type == InputTypeRelease) { + view_dispatcher->ongoing_input &= ~key_bit; + } else if(!(view_dispatcher->ongoing_input & key_bit)) { + FURI_LOG_W( + "ViewDispatcher", + "non-complementary input, discarding key: %s, type: %s", + input_get_key_name(event->key), + input_get_type_name(event->type)); return; } @@ -251,7 +257,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } // Delayed view switch - if(view_dispatcher->delayed_next_view && view_dispatcher->ongoing_input_events_count == 0) { + if(view_dispatcher->delayed_next_view && !(view_dispatcher->ongoing_input)) { view_dispatcher_set_current_view(view_dispatcher, view_dispatcher->delayed_next_view); view_dispatcher->delayed_next_view = NULL; } diff --git a/applications/gui/view_dispatcher_i.h b/applications/gui/view_dispatcher_i.h index 86ee3257..9d50432c 100644 --- a/applications/gui/view_dispatcher_i.h +++ b/applications/gui/view_dispatcher_i.h @@ -18,7 +18,7 @@ struct ViewDispatcher { View* current_view; View* delayed_next_view; - uint8_t ongoing_input_events_count; + uint8_t ongoing_input; ViewDispatcherCustomEventCallback custom_event_callback; ViewDispatcherNavigationEventCallback navigation_event_callback; diff --git a/applications/input/input.c b/applications/input/input.c index 6426ba20..4dd4df43 100644 --- a/applications/input/input.c +++ b/applications/input/input.c @@ -91,6 +91,31 @@ void input_cli_send(Cli* cli, string_t args, void* context) { notify_pubsub(&input->event_pubsub, &event); } +const char* input_get_key_name(InputKey key) { + for(size_t i = 0; i < input_pins_count; i++) { + if(input_pins[i].key == key) { + return input_pins[i].name; + } + } + return "Unknown"; +} + +const char* input_get_type_name(InputType type) { + switch(type) { + case InputTypePress: + return "Press"; + case InputTypeRelease: + return "Release"; + case InputTypeShort: + return "Short"; + case InputTypeLong: + return "Long"; + case InputTypeRepeat: + return "Repeat"; + } + return "Unknown"; +} + int32_t input_srv() { input = furi_alloc(sizeof(Input)); input->thread = osThreadGetId(); @@ -103,10 +128,9 @@ int32_t input_srv() { input->cli, "input_send", CliCommandFlagParallelSafe, input_cli_send, input); } - const size_t pin_count = input_pins_count; - input->pin_states = furi_alloc(pin_count * sizeof(InputPinState)); + input->pin_states = furi_alloc(input_pins_count * sizeof(InputPinState)); - for(size_t i = 0; i < pin_count; i++) { + for(size_t i = 0; i < input_pins_count; i++) { GpioPin gpio = {(GPIO_TypeDef*)input_pins[i].port, (uint16_t)input_pins[i].pin}; hal_gpio_add_int_callback(&gpio, input_isr, NULL); input->pin_states[i].pin = &input_pins[i]; @@ -119,7 +143,7 @@ int32_t input_srv() { while(1) { bool is_changing = false; - for(size_t i = 0; i < pin_count; i++) { + for(size_t i = 0; i < input_pins_count; i++) { bool state = GPIO_Read(input->pin_states[i]); if(input->pin_states[i].debounce > 0 && input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { diff --git a/applications/input/input.h b/applications/input/input.h index 1ad82555..228eaaf5 100644 --- a/applications/input/input.h +++ b/applications/input/input.h @@ -18,3 +18,15 @@ typedef struct { InputKey key; InputType type; } InputEvent; + +/** Get human readable input key name + * @param key - InputKey + * @return string + */ +const char* input_get_key_name(InputKey key); + +/** Get human readable input type name + * @param type - InputType + * @return string + */ +const char* input_get_type_name(InputType type); diff --git a/firmware/targets/f6/furi-hal/furi-hal-resources.c b/firmware/targets/f6/furi-hal/furi-hal-resources.c index f1d2003f..6d7f8a0b 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-resources.c +++ b/firmware/targets/f6/furi-hal/furi-hal-resources.c @@ -3,15 +3,15 @@ #include const InputPin input_pins[] = { - {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true}, - {.port = BUTTON_DOWN_GPIO_Port, .pin = BUTTON_DOWN_Pin, .key = InputKeyDown, .inverted = true}, + {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true, .name="Up"}, + {.port = BUTTON_DOWN_GPIO_Port, .pin = BUTTON_DOWN_Pin, .key = InputKeyDown, .inverted = true, .name="Down"}, {.port = BUTTON_RIGHT_GPIO_Port, .pin = BUTTON_RIGHT_Pin, .key = InputKeyRight, - .inverted = true}, - {.port = BUTTON_LEFT_GPIO_Port, .pin = BUTTON_LEFT_Pin, .key = InputKeyLeft, .inverted = true}, - {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false}, - {.port = BUTTON_BACK_GPIO_Port, .pin = BUTTON_BACK_Pin, .key = InputKeyBack, .inverted = true}, + .inverted = true, .name="Right"}, + {.port = BUTTON_LEFT_GPIO_Port, .pin = BUTTON_LEFT_Pin, .key = InputKeyLeft, .inverted = true, .name="Left"}, + {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false, .name="Ok"}, + {.port = BUTTON_BACK_GPIO_Port, .pin = BUTTON_BACK_Pin, .key = InputKeyBack, .inverted = true, .name="Back"}, }; const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); diff --git a/firmware/targets/f6/furi-hal/furi-hal-resources.h b/firmware/targets/f6/furi-hal/furi-hal-resources.h index e962a232..bec183ef 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-resources.h +++ b/firmware/targets/f6/furi-hal/furi-hal-resources.h @@ -54,6 +54,7 @@ typedef struct { const uint16_t pin; const InputKey key; const bool inverted; + const char* name; } InputPin; extern const InputPin input_pins[];