Input, Gui: total events complementarity on all levels (#681)
* Cli: add missing const in cli_write. * Gui, Input: bit fields based key complementarity on all levels, key and type names API. * Gui: minor cleanup of bit filed usage
This commit is contained in:
		
							parent
							
								
									6f7bcdf9a7
								
							
						
					
					
						commit
						433025b5c7
					
				| @ -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); |     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); |     return furi_hal_vcp_tx(buffer, size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ bool cli_cmd_interrupt_received(Cli* cli); | |||||||
|  * @param size - size of buffer in bytes |  * @param size - size of buffer in bytes | ||||||
|  * @return bytes written |  * @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
 | /* Read character
 | ||||||
|  * @param cli - Cli instance |  * @param cli - Cli instance | ||||||
|  | |||||||
| @ -22,42 +22,6 @@ typedef struct { | |||||||
|     EventType type; |     EventType type; | ||||||
| } KeypadTestEvent; | } 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) { | static void keypad_test_reset_state(KeypadTestState* state) { | ||||||
|     state->left = 0; |     state->left = 0; | ||||||
|     state->right = 0; |     state->right = 0; | ||||||
| @ -139,8 +103,8 @@ int32_t keypad_test_app(void* p) { | |||||||
|                 FURI_LOG_I( |                 FURI_LOG_I( | ||||||
|                     "KeypadTest", |                     "KeypadTest", | ||||||
|                     "key: %s type: %s", |                     "key: %s type: %s", | ||||||
|                     keypad_test_get_key_name(event.input.key), |                     input_get_key_name(event.input.key), | ||||||
|                     keypad_test_get_type_name(event.input.type)); |                     input_get_type_name(event.input.type)); | ||||||
| 
 | 
 | ||||||
|                 if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { |                 if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { | ||||||
|                     release_mutex(&state_mutex, state); |                     release_mutex(&state_mutex, state); | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ struct ViewHolder { | |||||||
|     BackCallback back_callback; |     BackCallback back_callback; | ||||||
|     void* back_context; |     void* back_context; | ||||||
| 
 | 
 | ||||||
|     uint8_t ongoing_input_events_count; |     uint8_t ongoing_input; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void view_holder_draw_callback(Canvas* canvas, void* context); | 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) { | 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); |     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) { | static void view_holder_input_callback(InputEvent* event, void* context) { | ||||||
|     ViewHolder* view_holder = context; |     ViewHolder* view_holder = context; | ||||||
| 
 | 
 | ||||||
|     if(event->type == InputTypeRelease && view_holder->ongoing_input_events_count > 0) { |     uint8_t key_bit = (1 << event->key); | ||||||
|         view_holder->ongoing_input_events_count--; |     if(event->type == InputTypePress) { | ||||||
|     } else if(event->type == InputTypePress) { |         view_holder->ongoing_input |= key_bit; | ||||||
|         view_holder->ongoing_input_events_count++; |     } else if(event->type == InputTypeRelease) { | ||||||
|     } else if(view_holder->ongoing_input_events_count == 0) { |         view_holder->ongoing_input &= ~key_bit; | ||||||
|         FURI_LOG_E("ViewHolder", "non-complementary input, discarding"); |     } 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; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -216,20 +216,46 @@ void gui_input(Gui* gui, InputEvent* input_event) { | |||||||
|     furi_assert(gui); |     furi_assert(gui); | ||||||
|     furi_assert(input_event); |     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); |     gui_lock(gui); | ||||||
| 
 | 
 | ||||||
|     ViewPort* view_port; |     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); | ||||||
| 
 |  | ||||||
|     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[GuiLayerMain]); | ||||||
|     if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]); |     if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]); | ||||||
| 
 | 
 | ||||||
|     if(view_port) { |     if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) { | ||||||
|         if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { |         gui->ongoing_input_view_port = view_port; | ||||||
|             gui_rotate_buttons(input_event); |     } | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         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); |     gui_unlock(gui); | ||||||
| @ -251,7 +277,7 @@ void gui_cli_screen_stream_callback(uint8_t* data, size_t size, void* context) { | |||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 | 
 | ||||||
|     Gui* gui = 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, magic, sizeof(magic)); | ||||||
|     cli_write(gui->cli, data, size); |     cli_write(gui->cli, data, size); | ||||||
| } | } | ||||||
|  | |||||||
| @ -35,14 +35,19 @@ struct Gui { | |||||||
|     // Thread and lock
 |     // Thread and lock
 | ||||||
|     osThreadId_t thread; |     osThreadId_t thread; | ||||||
|     osMutexId_t mutex; |     osMutexId_t mutex; | ||||||
|  | 
 | ||||||
|     // Layers and Canvas
 |     // Layers and Canvas
 | ||||||
|     ViewPortArray_t layers[GuiLayerMAX]; |     ViewPortArray_t layers[GuiLayerMAX]; | ||||||
|     Canvas* canvas; |     Canvas* canvas; | ||||||
|     GuiCanvasCommitCallback canvas_callback; |     GuiCanvasCommitCallback canvas_callback; | ||||||
|     void* canvas_callback_context; |     void* canvas_callback_context; | ||||||
|  | 
 | ||||||
|     // Input
 |     // Input
 | ||||||
|     osMessageQueueId_t input_queue; |     osMessageQueueId_t input_queue; | ||||||
|     PubSub* input_events; |     PubSub* input_events; | ||||||
|  |     uint8_t ongoing_input; | ||||||
|  |     ViewPort* ongoing_input_view_port; | ||||||
|  | 
 | ||||||
|     // Cli
 |     // Cli
 | ||||||
|     Cli* cli; |     Cli* cli; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -93,13 +93,14 @@ void view_dispatcher_run(ViewDispatcher* view_dispatcher) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Wait till all input events delivered
 |     // 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); |         osMessageQueueGet(view_dispatcher->queue, &message, NULL, osWaitForever); | ||||||
|         if(message.type == ViewDispatcherMessageTypeInput) { |         if(message.type == ViewDispatcherMessageTypeInput) { | ||||||
|  |             uint8_t key_bit = (1 << message.input.key); | ||||||
|             if(message.input.type == InputTypePress) { |             if(message.input.type == InputTypePress) { | ||||||
|                 view_dispatcher->ongoing_input_events_count++; |                 view_dispatcher->ongoing_input |= key_bit; | ||||||
|             } else if(message.input.type == InputTypeRelease) { |             } 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 { |     } else { | ||||||
|         View** view_pp = ViewDict_get(view_dispatcher->views, view_id); |         View** view_pp = ViewDict_get(view_dispatcher->views, view_id); | ||||||
|         furi_check(view_pp != NULL); |         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; |             view_dispatcher->delayed_next_view = *view_pp; | ||||||
|         } else { |         } else { | ||||||
|             view_dispatcher->delayed_next_view = NULL; |             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) { | void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* event) { | ||||||
|     // Ongoing input events counting
 |     // Check input complementarity
 | ||||||
|     if(event->type == InputTypeRelease && view_dispatcher->ongoing_input_events_count > 0) { |     uint8_t key_bit = (1 << event->key); | ||||||
|         view_dispatcher->ongoing_input_events_count--; |     if(event->type == InputTypePress) { | ||||||
|     } else if(event->type == InputTypePress) { |         view_dispatcher->ongoing_input |= key_bit; | ||||||
|         view_dispatcher->ongoing_input_events_count++; |     } else if(event->type == InputTypeRelease) { | ||||||
|     } else if(view_dispatcher->ongoing_input_events_count == 0) { |         view_dispatcher->ongoing_input &= ~key_bit; | ||||||
|         FURI_LOG_E("ViewDispatcher", "non-complementary input, discarding"); |     } 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; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -251,7 +257,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Delayed view switch
 |     // 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_set_current_view(view_dispatcher, view_dispatcher->delayed_next_view); | ||||||
|         view_dispatcher->delayed_next_view = NULL; |         view_dispatcher->delayed_next_view = NULL; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ struct ViewDispatcher { | |||||||
|     View* current_view; |     View* current_view; | ||||||
| 
 | 
 | ||||||
|     View* delayed_next_view; |     View* delayed_next_view; | ||||||
|     uint8_t ongoing_input_events_count; |     uint8_t ongoing_input; | ||||||
| 
 | 
 | ||||||
|     ViewDispatcherCustomEventCallback custom_event_callback; |     ViewDispatcherCustomEventCallback custom_event_callback; | ||||||
|     ViewDispatcherNavigationEventCallback navigation_event_callback; |     ViewDispatcherNavigationEventCallback navigation_event_callback; | ||||||
|  | |||||||
| @ -91,6 +91,31 @@ void input_cli_send(Cli* cli, string_t args, void* context) { | |||||||
|     notify_pubsub(&input->event_pubsub, &event); |     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() { | int32_t input_srv() { | ||||||
|     input = furi_alloc(sizeof(Input)); |     input = furi_alloc(sizeof(Input)); | ||||||
|     input->thread = osThreadGetId(); |     input->thread = osThreadGetId(); | ||||||
| @ -103,10 +128,9 @@ int32_t input_srv() { | |||||||
|             input->cli, "input_send", CliCommandFlagParallelSafe, input_cli_send, input); |             input->cli, "input_send", CliCommandFlagParallelSafe, input_cli_send, input); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const size_t pin_count = input_pins_count; |     input->pin_states = furi_alloc(input_pins_count * sizeof(InputPinState)); | ||||||
|     input->pin_states = furi_alloc(pin_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}; |         GpioPin gpio = {(GPIO_TypeDef*)input_pins[i].port, (uint16_t)input_pins[i].pin}; | ||||||
|         hal_gpio_add_int_callback(&gpio, input_isr, NULL); |         hal_gpio_add_int_callback(&gpio, input_isr, NULL); | ||||||
|         input->pin_states[i].pin = &input_pins[i]; |         input->pin_states[i].pin = &input_pins[i]; | ||||||
| @ -119,7 +143,7 @@ int32_t input_srv() { | |||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         bool is_changing = false; |         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]); |             bool state = GPIO_Read(input->pin_states[i]); | ||||||
|             if(input->pin_states[i].debounce > 0 && |             if(input->pin_states[i].debounce > 0 && | ||||||
|                input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { |                input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { | ||||||
|  | |||||||
| @ -18,3 +18,15 @@ typedef struct { | |||||||
|     InputKey key; |     InputKey key; | ||||||
|     InputType type; |     InputType type; | ||||||
| } InputEvent; | } 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); | ||||||
|  | |||||||
| @ -3,15 +3,15 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| const InputPin input_pins[] = { | const InputPin input_pins[] = { | ||||||
|     {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .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}, |     {.port = BUTTON_DOWN_GPIO_Port, .pin = BUTTON_DOWN_Pin, .key = InputKeyDown, .inverted = true, .name="Down"}, | ||||||
|     {.port = BUTTON_RIGHT_GPIO_Port, |     {.port = BUTTON_RIGHT_GPIO_Port, | ||||||
|      .pin = BUTTON_RIGHT_Pin, |      .pin = BUTTON_RIGHT_Pin, | ||||||
|      .key = InputKeyRight, |      .key = InputKeyRight, | ||||||
|      .inverted = true}, |      .inverted = true, .name="Right"}, | ||||||
|     {.port = BUTTON_LEFT_GPIO_Port, .pin = BUTTON_LEFT_Pin, .key = InputKeyLeft, .inverted = true}, |     {.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}, |     {.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}, |     {.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); | const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); | ||||||
|  | |||||||
| @ -54,6 +54,7 @@ typedef struct { | |||||||
|     const uint16_t pin; |     const uint16_t pin; | ||||||
|     const InputKey key; |     const InputKey key; | ||||||
|     const bool inverted; |     const bool inverted; | ||||||
|  |     const char* name; | ||||||
| } InputPin; | } InputPin; | ||||||
| 
 | 
 | ||||||
| extern const InputPin input_pins[]; | extern const InputPin input_pins[]; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく