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
This commit is contained in:
		
							parent
							
								
									26e5527a93
								
							
						
					
					
						commit
						b11b9f1b38
					
				
							
								
								
									
										10
									
								
								applications/debug/direct_draw/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/debug/direct_draw/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								applications/debug/direct_draw/direct_draw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								applications/debug/direct_draw/direct_draw.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| } | ||||
| @ -67,6 +67,18 @@ typedef struct { | ||||
| /** Canvas anonymous structure */ | ||||
| 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
 | ||||
|  * | ||||
|  * @param      canvas  Canvas instance | ||||
|  | ||||
| @ -31,18 +31,6 @@ Canvas* canvas_init(); | ||||
|  */ | ||||
| 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.
 | ||||
|  * | ||||
|  * @param      canvas  Canvas instance | ||||
|  | ||||
| @ -20,7 +20,7 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { | ||||
| 
 | ||||
| void gui_update(Gui* 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) { | ||||
| @ -34,7 +34,7 @@ void gui_input_events_callback(const void* value, void* ctx) { | ||||
| } | ||||
| 
 | ||||
| // 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_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); | ||||
|     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_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]); | ||||
| @ -203,7 +203,7 @@ bool gui_redraw_window(Gui* gui) { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool gui_redraw_desktop(Gui* gui) { | ||||
| static bool gui_redraw_desktop(Gui* gui) { | ||||
|     canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); | ||||
|     canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); | ||||
|     ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); | ||||
| @ -215,10 +215,13 @@ bool gui_redraw_desktop(Gui* gui) { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void gui_redraw(Gui* gui) { | ||||
| static void gui_redraw(Gui* gui) { | ||||
|     furi_assert(gui); | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     do { | ||||
|         if(gui->direct_draw) break; | ||||
| 
 | ||||
|         canvas_reset(gui->canvas); | ||||
| 
 | ||||
|         if(gui->lockdown) { | ||||
| @ -240,12 +243,16 @@ void gui_redraw(Gui* gui) { | ||||
|         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_get_buffer(gui->canvas), | ||||
|                     canvas_get_buffer_size(gui->canvas), | ||||
|                     p->context); | ||||
|             } | ||||
|     } while(false); | ||||
| 
 | ||||
|     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(input_event); | ||||
| 
 | ||||
| @ -267,6 +274,11 @@ void gui_input(Gui* gui, InputEvent* input_event) { | ||||
| 
 | ||||
|     gui_lock(gui); | ||||
| 
 | ||||
|     do { | ||||
|         if(gui->direct_draw && !gui->ongoing_input_view_port) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         ViewPort* view_port = NULL; | ||||
| 
 | ||||
|         if(gui->lockdown) { | ||||
| @ -303,6 +315,7 @@ void gui_input(Gui* gui, InputEvent* input_event) { | ||||
|                 input_get_type_name(input_event->type), | ||||
|                 (void*)input_event->sequence); | ||||
|         } | ||||
|     } while(false); | ||||
| 
 | ||||
|     gui_unlock(gui); | ||||
| } | ||||
| @ -471,6 +484,31 @@ void gui_set_lockdown(Gui* gui, bool lockdown) { | ||||
|     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 = malloc(sizeof(Gui)); | ||||
|     // Thread ID
 | ||||
|  | ||||
| @ -106,6 +106,28 @@ size_t gui_get_framebuffer_size(Gui* gui); | ||||
|  */ | ||||
| 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 | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -62,6 +62,7 @@ struct Gui { | ||||
| 
 | ||||
|     // Layers and Canvas
 | ||||
|     bool lockdown; | ||||
|     bool direct_draw; | ||||
|     ViewPortArray_t layers[GuiLayerMAX]; | ||||
|     Canvas* canvas; | ||||
|     CanvasCallbackPairArray_t canvas_callback_pair; | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| entry,status,name,type,params | ||||
| Version,+,11.3,, | ||||
| Version,+,11.4,, | ||||
| Header,+,applications/services/bt/bt_service/bt.h,, | ||||
| Header,+,applications/services/cli/cli.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,-,calloc,void*,"size_t, size_t" | ||||
| Function,+,canvas_clear,void,Canvas* | ||||
| Function,+,canvas_commit,void,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_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_height,uint8_t,Canvas* | ||||
| Function,+,canvas_invert_color,void,Canvas* | ||||
| Function,+,canvas_reset,void,Canvas* | ||||
| Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" | ||||
| Function,+,canvas_set_color,void,"Canvas*, Color" | ||||
| 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,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" | ||||
| 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_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" | ||||
| Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" | ||||
|  | ||||
| 
 | 
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく