diff --git a/.gitignore b/.gitignore index 542652eb..45ac9113 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*~ *.swp *.swo *.gdb_history diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index d47bba6b..9c29a39f 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -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; } } diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 94bab140..24b48a83 100644 --- a/applications/services/gui/gui.c +++ b/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); diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h index 45061bd5..a5cd8412 100644 --- a/applications/services/gui/gui_i.h +++ b/applications/services/gui/gui_i.h @@ -8,6 +8,7 @@ #include "gui.h" #include +#include #include #include #include diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 04695874..920b3c13 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/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); diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 5760ed57..8c2ff6fe 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -1,6 +1,8 @@ #include "view_port_i.h" #include +#include +#include #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() { diff --git a/applications/services/input/input.c b/applications/services/input/input.c index e1e581c9..8da0a340 100644 --- a/applications/services/input/input.c +++ b/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; diff --git a/applications/services/input/input.h b/applications/services/input/input.h index 062dc0fa..a62e8456 100644 --- a/applications/services/input/input.h +++ b/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; diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index e66553d5..c2af425e 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/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); diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 5eade211..cb74d7a8 100644 --- a/applications/settings/system/system_settings.c +++ b/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", diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index fe095e74..b16b04a6 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/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 {