فهرست منبع

Gui: Direct Draw API (#2215)

* Furi: allow on-fly thread priority change. Gui: Direct Draw API. DirectDraw debug app.
* Gui: drop input in direct draw
* Furi: handle priority change for starting threads
* DirectDraw: rollback to FreeRTOS primitives for priority change
あく 3 سال پیش
والد
کامیت
b11b9f1b38

+ 10 - 0
applications/debug/direct_draw/application.fam

@@ -0,0 +1,10 @@
+App(
+    appid="direct_draw",
+    name="Direct Draw",
+    apptype=FlipperAppType.DEBUG,
+    entry_point="direct_draw_app",
+    requires=["gui", "input"],
+    stack_size=2 * 1024,
+    order=70,
+    fap_category="Debug",
+)

+ 112 - 0
applications/debug/direct_draw/direct_draw.c

@@ -0,0 +1,112 @@
+#include <furi.h>
+#include <gui/gui.h>
+#include <gui/canvas_i.h>
+#include <input/input.h>
+
+#define BUFFER_SIZE (32U)
+
+typedef struct {
+    FuriPubSub* input;
+    FuriPubSubSubscription* input_subscription;
+    Gui* gui;
+    Canvas* canvas;
+    bool stop;
+    uint32_t counter;
+} DirectDraw;
+
+static void gui_input_events_callback(const void* value, void* ctx) {
+    furi_assert(value);
+    furi_assert(ctx);
+
+    DirectDraw* instance = ctx;
+    const InputEvent* event = value;
+
+    if(event->key == InputKeyBack && event->type == InputTypeShort) {
+        instance->stop = true;
+    }
+}
+
+static DirectDraw* direct_draw_alloc() {
+    DirectDraw* instance = malloc(sizeof(DirectDraw));
+
+    instance->input = furi_record_open(RECORD_INPUT_EVENTS);
+    instance->gui = furi_record_open(RECORD_GUI);
+    instance->canvas = gui_direct_draw_acquire(instance->gui);
+
+    instance->input_subscription =
+        furi_pubsub_subscribe(instance->input, gui_input_events_callback, instance);
+
+    return instance;
+}
+
+static void direct_draw_free(DirectDraw* instance) {
+    furi_pubsub_unsubscribe(instance->input, instance->input_subscription);
+
+    instance->canvas = NULL;
+    gui_direct_draw_release(instance->gui);
+    furi_record_close(RECORD_GUI);
+    furi_record_close(RECORD_INPUT_EVENTS);
+}
+
+static void direct_draw_block(Canvas* canvas, uint32_t size, uint32_t counter) {
+    size += 16;
+    uint8_t width = canvas_width(canvas) - size;
+    uint8_t height = canvas_height(canvas) - size;
+
+    uint8_t x = counter % width;
+    if((counter / width) % 2) {
+        x = width - x;
+    }
+
+    uint8_t y = counter % height;
+    if((counter / height) % 2) {
+        y = height - y;
+    }
+
+    canvas_draw_box(canvas, x, y, size, size);
+}
+
+static void direct_draw_run(DirectDraw* instance) {
+    size_t start = DWT->CYCCNT;
+    size_t counter = 0;
+    float fps = 0;
+
+    vTaskPrioritySet(furi_thread_get_current_id(), FuriThreadPriorityIdle);
+
+    do {
+        size_t elapsed = DWT->CYCCNT - start;
+        char buffer[BUFFER_SIZE] = {0};
+
+        if(elapsed >= 64000000) {
+            fps = (float)counter / ((float)elapsed / 64000000.0f);
+
+            start = DWT->CYCCNT;
+            counter = 0;
+        }
+        snprintf(buffer, BUFFER_SIZE, "FPS: %.1f", (double)fps);
+
+        canvas_reset(instance->canvas);
+        canvas_set_color(instance->canvas, ColorXOR);
+        direct_draw_block(instance->canvas, instance->counter % 16, instance->counter);
+        direct_draw_block(instance->canvas, instance->counter * 2 % 16, instance->counter * 2);
+        direct_draw_block(instance->canvas, instance->counter * 3 % 16, instance->counter * 3);
+        direct_draw_block(instance->canvas, instance->counter * 4 % 16, instance->counter * 4);
+        direct_draw_block(instance->canvas, instance->counter * 5 % 16, instance->counter * 5);
+        canvas_draw_str(instance->canvas, 10, 10, buffer);
+        canvas_commit(instance->canvas);
+
+        counter++;
+        instance->counter++;
+        furi_thread_yield();
+    } while(!instance->stop);
+}
+
+int32_t direct_draw_app(void* p) {
+    UNUSED(p);
+
+    DirectDraw* instance = direct_draw_alloc();
+    direct_draw_run(instance);
+    direct_draw_free(instance);
+
+    return 0;
+}

+ 12 - 0
applications/services/gui/canvas.h

@@ -67,6 +67,18 @@ typedef struct {
 /** Canvas anonymous structure */
 /** Canvas anonymous structure */
 typedef struct Canvas Canvas;
 typedef struct Canvas Canvas;
 
 
+/** Reset canvas drawing tools configuration
+ *
+ * @param      canvas  Canvas instance
+ */
+void canvas_reset(Canvas* canvas);
+
+/** Commit canvas. Send buffer to display
+ *
+ * @param      canvas  Canvas instance
+ */
+void canvas_commit(Canvas* canvas);
+
 /** Get Canvas width
 /** Get Canvas width
  *
  *
  * @param      canvas  Canvas instance
  * @param      canvas  Canvas instance

+ 0 - 12
applications/services/gui/canvas_i.h

@@ -31,18 +31,6 @@ Canvas* canvas_init();
  */
  */
 void canvas_free(Canvas* canvas);
 void canvas_free(Canvas* canvas);
 
 
-/** Reset canvas drawing tools configuration
- *
- * @param      canvas  Canvas instance
- */
-void canvas_reset(Canvas* canvas);
-
-/** Commit canvas. Send buffer to display
- *
- * @param      canvas  Canvas instance
- */
-void canvas_commit(Canvas* canvas);
-
 /** Get canvas buffer.
 /** Get canvas buffer.
  *
  *
  * @param      canvas  Canvas instance
  * @param      canvas  Canvas instance

+ 97 - 59
applications/services/gui/gui.c

@@ -20,7 +20,7 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
 
 
 void gui_update(Gui* gui) {
 void gui_update(Gui* gui) {
     furi_assert(gui);
     furi_assert(gui);
-    furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW);
+    if(!gui->direct_draw) furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW);
 }
 }
 
 
 void gui_input_events_callback(const void* value, void* ctx) {
 void gui_input_events_callback(const void* value, void* ctx) {
@@ -34,7 +34,7 @@ void gui_input_events_callback(const void* value, void* ctx) {
 }
 }
 
 
 // Only Fullscreen supports vertical display for now
 // Only Fullscreen supports vertical display for now
-bool gui_redraw_fs(Gui* gui) {
+static bool gui_redraw_fs(Gui* gui) {
     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
     canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);
     canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);
     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
@@ -192,7 +192,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) {
     }
     }
 }
 }
 
 
-bool gui_redraw_window(Gui* gui) {
+static bool gui_redraw_window(Gui* gui) {
     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
     canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT);
     canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT);
     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
@@ -203,7 +203,7 @@ bool gui_redraw_window(Gui* gui) {
     return false;
     return false;
 }
 }
 
 
-bool gui_redraw_desktop(Gui* gui) {
+static bool gui_redraw_desktop(Gui* gui) {
     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal);
     canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);
     canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);
     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
@@ -215,37 +215,44 @@ bool gui_redraw_desktop(Gui* gui) {
     return false;
     return false;
 }
 }
 
 
-void gui_redraw(Gui* gui) {
+static void gui_redraw(Gui* gui) {
     furi_assert(gui);
     furi_assert(gui);
     gui_lock(gui);
     gui_lock(gui);
 
 
-    canvas_reset(gui->canvas);
-
-    if(gui->lockdown) {
-        gui_redraw_desktop(gui);
-        bool need_attention =
-            (gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 ||
-             gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0);
-        gui_redraw_status_bar(gui, need_attention);
-    } else {
-        if(!gui_redraw_fs(gui)) {
-            if(!gui_redraw_window(gui)) {
-                gui_redraw_desktop(gui);
+    do {
+        if(gui->direct_draw) break;
+
+        canvas_reset(gui->canvas);
+
+        if(gui->lockdown) {
+            gui_redraw_desktop(gui);
+            bool need_attention =
+                (gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 ||
+                 gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0);
+            gui_redraw_status_bar(gui, need_attention);
+        } else {
+            if(!gui_redraw_fs(gui)) {
+                if(!gui_redraw_window(gui)) {
+                    gui_redraw_desktop(gui);
+                }
+                gui_redraw_status_bar(gui, false);
             }
             }
-            gui_redraw_status_bar(gui, false);
         }
         }
-    }
 
 
-    canvas_commit(gui->canvas);
-    for
-        M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) {
-            p->callback(
-                canvas_get_buffer(gui->canvas), canvas_get_buffer_size(gui->canvas), p->context);
-        }
+        canvas_commit(gui->canvas);
+        for
+            M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) {
+                p->callback(
+                    canvas_get_buffer(gui->canvas),
+                    canvas_get_buffer_size(gui->canvas),
+                    p->context);
+            }
+    } while(false);
+
     gui_unlock(gui);
     gui_unlock(gui);
 }
 }
 
 
-void gui_input(Gui* gui, InputEvent* input_event) {
+static void gui_input(Gui* gui, InputEvent* input_event) {
     furi_assert(gui);
     furi_assert(gui);
     furi_assert(input_event);
     furi_assert(input_event);
 
 
@@ -267,42 +274,48 @@ void gui_input(Gui* gui, InputEvent* input_event) {
 
 
     gui_lock(gui);
     gui_lock(gui);
 
 
-    ViewPort* view_port = NULL;
+    do {
+        if(gui->direct_draw && !gui->ongoing_input_view_port) {
+            break;
+        }
 
 
-    if(gui->lockdown) {
-        view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
-    } else {
-        view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
-        if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
-        if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
-    }
+        ViewPort* view_port = NULL;
 
 
-    if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) {
-        gui->ongoing_input_view_port = view_port;
-    }
+        if(gui->lockdown) {
+            view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
+        } else {
+            view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]);
+            if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]);
+            if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]);
+        }
 
 
-    if(view_port && view_port == gui->ongoing_input_view_port) {
-        view_port_input(view_port, input_event);
-    } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {
-        FURI_LOG_D(
-            TAG,
-            "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
-            gui->ongoing_input_view_port,
-            view_port,
-            input_get_key_name(input_event->key),
-            input_get_type_name(input_event->type),
-            (void*)input_event->sequence);
-        view_port_input(gui->ongoing_input_view_port, input_event);
-    } else {
-        FURI_LOG_D(
-            TAG,
-            "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p",
-            gui->ongoing_input_view_port,
-            view_port,
-            input_get_key_name(input_event->key),
-            input_get_type_name(input_event->type),
-            (void*)input_event->sequence);
-    }
+        if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) {
+            gui->ongoing_input_view_port = view_port;
+        }
+
+        if(view_port && view_port == gui->ongoing_input_view_port) {
+            view_port_input(view_port, input_event);
+        } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {
+            FURI_LOG_D(
+                TAG,
+                "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
+                gui->ongoing_input_view_port,
+                view_port,
+                input_get_key_name(input_event->key),
+                input_get_type_name(input_event->type),
+                (void*)input_event->sequence);
+            view_port_input(gui->ongoing_input_view_port, input_event);
+        } else {
+            FURI_LOG_D(
+                TAG,
+                "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p",
+                gui->ongoing_input_view_port,
+                view_port,
+                input_get_key_name(input_event->key),
+                input_get_type_name(input_event->type),
+                (void*)input_event->sequence);
+        }
+    } while(false);
 
 
     gui_unlock(gui);
     gui_unlock(gui);
 }
 }
@@ -471,6 +484,31 @@ void gui_set_lockdown(Gui* gui, bool lockdown) {
     gui_update(gui);
     gui_update(gui);
 }
 }
 
 
+Canvas* gui_direct_draw_acquire(Gui* gui) {
+    furi_assert(gui);
+    gui_lock(gui);
+    gui->direct_draw = true;
+    gui_unlock(gui);
+
+    canvas_reset(gui->canvas);
+    canvas_commit(gui->canvas);
+
+    return gui->canvas;
+}
+
+void gui_direct_draw_release(Gui* gui) {
+    furi_assert(gui);
+
+    canvas_reset(gui->canvas);
+    canvas_commit(gui->canvas);
+
+    gui_lock(gui);
+    gui->direct_draw = false;
+    gui_unlock(gui);
+
+    gui_update(gui);
+}
+
 Gui* gui_alloc() {
 Gui* gui_alloc() {
     Gui* gui = malloc(sizeof(Gui));
     Gui* gui = malloc(sizeof(Gui));
     // Thread ID
     // Thread ID

+ 22 - 0
applications/services/gui/gui.h

@@ -106,6 +106,28 @@ size_t gui_get_framebuffer_size(Gui* gui);
  */
  */
 void gui_set_lockdown(Gui* gui, bool lockdown);
 void gui_set_lockdown(Gui* gui, bool lockdown);
 
 
+/** Acquire Direct Draw lock and get Canvas instance
+ *
+ * This method return Canvas instance for use in monopoly mode. Direct draw lock
+ * disables input and draw call dispatch functions in GUI service. No other
+ * applications or services will be able to draw until gui_direct_draw_release
+ * call.
+ *
+ * @param      gui   The graphical user interface
+ *
+ * @return     Canvas instance
+ */
+Canvas* gui_direct_draw_acquire(Gui* gui);
+
+/** Release Direct Draw Lock
+ *
+ * Release Direct Draw Lock, enables Input and Draw call processing. Canvas
+ * acquired in gui_direct_draw_acquire will become invalid after this call.
+ *
+ * @param      gui   Gui instance
+ */
+void gui_direct_draw_release(Gui* gui);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

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

@@ -62,6 +62,7 @@ struct Gui {
 
 
     // Layers and Canvas
     // Layers and Canvas
     bool lockdown;
     bool lockdown;
+    bool direct_draw;
     ViewPortArray_t layers[GuiLayerMAX];
     ViewPortArray_t layers[GuiLayerMAX];
     Canvas* canvas;
     Canvas* canvas;
     CanvasCallbackPairArray_t canvas_callback_pair;
     CanvasCallbackPairArray_t canvas_callback_pair;

+ 5 - 1
firmware/targets/f7/api_symbols.csv

@@ -1,5 +1,5 @@
 entry,status,name,type,params
 entry,status,name,type,params
-Version,+,11.3,,
+Version,+,11.4,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
@@ -593,6 +593,7 @@ Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, B
 Function,-,bzero,void,"void*, size_t"
 Function,-,bzero,void,"void*, size_t"
 Function,-,calloc,void*,"size_t, size_t"
 Function,-,calloc,void*,"size_t, size_t"
 Function,+,canvas_clear,void,Canvas*
 Function,+,canvas_clear,void,Canvas*
+Function,+,canvas_commit,void,Canvas*
 Function,+,canvas_current_font_height,uint8_t,Canvas*
 Function,+,canvas_current_font_height,uint8_t,Canvas*
 Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*"
 Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*"
 Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
 Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
@@ -614,6 +615,7 @@ Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font"
 Function,+,canvas_glyph_width,uint8_t,"Canvas*, char"
 Function,+,canvas_glyph_width,uint8_t,"Canvas*, char"
 Function,+,canvas_height,uint8_t,Canvas*
 Function,+,canvas_height,uint8_t,Canvas*
 Function,+,canvas_invert_color,void,Canvas*
 Function,+,canvas_invert_color,void,Canvas*
+Function,+,canvas_reset,void,Canvas*
 Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool"
 Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool"
 Function,+,canvas_set_color,void,"Canvas*, Color"
 Function,+,canvas_set_color,void,"Canvas*, Color"
 Function,+,canvas_set_font,void,"Canvas*, Font"
 Function,+,canvas_set_font,void,"Canvas*, Font"
@@ -1554,6 +1556,8 @@ Function,-,gmtime,tm*,const time_t*
 Function,-,gmtime_r,tm*,"const time_t*, tm*"
 Function,-,gmtime_r,tm*,"const time_t*, tm*"
 Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*"
 Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*"
 Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer"
 Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer"
+Function,+,gui_direct_draw_acquire,Canvas*,Gui*
+Function,+,gui_direct_draw_release,void,Gui*
 Function,+,gui_get_framebuffer_size,size_t,Gui*
 Function,+,gui_get_framebuffer_size,size_t,Gui*
 Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*"
 Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*"
 Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"
 Function,+,gui_remove_view_port,void,"Gui*, ViewPort*"