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 */ | /** 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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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,10 +215,13 @@ 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); | ||||||
| 
 | 
 | ||||||
|  |     do { | ||||||
|  |         if(gui->direct_draw) break; | ||||||
|  | 
 | ||||||
|         canvas_reset(gui->canvas); |         canvas_reset(gui->canvas); | ||||||
| 
 | 
 | ||||||
|         if(gui->lockdown) { |         if(gui->lockdown) { | ||||||
| @ -240,12 +243,16 @@ void gui_redraw(Gui* gui) { | |||||||
|         for |         for | ||||||
|             M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) { |             M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) { | ||||||
|                 p->callback( |                 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); |     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,6 +274,11 @@ void gui_input(Gui* gui, InputEvent* input_event) { | |||||||
| 
 | 
 | ||||||
|     gui_lock(gui); |     gui_lock(gui); | ||||||
| 
 | 
 | ||||||
|  |     do { | ||||||
|  |         if(gui->direct_draw && !gui->ongoing_input_view_port) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         ViewPort* view_port = NULL; |         ViewPort* view_port = NULL; | ||||||
| 
 | 
 | ||||||
|         if(gui->lockdown) { |         if(gui->lockdown) { | ||||||
| @ -303,6 +315,7 @@ void gui_input(Gui* gui, InputEvent* input_event) { | |||||||
|                 input_get_type_name(input_event->type), |                 input_get_type_name(input_event->type), | ||||||
|                 (void*)input_event->sequence); |                 (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
 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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; | ||||||
|  | |||||||
| @ -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*" | ||||||
|  | |||||||
| 
 | 
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく