diff --git a/app/src/events.c b/app/src/events.c index 4e256be2..ce885241 100644 --- a/app/src/events.c +++ b/app/src/events.c @@ -1,6 +1,7 @@ #include "events.h" #include "util/log.h" +#include "util/thread.h" bool sc_push_event_impl(uint32_t type, const char *name) { @@ -17,3 +18,49 @@ sc_push_event_impl(uint32_t type, const char *name) { return true; } + +bool +sc_post_to_main_thread(sc_runnable_fn run, void *userdata) { + SDL_Event event = { + .user = { + .type = SC_EVENT_RUN_ON_MAIN_THREAD, + .data1 = run, + .data2 = userdata, + }, + }; + int ret = SDL_PushEvent(&event); + // ret < 0: error (queue full) + // ret == 0: event was filtered + // ret == 1: success + if (ret != 1) { + if (ret == 0) { + // if ret == 0, this is expected on exit, log in debug mode + LOGD("Could not post runnable to main thread (filtered)"); + } else { + assert(ret < 0); + LOGW("Could not post runnable to main thread: %s", SDL_GetError()); + } + return false; + } + + return true; +} + +static int SDLCALL +task_event_filter(void *userdata, SDL_Event *event) { + (void) userdata; + + if (event->type == SC_EVENT_RUN_ON_MAIN_THREAD) { + // Reject this event type from now on + return 0; + } + + return 1; +} + +void +sc_reject_new_runnables(void) { + assert(sc_thread_get_id() == SC_MAIN_THREAD_ID); + + SDL_SetEventFilter(task_event_filter, NULL); +} diff --git a/app/src/events.h b/app/src/events.h index d803fb68..3f15087a 100644 --- a/app/src/events.h +++ b/app/src/events.h @@ -9,6 +9,7 @@ enum { SC_EVENT_NEW_FRAME = SDL_USEREVENT, + SC_EVENT_RUN_ON_MAIN_THREAD, SC_EVENT_DEVICE_DISCONNECTED, SC_EVENT_SERVER_CONNECTION_FAILED, SC_EVENT_SERVER_CONNECTED, @@ -25,4 +26,12 @@ sc_push_event_impl(uint32_t type, const char *name); #define sc_push_event(TYPE) sc_push_event_impl(TYPE, # TYPE) +typedef void (*sc_runnable_fn)(void *userdata); + +bool +sc_post_to_main_thread(sc_runnable_fn run, void *userdata); + +void +sc_reject_new_runnables(void); + #endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 625a53a9..efad5891 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -174,6 +174,12 @@ event_loop(struct scrcpy *s) { case SDL_QUIT: LOGD("User requested to quit"); return SCRCPY_EXIT_SUCCESS; + case SC_EVENT_RUN_ON_MAIN_THREAD: { + sc_runnable_fn run = event.user.data1; + void *userdata = event.user.data2; + run(userdata); + break; + } default: if (!sc_screen_handle_event(&s->screen, &event)) { return SCRCPY_EXIT_FAILURE; @@ -184,6 +190,21 @@ event_loop(struct scrcpy *s) { return SCRCPY_EXIT_FAILURE; } +static void +terminate_event_loop(void) { + sc_reject_new_runnables(); + + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SC_EVENT_RUN_ON_MAIN_THREAD) { + // Make sure all posted runnables are run, to avoid memory leaks + sc_runnable_fn run = event.user.data1; + void *userdata = event.user.data2; + run(userdata); + } + } +} + // Return true on success, false on error static bool await_for_server(bool *connected) { @@ -819,6 +840,7 @@ scrcpy(struct scrcpy_options *options) { } ret = event_loop(s); + terminate_event_loop(); LOGD("quit..."); if (options->video_playback) {