فهرست منبع

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>
Michal Suchánek 2 سال پیش
والد
کامیت
780da7d4d5

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
+*~
 *.swp
 *.swo
 *.gdb_history

+ 18 - 20
applications/services/gui/canvas.c

@@ -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;
     }
 }

+ 7 - 1
applications/services/gui/gui.c

@@ -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);
 

+ 1 - 0
applications/services/gui/gui_i.h

@@ -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>

+ 13 - 9
applications/services/gui/view_dispatcher.c

@@ -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);

+ 34 - 14
applications/services/gui/view_port.c

@@ -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() {

+ 5 - 3
applications/services/input/input.c

@@ -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;

+ 9 - 1
applications/services/input/input.h

@@ -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;

+ 21 - 0
applications/services/rpc/rpc_gui.c

@@ -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);

+ 23 - 0
applications/settings/system/system_settings.c

@@ -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",

+ 1 - 0
firmware/targets/furi_hal_include/furi_hal_rtc.h

@@ -29,6 +29,7 @@ typedef enum {
     FuriHalRtcFlagFactoryReset = (1 << 1),
     FuriHalRtcFlagLock = (1 << 2),
     FuriHalRtcFlagC2Update = (1 << 3),
+    FuriHalRtcFlagHandOrient = (1 << 4),
 } FuriHalRtcFlag;
 
 typedef enum {