Upside down / left handed orientation support (#2462)
* Add backup files to .gitignore * Added lefty support in Settings > System > hand Orient: Fixes: #1015 * Left handed mode * Fix lefthanded mode on vertical interfaces * Input: new composite sequence identifier * Gui: move input mapping from Canvas to ViewPort, properly handle input mapping on View switch in ViewDispatcher * Rpc: proper input sequencing and tagging in RpcGui * Rpc: remove magic from RpcGui Co-authored-by: MrDaGree <5050898+MrDaGree@users.noreply.github.com> Co-authored-by: Willy-JL <willy.leslie@icloud.com> Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com> Co-authored-by: Sergey Gavrilov <who.just.the.doctor@gmail.com>
This commit is contained in:
		
							parent
							
								
									4fd043398a
								
							
						
					
					
						commit
						780da7d4d5
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +1,4 @@ | ||||
| *~ | ||||
| *.swp | ||||
| *.swo | ||||
| *.gdb_history | ||||
|  | ||||
| @ -4,6 +4,7 @@ | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <furi_hal_rtc.h> | ||||
| #include <stdint.h> | ||||
| #include <u8g2_glue.h> | ||||
| 
 | ||||
| @ -376,39 +377,36 @@ void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { | ||||
| 
 | ||||
| void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { | ||||
|     furi_assert(canvas); | ||||
|     const u8g2_cb_t* rotate_cb = NULL; | ||||
|     bool need_swap = false; | ||||
|     if(canvas->orientation != orientation) { | ||||
|         switch(orientation) { | ||||
|         case CanvasOrientationHorizontal: | ||||
|             if(canvas->orientation == CanvasOrientationVertical || | ||||
|                canvas->orientation == CanvasOrientationVerticalFlip) { | ||||
|                 FURI_SWAP(canvas->width, canvas->height); | ||||
|             } | ||||
|             u8g2_SetDisplayRotation(&canvas->fb, U8G2_R0); | ||||
|             need_swap = canvas->orientation == CanvasOrientationVertical || | ||||
|                         canvas->orientation == CanvasOrientationVerticalFlip; | ||||
|             rotate_cb = U8G2_R0; | ||||
|             break; | ||||
|         case CanvasOrientationHorizontalFlip: | ||||
|             if(canvas->orientation == CanvasOrientationVertical || | ||||
|                canvas->orientation == CanvasOrientationVerticalFlip) { | ||||
|                 FURI_SWAP(canvas->width, canvas->height); | ||||
|             } | ||||
|             u8g2_SetDisplayRotation(&canvas->fb, U8G2_R2); | ||||
|             need_swap = canvas->orientation == CanvasOrientationVertical || | ||||
|                         canvas->orientation == CanvasOrientationVerticalFlip; | ||||
|             rotate_cb = U8G2_R2; | ||||
|             break; | ||||
|         case CanvasOrientationVertical: | ||||
|             if(canvas->orientation == CanvasOrientationHorizontal || | ||||
|                canvas->orientation == CanvasOrientationHorizontalFlip) { | ||||
|                 FURI_SWAP(canvas->width, canvas->height); | ||||
|             }; | ||||
|             u8g2_SetDisplayRotation(&canvas->fb, U8G2_R3); | ||||
|             need_swap = canvas->orientation == CanvasOrientationHorizontal || | ||||
|                         canvas->orientation == CanvasOrientationHorizontalFlip; | ||||
|             rotate_cb = U8G2_R3; | ||||
|             break; | ||||
|         case CanvasOrientationVerticalFlip: | ||||
|             if(canvas->orientation == CanvasOrientationHorizontal || | ||||
|                canvas->orientation == CanvasOrientationHorizontalFlip) { | ||||
|                 FURI_SWAP(canvas->width, canvas->height); | ||||
|             } | ||||
|             u8g2_SetDisplayRotation(&canvas->fb, U8G2_R1); | ||||
|             need_swap = canvas->orientation == CanvasOrientationHorizontal || | ||||
|                         canvas->orientation == CanvasOrientationHorizontalFlip; | ||||
|             rotate_cb = U8G2_R1; | ||||
|             break; | ||||
|         default: | ||||
|             furi_assert(0); | ||||
|         } | ||||
| 
 | ||||
|         if(need_swap) FURI_SWAP(canvas->width, canvas->height); | ||||
|         u8g2_SetDisplayRotation(&canvas->fb, rotate_cb); | ||||
|         canvas->orientation = orientation; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -50,7 +50,13 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { | ||||
|     uint8_t left_used = 0; | ||||
|     uint8_t right_used = 0; | ||||
|     uint8_t width; | ||||
|     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) { | ||||
|         canvas_set_orientation(gui->canvas, CanvasOrientationHorizontalFlip); | ||||
|     } else { | ||||
|         canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); | ||||
|     } | ||||
| 
 | ||||
|     canvas_frame_set( | ||||
|         gui->canvas, GUI_STATUS_BAR_X, GUI_STATUS_BAR_Y, GUI_DISPLAY_WIDTH, GUI_STATUS_BAR_HEIGHT); | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ | ||||
| #include "gui.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <furi_hal_rtc.h> | ||||
| #include <m-array.h> | ||||
| #include <m-algo.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| @ -320,6 +320,13 @@ void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t | ||||
|         furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk); | ||||
| } | ||||
| 
 | ||||
| static const ViewPortOrientation view_dispatcher_view_port_orientation_table[] = { | ||||
|     [ViewOrientationVertical] = ViewPortOrientationVertical, | ||||
|     [ViewOrientationVerticalFlip] = ViewPortOrientationVerticalFlip, | ||||
|     [ViewOrientationHorizontal] = ViewPortOrientationHorizontal, | ||||
|     [ViewOrientationHorizontalFlip] = ViewPortOrientationHorizontalFlip, | ||||
| }; | ||||
| 
 | ||||
| void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view) { | ||||
|     furi_assert(view_dispatcher); | ||||
|     // Dispatch view exit event
 | ||||
| @ -330,15 +337,12 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie | ||||
|     view_dispatcher->current_view = view; | ||||
|     // Dispatch view enter event
 | ||||
|     if(view_dispatcher->current_view) { | ||||
|         if(view->orientation == ViewOrientationVertical) { | ||||
|             view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationVertical); | ||||
|         } else if(view->orientation == ViewOrientationVerticalFlip) { | ||||
|             view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationVerticalFlip); | ||||
|         } else if(view->orientation == ViewOrientationHorizontal) { | ||||
|             view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationHorizontal); | ||||
|         } else if(view->orientation == ViewOrientationHorizontalFlip) { | ||||
|             view_port_set_orientation( | ||||
|                 view_dispatcher->view_port, ViewPortOrientationHorizontalFlip); | ||||
|         ViewPortOrientation orientation = | ||||
|             view_dispatcher_view_port_orientation_table[view->orientation]; | ||||
|         if(view_port_get_orientation(view_dispatcher->view_port) != orientation) { | ||||
|             view_port_set_orientation(view_dispatcher->view_port, orientation); | ||||
|             // we just rotated input keys, now it's time to sacrifice some input
 | ||||
|             view_dispatcher->ongoing_input = 0; | ||||
|         } | ||||
|         view_enter(view_dispatcher->current_view); | ||||
|         view_port_enabled_set(view_dispatcher->view_port, true); | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| #include "view_port_i.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <furi_hal_rtc.h> | ||||
| 
 | ||||
| #include "gui.h" | ||||
| #include "gui_i.h" | ||||
| @ -48,27 +50,45 @@ static const InputKey view_port_input_mapping[ViewPortOrientationMAX][InputKeyMA | ||||
|      InputKeyBack}, //ViewPortOrientationVerticalFlip
 | ||||
| }; | ||||
| 
 | ||||
| static const InputKey view_port_left_hand_input_mapping[InputKeyMAX] = | ||||
|     {InputKeyDown, InputKeyUp, InputKeyLeft, InputKeyRight, InputKeyOk, InputKeyBack}; | ||||
| 
 | ||||
| static const CanvasOrientation view_port_orientation_mapping[ViewPortOrientationMAX] = { | ||||
|     [ViewPortOrientationHorizontal] = CanvasOrientationHorizontal, | ||||
|     [ViewPortOrientationHorizontalFlip] = CanvasOrientationHorizontalFlip, | ||||
|     [ViewPortOrientationVertical] = CanvasOrientationVertical, | ||||
|     [ViewPortOrientationVerticalFlip] = CanvasOrientationVerticalFlip, | ||||
| }; | ||||
| 
 | ||||
| // Remaps directional pad buttons on Flipper based on ViewPort orientation
 | ||||
| static void view_port_map_input(InputEvent* event, ViewPortOrientation orientation) { | ||||
|     furi_assert(orientation < ViewPortOrientationMAX && event->key < InputKeyMAX); | ||||
| 
 | ||||
|     if(event->sequence_source != INPUT_SEQUENCE_SOURCE_HARDWARE) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if(orientation == ViewPortOrientationHorizontal || | ||||
|        orientation == ViewPortOrientationHorizontalFlip) { | ||||
|         if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) { | ||||
|             event->key = view_port_left_hand_input_mapping[event->key]; | ||||
|         } | ||||
|     } | ||||
|     event->key = view_port_input_mapping[orientation][event->key]; | ||||
| } | ||||
| 
 | ||||
| static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { | ||||
|     switch(view_port->orientation) { | ||||
|     case ViewPortOrientationHorizontalFlip: | ||||
|         canvas_set_orientation(canvas, CanvasOrientationHorizontalFlip); | ||||
|         break; | ||||
|     case ViewPortOrientationVertical: | ||||
|         canvas_set_orientation(canvas, CanvasOrientationVertical); | ||||
|         break; | ||||
|     case ViewPortOrientationVerticalFlip: | ||||
|         canvas_set_orientation(canvas, CanvasOrientationVerticalFlip); | ||||
|         break; | ||||
|     default: | ||||
|         canvas_set_orientation(canvas, CanvasOrientationHorizontal); | ||||
|         break; | ||||
|     }; | ||||
|     CanvasOrientation orientation = view_port_orientation_mapping[view_port->orientation]; | ||||
| 
 | ||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) { | ||||
|         if(orientation == CanvasOrientationHorizontal) { | ||||
|             orientation = CanvasOrientationHorizontalFlip; | ||||
|         } else if(orientation == CanvasOrientationHorizontalFlip) { | ||||
|             orientation = CanvasOrientationHorizontal; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     canvas_set_orientation(canvas, orientation); | ||||
| } | ||||
| 
 | ||||
| ViewPort* view_port_alloc() { | ||||
|  | ||||
| @ -23,7 +23,8 @@ inline static void input_timer_stop(FuriTimer* timer_id) { | ||||
| void input_press_timer_callback(void* arg) { | ||||
|     InputPinState* input_pin = arg; | ||||
|     InputEvent event; | ||||
|     event.sequence = input_pin->counter; | ||||
|     event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE; | ||||
|     event.sequence_counter = input_pin->counter; | ||||
|     event.key = input_pin->pin->key; | ||||
|     input_pin->press_counter++; | ||||
|     if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) { | ||||
| @ -114,16 +115,17 @@ int32_t input_srv(void* p) { | ||||
| 
 | ||||
|                 // Common state info
 | ||||
|                 InputEvent event; | ||||
|                 event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE; | ||||
|                 event.key = input->pin_states[i].pin->key; | ||||
| 
 | ||||
|                 // Short / Long / Repeat timer routine
 | ||||
|                 if(state) { | ||||
|                     input->counter++; | ||||
|                     input->pin_states[i].counter = input->counter; | ||||
|                     event.sequence = input->pin_states[i].counter; | ||||
|                     event.sequence_counter = input->pin_states[i].counter; | ||||
|                     input_timer_start(input->pin_states[i].press_timer, INPUT_PRESS_TICKS); | ||||
|                 } else { | ||||
|                     event.sequence = input->pin_states[i].counter; | ||||
|                     event.sequence_counter = input->pin_states[i].counter; | ||||
|                     input_timer_stop(input->pin_states[i].press_timer); | ||||
|                     if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) { | ||||
|                         event.type = InputTypeShort; | ||||
|  | ||||
| @ -12,6 +12,8 @@ extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| #define RECORD_INPUT_EVENTS "input_events" | ||||
| #define INPUT_SEQUENCE_SOURCE_HARDWARE (0u) | ||||
| #define INPUT_SEQUENCE_SOURCE_SOFTWARE (1u) | ||||
| 
 | ||||
| /** Input Types
 | ||||
|  * Some of them are physical events and some logical | ||||
| @ -27,7 +29,13 @@ typedef enum { | ||||
| 
 | ||||
| /** Input Event, dispatches with FuriPubSub */ | ||||
| typedef struct { | ||||
|     uint32_t sequence; | ||||
|     union { | ||||
|         uint32_t sequence; | ||||
|         struct { | ||||
|             uint8_t sequence_source : 2; | ||||
|             uint32_t sequence_counter : 30; | ||||
|         }; | ||||
|     }; | ||||
|     InputKey key; | ||||
|     InputType type; | ||||
| } InputEvent; | ||||
|  | ||||
| @ -12,6 +12,8 @@ typedef enum { | ||||
| 
 | ||||
| #define RpcGuiWorkerFlagAny (RpcGuiWorkerFlagTransmit | RpcGuiWorkerFlagExit) | ||||
| 
 | ||||
| #define RPC_GUI_INPUT_RESET (0u) | ||||
| 
 | ||||
| typedef struct { | ||||
|     RpcSession* session; | ||||
|     Gui* gui; | ||||
| @ -26,6 +28,9 @@ typedef struct { | ||||
| 
 | ||||
|     bool virtual_display_not_empty; | ||||
|     bool is_streaming; | ||||
| 
 | ||||
|     uint32_t input_key_counter[InputKeyMAX]; | ||||
|     uint32_t input_counter; | ||||
| } RpcGuiSystem; | ||||
| 
 | ||||
| static void | ||||
| @ -194,6 +199,22 @@ static void | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Event sequence shenanigans
 | ||||
|     event.sequence_source = INPUT_SEQUENCE_SOURCE_SOFTWARE; | ||||
|     if(event.type == InputTypePress) { | ||||
|         rpc_gui->input_counter++; | ||||
|         if(rpc_gui->input_counter == RPC_GUI_INPUT_RESET) rpc_gui->input_counter++; | ||||
|         rpc_gui->input_key_counter[event.key] = rpc_gui->input_counter; | ||||
|     } | ||||
|     if(rpc_gui->input_key_counter[event.key] == RPC_GUI_INPUT_RESET) { | ||||
|         FURI_LOG_W(TAG, "Out of sequence input event: key %d, type %d,", event.key, event.type); | ||||
|     } | ||||
|     event.sequence_counter = rpc_gui->input_key_counter[event.key]; | ||||
|     if(event.type == InputTypeRelease) { | ||||
|         rpc_gui->input_key_counter[event.key] = RPC_GUI_INPUT_RESET; | ||||
|     } | ||||
| 
 | ||||
|     // Submit event
 | ||||
|     FuriPubSub* input_events = furi_record_open(RECORD_INPUT_EVENTS); | ||||
|     furi_check(input_events); | ||||
|     furi_pubsub_publish(input_events, &event); | ||||
|  | ||||
| @ -124,6 +124,23 @@ static void date_format_changed(VariableItem* item) { | ||||
|     locale_set_date_format(date_format_value[index]); | ||||
| } | ||||
| 
 | ||||
| const char* const hand_mode[] = { | ||||
|     "Righty", | ||||
|     "Lefty", | ||||
| }; | ||||
| 
 | ||||
| static void hand_orient_changed(VariableItem* item) { | ||||
|     uint8_t index = variable_item_get_current_value_index(item); | ||||
|     variable_item_set_current_value_text(item, hand_mode[index]); | ||||
|     if(index) { | ||||
|         furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); | ||||
|     } else { | ||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); | ||||
|     } | ||||
| 
 | ||||
|     loader_update_menu(); | ||||
| } | ||||
| 
 | ||||
| static uint32_t system_settings_exit(void* context) { | ||||
|     UNUSED(context); | ||||
|     return VIEW_NONE; | ||||
| @ -145,6 +162,12 @@ SystemSettings* system_settings_alloc() { | ||||
|     uint8_t value_index; | ||||
|     app->var_item_list = variable_item_list_alloc(); | ||||
| 
 | ||||
|     item = variable_item_list_add( | ||||
|         app->var_item_list, "Hand Orient", COUNT_OF(hand_mode), hand_orient_changed, app); | ||||
|     value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient) ? 1 : 0; | ||||
|     variable_item_set_current_value_index(item, value_index); | ||||
|     variable_item_set_current_value_text(item, hand_mode[value_index]); | ||||
| 
 | ||||
|     item = variable_item_list_add( | ||||
|         app->var_item_list, | ||||
|         "Units", | ||||
|  | ||||
| @ -29,6 +29,7 @@ typedef enum { | ||||
|     FuriHalRtcFlagFactoryReset = (1 << 1), | ||||
|     FuriHalRtcFlagLock = (1 << 2), | ||||
|     FuriHalRtcFlagC2Update = (1 << 3), | ||||
|     FuriHalRtcFlagHandOrient = (1 << 4), | ||||
| } FuriHalRtcFlag; | ||||
| 
 | ||||
| typedef enum { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Michal Suchánek
						Michal Suchánek