From 4fa49882e0e4b9588073764e228909130bd847be Mon Sep 17 00:00:00 2001 From: Albert Kharisov Date: Wed, 19 May 2021 12:43:15 +0300 Subject: [PATCH] [FL-1286] Add vertical screen orientation (#472) --- applications/applications.c | 8 ++ applications/applications.mk | 6 ++ applications/examples/vertical_submenu.c | 103 +++++++++++++++++++++++ applications/gui/canvas.c | 18 ++++ applications/gui/canvas.h | 5 ++ applications/gui/canvas_i.h | 11 +++ applications/gui/gui.c | 48 ++++++++++- applications/gui/modules/submenu.c | 2 +- applications/gui/modules/submenu.h | 4 +- applications/gui/view.c | 6 ++ applications/gui/view.h | 13 ++- applications/gui/view_dispatcher.c | 4 + applications/gui/view_i.h | 1 + applications/gui/view_port.c | 10 +++ applications/gui/view_port.h | 12 +++ applications/gui/view_port_i.h | 1 + 16 files changed, 248 insertions(+), 4 deletions(-) create mode 100644 applications/examples/vertical_submenu.c diff --git a/applications/applications.c b/applications/applications.c index 7e4e9285..011e923b 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -1,6 +1,7 @@ #include "applications.h" // Services and apps decalartion +int32_t application_vertical_screen(void* p); int32_t irda_monitor_app(void* p); int32_t flipper_test_app(void* p); int32_t application_blink(void* p); @@ -266,6 +267,13 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { #ifdef APP_IRDA_MONITOR {.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = A_Plugins_14}, #endif + +#ifdef APP_VERTICAL_SCREEN + {.app = application_vertical_screen, + .name = "Vertical Screen", + .stack_size = 1024, + .icon = A_Plugins_14}, +#endif }; const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication); diff --git a/applications/applications.mk b/applications/applications.mk index 3694ab41..d009fcf2 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -43,6 +43,7 @@ APP_EXAMPLE_UART_WRITE = 1 APP_EXAMPLE_INPUT_DUMP = 1 APP_UNIT_TESTS = 1 APP_IRDA_MONITOR = 1 +APP_VERTICAL_SCREEN = 1 endif SRV_DOLPHIN ?= 0 @@ -76,6 +77,11 @@ SRV_GUI = 1 CFLAGS += -DAPP_MENU endif +APP_VERTICAL_SCREEN ?= 0 +ifeq ($(APP_VERTICAL_SCREEN), 1) +CFLAGS += -DAPP_VERTICAL_SCREEN +endif + APP_IRDA_MONITOR ?= 0 ifeq ($(APP_IRDA_MONITOR), 1) CFLAGS += -DAPP_IRDA_MONITOR diff --git a/applications/examples/vertical_submenu.c b/applications/examples/vertical_submenu.c new file mode 100644 index 00000000..94e3710d --- /dev/null +++ b/applications/examples/vertical_submenu.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +static ViewDispatcher* view_dispatcher; +static osMessageQueueId_t event_queue; + +typedef enum { + EventTypeGoAway, + EventTypeGoToMainMenu, + EventTypeSwitchToVertical, + EventTypeSwitchToHorizontal, +} EventType; + +// Nothing dangerous in settings some vars and flags inside callback +static void submenu_callback(void* context, uint32_t index) { + EventType event = EventTypeGoAway; + switch(index) { + case 1: + event = EventTypeSwitchToVertical; + break; + case 2: + event = EventTypeSwitchToHorizontal; + break; + default: + break; + } + + osMessageQueuePut(event_queue, &event, 0, 0); +} + +uint32_t previous_exit_callback(void* context) { + EventType event = EventTypeGoAway; + osMessageQueuePut(event_queue, &event, 0, 0); + return VIEW_IGNORE; +} + +uint32_t previous_callback(void* context) { + EventType event = EventTypeGoToMainMenu; + osMessageQueuePut(event_queue, &event, 0, 0); + return VIEW_IGNORE; +} + +int32_t application_vertical_screen(void* p) { + event_queue = osMessageQueueNew(8, sizeof(EventType), NULL); + + view_dispatcher = view_dispatcher_alloc(); + Gui* gui = furi_record_open("gui"); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + + Submenu* submenu = submenu_alloc(); + View* submenu_view = submenu_get_view(submenu); + view_set_previous_callback(submenu_view, previous_exit_callback); + view_set_orientation(submenu_view, ViewOrientationVertical); + submenu_add_item(submenu, "VerSubm", 1, submenu_callback, view_dispatcher); + submenu_add_item(submenu, "HorSubm", 2, submenu_callback, view_dispatcher); + view_dispatcher_add_view(view_dispatcher, 1, submenu_view); + + Submenu* submenu_vertical = submenu_alloc(); + View* submenu_vertical_view = submenu_get_view(submenu_vertical); + view_set_previous_callback(submenu_vertical_view, previous_callback); + view_set_orientation(submenu_vertical_view, ViewOrientationVertical); + submenu_add_item(submenu_vertical, "Vert1", 1, NULL, view_dispatcher); + submenu_add_item(submenu_vertical, "Vert2", 2, NULL, view_dispatcher); + view_dispatcher_add_view(view_dispatcher, 2, submenu_vertical_view); + + Submenu* submenu_horizontal = submenu_alloc(); + View* submenu_horizontal_view = submenu_get_view(submenu_horizontal); + view_set_previous_callback(submenu_horizontal_view, previous_callback); + view_set_orientation(submenu_horizontal_view, ViewOrientationHorizontal); + submenu_add_item(submenu_horizontal, "Horiz1", 1, NULL, view_dispatcher); + submenu_add_item(submenu_horizontal, "Horiz2", 2, NULL, view_dispatcher); + view_dispatcher_add_view(view_dispatcher, 3, submenu_horizontal_view); + + view_dispatcher_switch_to_view(view_dispatcher, 1); + + while(1) { + EventType event; + furi_check(osMessageQueueGet(event_queue, &event, NULL, osWaitForever) == osOK); + if(event == EventTypeGoAway) { + break; + } else if(event == EventTypeGoToMainMenu) { + view_dispatcher_switch_to_view(view_dispatcher, 1); + } else if(event == EventTypeSwitchToVertical) { + view_dispatcher_switch_to_view(view_dispatcher, 2); + } else if(event == EventTypeSwitchToHorizontal) { + view_dispatcher_switch_to_view(view_dispatcher, 3); + } + } + + view_dispatcher_remove_view(view_dispatcher, 1); + view_dispatcher_remove_view(view_dispatcher, 2); + view_dispatcher_remove_view(view_dispatcher, 3); + submenu_free(submenu); + submenu_free(submenu_vertical); + submenu_free(submenu_horizontal); + view_dispatcher_free(view_dispatcher); + osMessageQueueDelete(event_queue); + furi_record_close("gui"); + + return 0; +} diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c index 843f64be..f2d52e66 100644 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -12,6 +12,7 @@ Canvas* canvas_init() { api_hal_power_insomnia_enter(); + canvas->orientation = CanvasOrientationHorizontal; u8g2_Setup_st7565_erc12864_alt_f( &canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); @@ -254,3 +255,20 @@ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) { void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0); } + +void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { + furi_assert(canvas); + if(canvas->orientation != orientation) { + canvas->orientation = orientation; + if(canvas->orientation == CanvasOrientationHorizontal) + u8g2_SetDisplayRotation(&canvas->fb, U8G2_R0); + else if(canvas->orientation == CanvasOrientationVertical) + u8g2_SetDisplayRotation(&canvas->fb, U8G2_R3); + else + furi_assert(0); + } +} + +CanvasOrientation canvas_get_orientation(const Canvas* canvas) { + return canvas->orientation; +} diff --git a/applications/gui/canvas.h b/applications/gui/canvas.h index 0ed6be9f..e5674b0e 100644 --- a/applications/gui/canvas.h +++ b/applications/gui/canvas.h @@ -28,6 +28,11 @@ typedef enum { AlignCenter, } Align; +typedef enum { + CanvasOrientationHorizontal, + CanvasOrientationVertical, +} CanvasOrientation; + typedef struct Canvas Canvas; /* diff --git a/applications/gui/canvas_i.h b/applications/gui/canvas_i.h index f1b7d027..e6cc6345 100644 --- a/applications/gui/canvas_i.h +++ b/applications/gui/canvas_i.h @@ -5,6 +5,7 @@ struct Canvas { u8g2_t fb; + CanvasOrientation orientation; uint8_t offset_x; uint8_t offset_y; uint8_t width; @@ -52,3 +53,13 @@ void canvas_frame_set( uint8_t offset_y, uint8_t width, uint8_t height); + +/* + * Set canvas orientation + */ +void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation); + +/* + * Get canvas orientation + */ +CanvasOrientation canvas_get_orientation(const Canvas* canvas); diff --git a/applications/gui/gui.c b/applications/gui/gui.c index 6ef701b9..3c1af74d 100644 --- a/applications/gui/gui.c +++ b/applications/gui/gui.c @@ -1,5 +1,40 @@ #include "gui_i.h" +static void gui_rotate_buttons(InputEvent* event) { + switch(event->key) { + case InputKeyUp: + event->key = InputKeyRight; + break; + case InputKeyDown: + event->key = InputKeyLeft; + break; + case InputKeyRight: + event->key = InputKeyDown; + break; + case InputKeyLeft: + event->key = InputKeyUp; + break; + default: + break; + } +} + +static void gui_setup_fs_orientation(const ViewPort* view_port, Canvas* canvas) { + ViewPortOrientation view_port_orientation = view_port_get_orientation(view_port); + CanvasOrientation canvas_orientation = canvas_get_orientation(canvas); + if(view_port_orientation == ViewPortOrientationHorizontal) { + canvas_frame_set(canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); + if(canvas_orientation != CanvasOrientationHorizontal) { + canvas_set_orientation(canvas, CanvasOrientationHorizontal); + } + } else if(view_port_orientation == ViewPortOrientationVertical) { + canvas_frame_set(canvas, 0, 0, GUI_DISPLAY_HEIGHT, GUI_DISPLAY_WIDTH); + if(canvas_orientation != CanvasOrientationVertical) { + canvas_set_orientation(canvas, CanvasOrientationVertical); + } + } +} + ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { // Iterating backward ViewPortArray_it_t it; @@ -29,10 +64,11 @@ void gui_input_events_callback(const void* value, void* ctx) { osThreadFlagsSet(gui->thread, GUI_THREAD_FLAG_INPUT); } +// Only Fullscreen supports vertical display for now bool gui_redraw_fs(Gui* gui) { - canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); if(view_port) { + gui_setup_fs_orientation(view_port, gui->canvas); view_port_draw(view_port, gui->canvas); return true; } else { @@ -46,6 +82,7 @@ void gui_redraw_status_bar(Gui* gui) { uint8_t x_used = 0; uint8_t width; ViewPort* view_port; + 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); canvas_draw_icon_name(gui->canvas, 0, 0, I_Background_128x11); @@ -130,6 +167,7 @@ void gui_redraw_status_bar(Gui* gui) { } bool gui_redraw_normal(Gui* gui) { + canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); canvas_frame_set(gui->canvas, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT); ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerMain]); if(view_port) { @@ -140,6 +178,7 @@ bool gui_redraw_normal(Gui* gui) { } bool gui_redraw_none(Gui* gui) { + canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); canvas_frame_set(gui->canvas, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT); ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerNone]); if(view_port) { @@ -186,6 +225,10 @@ void gui_input(Gui* gui, InputEvent* input_event) { 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); + } + view_port_input(view_port, input_event); } @@ -228,6 +271,9 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) { furi_assert(gui); furi_assert(view_port); furi_check(layer < GuiLayerMAX); + // Only fullscreen supports Vertical orientation for now + furi_assert( + (layer == GuiLayerFullscreen) || (view_port->orientation != ViewPortOrientationVertical)); gui_lock(gui); // Verify that view port is not yet added diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c index 79738534..c2623dc7 100644 --- a/applications/gui/modules/submenu.c +++ b/applications/gui/modules/submenu.c @@ -217,4 +217,4 @@ void submenu_process_ok(Submenu* submenu) { if(item && item->callback) { item->callback(item->callback_context, item->index); } -} \ No newline at end of file +} diff --git a/applications/gui/modules/submenu.h b/applications/gui/modules/submenu.h index 6528ff4f..cf963731 100644 --- a/applications/gui/modules/submenu.h +++ b/applications/gui/modules/submenu.h @@ -46,6 +46,8 @@ SubmenuItem* submenu_add_item( */ void submenu_clean(Submenu* submenu); +Submenu* submenu_vertical_alloc(); + #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/gui/view.c b/applications/gui/view.c index 8987ecfd..3c50c7ec 100644 --- a/applications/gui/view.c +++ b/applications/gui/view.c @@ -2,6 +2,7 @@ View* view_alloc() { View* view = furi_alloc(sizeof(View)); + view->orientation = ViewOrientationHorizontal; return view; } @@ -59,6 +60,11 @@ void view_set_context(View* view, void* context) { view->context = context; } +void view_set_orientation(View* view, ViewOrientation orientation) { + furi_assert(view); + view->orientation = orientation; +} + void view_allocate_model(View* view, ViewModelType type, size_t size) { furi_assert(view); furi_assert(size > 0); diff --git a/applications/gui/view.h b/applications/gui/view.h index 489d4cc1..92bca5cd 100644 --- a/applications/gui/view.h +++ b/applications/gui/view.h @@ -20,6 +20,11 @@ extern "C" { */ #define VIEW_DESTROY 0xFFFFFFFA +typedef enum { + ViewOrientationHorizontal, + ViewOrientationVertical, +} ViewOrientation; + /* View, anonymous type */ typedef struct View View; @@ -137,6 +142,12 @@ void view_set_update_callback_context(View* view, void* context); */ void view_set_context(View* view, void* context); +/* Set View Orientation + * @param view, pointer to View + * @param orientation, either vertical or horizontal + */ +void view_set_orientation(View* view, ViewOrientation orientation); + /* Allocate view model. * @param view, pointer to View * @param type, View Model Type @@ -186,4 +197,4 @@ void view_commit_model(View* view, bool update); bool update = ({ bool __fn__ function_body __fn__; })(p); \ view_commit_model(view, update); \ } -#endif \ No newline at end of file +#endif diff --git a/applications/gui/view_dispatcher.c b/applications/gui/view_dispatcher.c index 5f6fbdb0..8a501690 100644 --- a/applications/gui/view_dispatcher.c +++ b/applications/gui/view_dispatcher.c @@ -151,6 +151,10 @@ 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 == ViewOrientationHorizontal) + view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationHorizontal); view_enter(view_dispatcher->current_view); view_port_enabled_set(view_dispatcher->view_port, true); view_port_update(view_dispatcher->view_port); diff --git a/applications/gui/view_i.h b/applications/gui/view_i.h index 34bf3815..b36f3618 100644 --- a/applications/gui/view_i.h +++ b/applications/gui/view_i.h @@ -17,6 +17,7 @@ struct View { ViewNavigationCallback next_callback; ViewCallback enter_callback; ViewCallback exit_callback; + ViewOrientation orientation; ViewUpdateCallback update_callback; void* update_callback_context; diff --git a/applications/gui/view_port.c b/applications/gui/view_port.c index 094504a4..301c85db 100644 --- a/applications/gui/view_port.c +++ b/applications/gui/view_port.c @@ -9,6 +9,7 @@ ViewPort* view_port_alloc(ViewPortDrawCallback callback, void* callback_context) { ViewPort* view_port = furi_alloc(sizeof(ViewPort)); + view_port->orientation = ViewPortOrientationHorizontal; view_port->is_enabled = true; return view_port; } @@ -96,3 +97,12 @@ void view_port_input(ViewPort* view_port, InputEvent* event) { view_port->input_callback(event, view_port->input_callback_context); } } + +void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { + furi_assert(view_port); + view_port->orientation = orientation; +} + +ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) { + return view_port->orientation; +} diff --git a/applications/gui/view_port.h b/applications/gui/view_port.h index 2cb2ad52..6d00563b 100644 --- a/applications/gui/view_port.h +++ b/applications/gui/view_port.h @@ -9,6 +9,11 @@ extern "C" { typedef struct ViewPort ViewPort; +typedef enum { + ViewPortOrientationHorizontal, + ViewPortOrientationVertical, +} ViewPortOrientation; + /* * ViewPort Draw callback * @warning called from GUI thread @@ -75,6 +80,13 @@ void view_port_input_callback_set( */ void view_port_update(ViewPort* view_port); +/* + * Set ViewPort orientation. + * @param orientation, display orientation, horizontal or vertical. + */ +void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation); +ViewPortOrientation view_port_get_orientation(const ViewPort* view_port); + #ifdef __cplusplus } #endif diff --git a/applications/gui/view_port_i.h b/applications/gui/view_port_i.h index 889775c8..55a9aa00 100644 --- a/applications/gui/view_port_i.h +++ b/applications/gui/view_port_i.h @@ -6,6 +6,7 @@ struct ViewPort { Gui* gui; bool is_enabled; + ViewPortOrientation orientation; uint8_t width; uint8_t height;