diff --git a/app/meson.build b/app/meson.build index e33f9d4f..5168e59c 100644 --- a/app/meson.build +++ b/app/meson.build @@ -15,6 +15,7 @@ src = [ 'src/delay_buffer.c', 'src/demuxer.c', 'src/device_msg.c', + 'src/disconnect.c', 'src/events.c', 'src/icon.c', 'src/file_pusher.c', diff --git a/app/src/disconnect.c b/app/src/disconnect.c new file mode 100644 index 00000000..4ea4d56c --- /dev/null +++ b/app/src/disconnect.c @@ -0,0 +1,89 @@ +#include "disconnect.h" + +#include + +#include "icon.h" +#include "util/log.h" + +static int +run(void *userdata) { + struct sc_disconnect *d = userdata; + + SDL_Surface *icon = sc_icon_load(SC_ICON_FILENAME_DISCONNECTED); + if (icon) { + d->cbs->on_icon_loaded(d, icon, d->cbs_userdata); + } else { + LOGE("Could not load disconnected icon"); + } + + if (d->deadline != SC_TICK_NONE) { + sc_mutex_lock(&d->mutex); + bool timed_out = false; + while (!d->interrupted && !timed_out) { + timed_out = !sc_cond_timedwait(&d->cond, &d->mutex, d->deadline); + } + sc_mutex_unlock(&d->mutex); + + if (!d->interrupted) { + d->cbs->on_timeout(d, d->cbs_userdata); + } + } + + return 0; +} + +bool +sc_disconnect_start(struct sc_disconnect *d, sc_tick deadline, + const struct sc_disconnect_callbacks *cbs, + void *cbs_userdata) { + bool ok = sc_mutex_init(&d->mutex); + if (!ok) { + return false; + } + + ok = sc_cond_init(&d->cond); + if (!ok) { + goto error_destroy_mutex; + } + + ok = sc_thread_create(&d->thread, run, "scrcpy-dis", d); + if (!ok) { + goto error_destroy_cond; + } + + d->deadline = deadline; + d->interrupted = false; + + assert(cbs && cbs->on_icon_loaded && cbs->on_timeout); + d->cbs = cbs; + d->cbs_userdata = cbs_userdata; + + return true; + +error_destroy_mutex: + sc_mutex_destroy(&d->mutex); +error_destroy_cond: + sc_cond_destroy(&d->cond); + + return false; +} + +void +sc_disconnect_interrupt(struct sc_disconnect *d) { + sc_mutex_lock(&d->mutex); + d->interrupted = true; + sc_mutex_unlock(&d->mutex); + // wake up blocking wait + sc_cond_signal(&d->cond); +} + +void +sc_disconnect_join(struct sc_disconnect *d) { + sc_thread_join(&d->thread, NULL); +} + +void +sc_disconnect_destroy(struct sc_disconnect *d) { + sc_cond_destroy(&d->cond); + sc_mutex_destroy(&d->mutex); +} diff --git a/app/src/disconnect.h b/app/src/disconnect.h new file mode 100644 index 00000000..5b63ee59 --- /dev/null +++ b/app/src/disconnect.h @@ -0,0 +1,47 @@ +#ifndef SC_DISCONNECT +#define SC_DISCONNECT + +#include "common.h" + +#include "SDL3/SDL_surface.h" +#include "util/tick.h" +#include "util/thread.h" + +// Tool to handle loading the icon and signal timeout when the device is +// unexpectedly disconnected +struct sc_disconnect { + sc_tick deadline; + + struct sc_thread thread; + struct sc_mutex mutex; + struct sc_cond cond; + bool interrupted; + + const struct sc_disconnect_callbacks *cbs; + void *cbs_userdata; +}; + +struct sc_disconnect_callbacks { + // Called when the disconnected icon is loaded + void (*on_icon_loaded)(struct sc_disconnect *d, SDL_Surface *icon, + void *userdata); + + // Called when the timeout expired (the scrcpy window must be closed) + void (*on_timeout)(struct sc_disconnect *d, void *userdata); +}; + +bool +sc_disconnect_start(struct sc_disconnect *d, sc_tick deadline, + const struct sc_disconnect_callbacks *cbs, + void *cbs_userdata); + +void +sc_disconnect_interrupt(struct sc_disconnect *d); + +void +sc_disconnect_join(struct sc_disconnect *d); + +void +sc_disconnect_destroy(struct sc_disconnect *d); + +#endif diff --git a/app/src/util/tick.h b/app/src/util/tick.h index b037734b..646ada77 100644 --- a/app/src/util/tick.h +++ b/app/src/util/tick.h @@ -6,6 +6,7 @@ #include typedef int64_t sc_tick; +#define SC_TICK_NONE INT64_MIN #define PRItick PRIi64 #define SC_TICK_FREQ 1000000 // microsecond