|
@@ -5,11 +5,25 @@
|
|
|
|
|
|
|
|
#define TAG "RpcGui"
|
|
#define TAG "RpcGui"
|
|
|
|
|
|
|
|
|
|
+typedef enum {
|
|
|
|
|
+ RpcGuiWorkerFlagTransmit = (1 << 0),
|
|
|
|
|
+ RpcGuiWorkerFlagExit = (1 << 1),
|
|
|
|
|
+} RpcGuiWorkerFlag;
|
|
|
|
|
+
|
|
|
|
|
+#define RpcGuiWorkerFlagAny (RpcGuiWorkerFlagTransmit | RpcGuiWorkerFlagExit)
|
|
|
|
|
+
|
|
|
typedef struct {
|
|
typedef struct {
|
|
|
RpcSession* session;
|
|
RpcSession* session;
|
|
|
Gui* gui;
|
|
Gui* gui;
|
|
|
|
|
+
|
|
|
|
|
+ // Receive part
|
|
|
ViewPort* virtual_display_view_port;
|
|
ViewPort* virtual_display_view_port;
|
|
|
uint8_t* virtual_display_buffer;
|
|
uint8_t* virtual_display_buffer;
|
|
|
|
|
+
|
|
|
|
|
+ // Transmit
|
|
|
|
|
+ PB_Main* transmit_frame;
|
|
|
|
|
+ FuriThread* transmit_thread;
|
|
|
|
|
+
|
|
|
bool virtual_display_not_empty;
|
|
bool virtual_display_not_empty;
|
|
|
bool is_streaming;
|
|
bool is_streaming;
|
|
|
} RpcGuiSystem;
|
|
} RpcGuiSystem;
|
|
@@ -17,25 +31,35 @@ typedef struct {
|
|
|
static void
|
|
static void
|
|
|
rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) {
|
|
rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) {
|
|
|
furi_assert(data);
|
|
furi_assert(data);
|
|
|
- furi_assert(size == 1024);
|
|
|
|
|
furi_assert(context);
|
|
furi_assert(context);
|
|
|
|
|
|
|
|
RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;
|
|
RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;
|
|
|
- RpcSession* session = rpc_gui->session;
|
|
|
|
|
|
|
+ uint8_t* buffer = rpc_gui->transmit_frame->content.gui_screen_frame.data->bytes;
|
|
|
|
|
|
|
|
- PB_Main* frame = malloc(sizeof(PB_Main));
|
|
|
|
|
|
|
+ furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size);
|
|
|
|
|
|
|
|
- frame->which_content = PB_Main_gui_screen_frame_tag;
|
|
|
|
|
- frame->command_status = PB_CommandStatus_OK;
|
|
|
|
|
- frame->content.gui_screen_frame.data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(size));
|
|
|
|
|
- uint8_t* buffer = frame->content.gui_screen_frame.data->bytes;
|
|
|
|
|
- uint16_t* frame_size_msg = &frame->content.gui_screen_frame.data->size;
|
|
|
|
|
- *frame_size_msg = size;
|
|
|
|
|
memcpy(buffer, data, size);
|
|
memcpy(buffer, data, size);
|
|
|
|
|
|
|
|
- rpc_send_and_release(session, frame);
|
|
|
|
|
|
|
+ osThreadFlagsSet(
|
|
|
|
|
+ furi_thread_get_thread_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagTransmit);
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- free(frame);
|
|
|
|
|
|
|
+static int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context) {
|
|
|
|
|
+ furi_assert(context);
|
|
|
|
|
+
|
|
|
|
|
+ RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context;
|
|
|
|
|
+
|
|
|
|
|
+ while(true) {
|
|
|
|
|
+ uint32_t flags = osThreadFlagsWait(RpcGuiWorkerFlagAny, osFlagsWaitAny, osWaitForever);
|
|
|
|
|
+ if(flags & RpcGuiWorkerFlagTransmit) {
|
|
|
|
|
+ rpc_send(rpc_gui->session, rpc_gui->transmit_frame);
|
|
|
|
|
+ }
|
|
|
|
|
+ if(flags & RpcGuiWorkerFlagExit) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, void* context) {
|
|
static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, void* context) {
|
|
@@ -45,15 +69,30 @@ static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, v
|
|
|
|
|
|
|
|
RpcSession* session = rpc_gui->session;
|
|
RpcSession* session = rpc_gui->session;
|
|
|
furi_assert(session);
|
|
furi_assert(session);
|
|
|
|
|
+ furi_assert(!rpc_gui->is_streaming);
|
|
|
|
|
|
|
|
- if(gui_get_framebuffer_callback(rpc_gui->gui) == NULL) {
|
|
|
|
|
- rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
|
|
|
|
|
- rpc_gui->is_streaming = true;
|
|
|
|
|
- gui_set_framebuffer_callback(
|
|
|
|
|
- rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);
|
|
|
|
|
- } else {
|
|
|
|
|
- rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR_BUSY);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
|
|
|
|
|
+
|
|
|
|
|
+ rpc_gui->is_streaming = true;
|
|
|
|
|
+ size_t framebuffer_size = gui_get_framebuffer_size(rpc_gui->gui);
|
|
|
|
|
+ // Reusable Frame
|
|
|
|
|
+ rpc_gui->transmit_frame = malloc(sizeof(PB_Main));
|
|
|
|
|
+ rpc_gui->transmit_frame->which_content = PB_Main_gui_screen_frame_tag;
|
|
|
|
|
+ rpc_gui->transmit_frame->command_status = PB_CommandStatus_OK;
|
|
|
|
|
+ rpc_gui->transmit_frame->content.gui_screen_frame.data =
|
|
|
|
|
+ malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size));
|
|
|
|
|
+ rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size;
|
|
|
|
|
+ // Transmission thread for async TX
|
|
|
|
|
+ rpc_gui->transmit_thread = furi_thread_alloc();
|
|
|
|
|
+ furi_thread_set_name(rpc_gui->transmit_thread, "GuiRpcWorker");
|
|
|
|
|
+ furi_thread_set_callback(
|
|
|
|
|
+ rpc_gui->transmit_thread, rpc_system_gui_screen_stream_frame_transmit_thread);
|
|
|
|
|
+ furi_thread_set_context(rpc_gui->transmit_thread, rpc_gui);
|
|
|
|
|
+ furi_thread_set_stack_size(rpc_gui->transmit_thread, 1024);
|
|
|
|
|
+ furi_thread_start(rpc_gui->transmit_thread);
|
|
|
|
|
+ // GUI framebuffer callback
|
|
|
|
|
+ gui_add_framebuffer_callback(
|
|
|
|
|
+ rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, void* context) {
|
|
static void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, void* context) {
|
|
@@ -66,7 +105,18 @@ static void rpc_system_gui_stop_screen_stream_process(const PB_Main* request, vo
|
|
|
|
|
|
|
|
if(rpc_gui->is_streaming) {
|
|
if(rpc_gui->is_streaming) {
|
|
|
rpc_gui->is_streaming = false;
|
|
rpc_gui->is_streaming = false;
|
|
|
- gui_set_framebuffer_callback(rpc_gui->gui, NULL, NULL);
|
|
|
|
|
|
|
+ // Remove GUI framebuffer callback
|
|
|
|
|
+ gui_remove_framebuffer_callback(
|
|
|
|
|
+ rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);
|
|
|
|
|
+ // Stop and release worker thread
|
|
|
|
|
+ osThreadFlagsSet(
|
|
|
|
|
+ furi_thread_get_thread_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagExit);
|
|
|
|
|
+ furi_thread_join(rpc_gui->transmit_thread);
|
|
|
|
|
+ furi_thread_free(rpc_gui->transmit_thread);
|
|
|
|
|
+ // Release frame
|
|
|
|
|
+ pb_release(&PB_Main_msg, rpc_gui->transmit_frame);
|
|
|
|
|
+ free(rpc_gui->transmit_frame);
|
|
|
|
|
+ rpc_gui->transmit_frame = NULL;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
|
|
rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK);
|
|
@@ -296,7 +346,8 @@ void rpc_system_gui_free(void* context) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if(rpc_gui->is_streaming) {
|
|
if(rpc_gui->is_streaming) {
|
|
|
- gui_set_framebuffer_callback(rpc_gui->gui, NULL, NULL);
|
|
|
|
|
|
|
+ gui_remove_framebuffer_callback(
|
|
|
|
|
+ rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context);
|
|
|
}
|
|
}
|
|
|
furi_record_close("gui");
|
|
furi_record_close("gui");
|
|
|
free(rpc_gui);
|
|
free(rpc_gui);
|