diff --git a/app/meson.build b/app/meson.build index b74b75fa..c09d772d 100644 --- a/app/meson.build +++ b/app/meson.build @@ -6,10 +6,10 @@ src = [ 'src/convert.c', 'src/decoder.c', 'src/device.c', + 'src/file_handler.c', 'src/fpscounter.c', 'src/frames.c', 'src/inputmanager.c', - 'src/installer.c', 'src/lockutil.c', 'src/net.c', 'src/scrcpy.c', diff --git a/app/src/file_handler.c b/app/src/file_handler.c new file mode 100644 index 00000000..2820c825 --- /dev/null +++ b/app/src/file_handler.c @@ -0,0 +1,181 @@ +#include "file_handler.h" + +#include +#include "command.h" +#include "lockutil.h" +#include "log.h" + +// NOTE(adopi) this can be more generic: +// it could be used with a command queue instead of a filename queue +// then we would have a generic invoker (useful if we want to handle more async commands) + +SDL_bool file_queue_is_empty(const struct file_queue *queue) { + return queue->head == queue->tail; +} + +SDL_bool file_queue_is_full(const struct file_queue *queue) { + return (queue->head + 1) % FILE_QUEUE_SIZE == queue->tail; +} + +SDL_bool file_queue_init(struct file_queue *queue) { + queue->head = 0; + queue->tail = 0; + return SDL_TRUE; +} + +void file_queue_destroy(struct file_queue *queue) { + int i = queue->tail; + while (i != queue->head) { + SDL_free(queue->data[i]); + i = (i + 1) % FILE_QUEUE_SIZE; + } +} + +SDL_bool file_queue_push(struct file_queue *queue, const char *file) { + if (file_queue_is_full(queue)) { + return SDL_FALSE; + } + queue->data[queue->head] = SDL_strdup(file); + queue->head = (queue->head + 1) % FILE_QUEUE_SIZE; + return SDL_TRUE; +} + +SDL_bool file_queue_take(struct file_queue *queue, char **file) { + if (file_queue_is_empty(queue)) { + return SDL_FALSE; + } + // transfer ownership + *file = queue->data[queue->tail]; + queue->tail = (queue->tail + 1) % FILE_QUEUE_SIZE; + return SDL_TRUE; +} + +SDL_bool file_handler_init(struct file_handler *file_handler, const char *serial) { + + if (!file_queue_init(&file_handler->queue)) { + return SDL_FALSE; + } + + if (!(file_handler->mutex = SDL_CreateMutex())) { + return SDL_FALSE; + } + + if (!(file_handler->event_cond = SDL_CreateCond())) { + SDL_DestroyMutex(file_handler->mutex); + return SDL_FALSE; + } + + if (serial) { + file_handler->serial = SDL_strdup(serial); + if (!file_handler->serial) { + LOGW("Cannot strdup serial"); + SDL_DestroyMutex(file_handler->mutex); + return SDL_FALSE; + } + } else { + file_handler->serial = NULL; + } + + // lazy initialization + file_handler->initialized = SDL_FALSE; + + file_handler->stopped = SDL_FALSE; + file_handler->current_process = PROCESS_NONE; + + return SDL_TRUE; +} + +void file_handler_destroy(struct file_handler *file_handler) { + SDL_DestroyCond(file_handler->event_cond); + SDL_DestroyMutex(file_handler->mutex); + file_queue_destroy(&file_handler->queue); + SDL_free((void *) file_handler->serial); +} + +SDL_bool file_handler_do(struct file_handler *file_handler, const char *file) { + SDL_bool res; + + // start file_handler if it's used for the first time + if (!file_handler->initialized) { + if (!file_handler_start(file_handler)) { + return SDL_FALSE; + } + file_handler->initialized = SDL_TRUE; + } + + mutex_lock(file_handler->mutex); + SDL_bool was_empty = file_queue_is_empty(&file_handler->queue); + res = file_queue_push(&file_handler->queue, file); + if (was_empty) { + cond_signal(file_handler->event_cond); + } + mutex_unlock(file_handler->mutex); + return res; +} + +static int run_file_handler(void *data) { + struct file_handler *file_handler = data; + + for (;;) { + mutex_lock(file_handler->mutex); + file_handler->current_process = PROCESS_NONE; + while (!file_handler->stopped && file_queue_is_empty(&file_handler->queue)) { + cond_wait(file_handler->event_cond, file_handler->mutex); + } + if (file_handler->stopped) { + // stop immediately, do not process further events + mutex_unlock(file_handler->mutex); + break; + } + char *current_apk; +#ifdef BUILD_DEBUG + bool non_empty = file_queue_take(&file_handler->queue, ¤t_apk); + SDL_assert(non_empty); +#else + file_queue_take(&file_handler->queue, ¤t_apk); +#endif + LOGI("Installing %s...", current_apk); + process_t process = adb_install(file_handler->serial, current_apk); + file_handler->current_process = process; + + mutex_unlock(file_handler->mutex); + + if (process_check_success(process, "adb install")) { + LOGI("%s installed successfully", current_apk); + } else { + LOGE("Failed to install %s", current_apk); + } + SDL_free(current_apk); + } + return 0; +} + +SDL_bool file_handler_start(struct file_handler *file_handler) { + LOGD("Starting file_handler thread"); + + file_handler->thread = SDL_CreateThread(run_file_handler, "file_handler", file_handler); + if (!file_handler->thread) { + LOGC("Could not start file_handler thread"); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +void file_handler_stop(struct file_handler *file_handler) { + mutex_lock(file_handler->mutex); + file_handler->stopped = SDL_TRUE; + cond_signal(file_handler->event_cond); + if (file_handler->current_process != PROCESS_NONE) { + if (!cmd_terminate(file_handler->current_process)) { + LOGW("Cannot terminate install process"); + } + cmd_simple_wait(file_handler->current_process, NULL); + file_handler->current_process = PROCESS_NONE; + } + mutex_unlock(file_handler->mutex); +} + +void file_handler_join(struct file_handler *file_handler) { + SDL_WaitThread(file_handler->thread, NULL); +} diff --git a/app/src/file_handler.h b/app/src/file_handler.h new file mode 100644 index 00000000..9cee9d3e --- /dev/null +++ b/app/src/file_handler.h @@ -0,0 +1,39 @@ +#ifndef FILE_HANDLER_H +#define FILE_HADNELR_H + +#include +#include +#include +#include "command.h" + +#define FILE_QUEUE_SIZE 16 + +// NOTE(AdoPi) file_queue and control_event can use a generic queue + +struct file_queue { + char *data[FILE_QUEUE_SIZE]; + int tail; + int head; +}; + +struct file_handler { + const char *serial; + SDL_Thread *thread; + SDL_mutex *mutex; + SDL_cond *event_cond; + SDL_bool stopped; + SDL_bool initialized; + process_t current_process; + struct file_queue queue; +}; + +SDL_bool file_handler_init(struct file_handler *file_handler, const char *serial); +void file_handler_destroy(struct file_handler *file_handler); + +SDL_bool file_handler_start(struct file_handler *file_handler); +void file_handler_stop(struct file_handler *file_handler); +void file_handler_join(struct file_handler *file_handler); + +SDL_bool file_handler_do(struct file_handler *file_handler, const char *filename); + +#endif diff --git a/app/src/installer.c b/app/src/installer.c deleted file mode 100644 index 5b9e6637..00000000 --- a/app/src/installer.c +++ /dev/null @@ -1,182 +0,0 @@ -#include "installer.h" - -#include -#include "command.h" -#include "lockutil.h" -#include "log.h" - -// NOTE(adopi) this can be more generic: -// it could be used with a command queue instead of a filename queue -// then we would have a generic invoker (useful if we want to handle more async commands) - -SDL_bool apk_queue_is_empty(const struct apk_queue *queue) { - return queue->head == queue->tail; -} - -SDL_bool apk_queue_is_full(const struct apk_queue *queue) { - return (queue->head + 1) % APK_QUEUE_SIZE == queue->tail; -} - -SDL_bool apk_queue_init(struct apk_queue *queue) { - queue->head = 0; - queue->tail = 0; - return SDL_TRUE; -} - -void apk_queue_destroy(struct apk_queue *queue) { - int i = queue->tail; - while (i != queue->head) { - SDL_free(queue->data[i]); - i = (i + 1) % APK_QUEUE_SIZE; - } -} - -SDL_bool apk_queue_push(struct apk_queue *queue, const char *apk) { - if (apk_queue_is_full(queue)) { - return SDL_FALSE; - } - queue->data[queue->head] = SDL_strdup(apk); - queue->head = (queue->head + 1) % APK_QUEUE_SIZE; - return SDL_TRUE; -} - -SDL_bool apk_queue_take(struct apk_queue *queue, char **apk) { - if (apk_queue_is_empty(queue)) { - return SDL_FALSE; - } - // transfer ownership - *apk = queue->data[queue->tail]; - queue->tail = (queue->tail + 1) % APK_QUEUE_SIZE; - return SDL_TRUE; -} - -SDL_bool installer_init(struct installer *installer, const char *serial) { - - if (!apk_queue_init(&installer->queue)) { - return SDL_FALSE; - } - - if (!(installer->mutex = SDL_CreateMutex())) { - return SDL_FALSE; - } - - if (!(installer->event_cond = SDL_CreateCond())) { - SDL_DestroyMutex(installer->mutex); - return SDL_FALSE; - } - - if (serial) { - installer->serial = SDL_strdup(serial); - if (!installer->serial) { - LOGW("Cannot strdup serial"); - SDL_DestroyMutex(installer->mutex); - return SDL_FALSE; - } - } else { - installer->serial = NULL; - } - - // lazy initialization - installer->initialized = SDL_FALSE; - - installer->stopped = SDL_FALSE; - installer->current_process = PROCESS_NONE; - - return SDL_TRUE; -} - -void installer_destroy(struct installer *installer) { - SDL_DestroyCond(installer->event_cond); - SDL_DestroyMutex(installer->mutex); - apk_queue_destroy(&installer->queue); - SDL_free((void *) installer->serial); -} - -SDL_bool installer_install_apk(struct installer *installer, const char *apk) { - SDL_bool res; - - // start installer if it's used for the first time - if (!installer->initialized) { - if (!installer_start(installer)) { - return SDL_FALSE; - } - installer->initialized = SDL_TRUE; - } - - mutex_lock(installer->mutex); - SDL_bool was_empty = apk_queue_is_empty(&installer->queue); - res = apk_queue_push(&installer->queue, apk); - if (was_empty) { - cond_signal(installer->event_cond); - } - mutex_unlock(installer->mutex); - return res; -} - -static int run_installer(void *data) { - struct installer *installer = data; - - for (;;) { - mutex_lock(installer->mutex); - installer->current_process = PROCESS_NONE; - while (!installer->stopped && apk_queue_is_empty(&installer->queue)) { - cond_wait(installer->event_cond, installer->mutex); - } - if (installer->stopped) { - // stop immediately, do not process further events - mutex_unlock(installer->mutex); - break; - } - char *current_apk; -#ifdef BUILD_DEBUG - bool non_empty = apk_queue_take(&installer->queue, ¤t_apk); - SDL_assert(non_empty); -#else - apk_queue_take(&installer->queue, ¤t_apk); -#endif - - LOGI("Installing %s...", current_apk); - process_t process = adb_install(installer->serial, current_apk); - installer->current_process = process; - - mutex_unlock(installer->mutex); - - if (process_check_success(process, "adb install")) { - LOGI("%s installed successfully", current_apk); - } else { - LOGE("Failed to install %s", current_apk); - } - SDL_free(current_apk); - } - return 0; -} - -SDL_bool installer_start(struct installer *installer) { - LOGD("Starting installer thread"); - - installer->thread = SDL_CreateThread(run_installer, "installer", installer); - if (!installer->thread) { - LOGC("Could not start installer thread"); - return SDL_FALSE; - } - - return SDL_TRUE; -} - -void installer_stop(struct installer *installer) { - mutex_lock(installer->mutex); - installer->stopped = SDL_TRUE; - cond_signal(installer->event_cond); - if (installer->current_process != PROCESS_NONE) { - if (!cmd_terminate(installer->current_process)) { - LOGW("Cannot terminate install process"); - } - cmd_simple_wait(installer->current_process, NULL); - installer->current_process = PROCESS_NONE; - } - mutex_unlock(installer->mutex); -} - -void installer_join(struct installer *installer) { - SDL_WaitThread(installer->thread, NULL); -} diff --git a/app/src/installer.h b/app/src/installer.h deleted file mode 100644 index 0ff5f380..00000000 --- a/app/src/installer.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef APK_INSTALLER_H -#define APK_INSTALLER_H - -#include -#include -#include -#include "command.h" - -#define APK_QUEUE_SIZE 16 - -// NOTE(AdoPi) apk_queue and control_event can use a generic queue - -struct apk_queue { - char *data[APK_QUEUE_SIZE]; - int tail; - int head; -}; - -struct installer { - const char *serial; - SDL_Thread *thread; - SDL_mutex *mutex; - SDL_cond *event_cond; - SDL_bool stopped; - SDL_bool initialized; - process_t current_process; - struct apk_queue queue; -}; - -SDL_bool installer_init(struct installer *installer, const char *serial); -void installer_destroy(struct installer *installer); - -SDL_bool installer_start(struct installer *installer); -void installer_stop(struct installer *installer); -void installer_join(struct installer *installer); - -// install an apk -SDL_bool installer_install_apk(struct installer *installer, const char *filename); - -#endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 23de4984..c0efa1f4 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -13,6 +13,7 @@ #include "decoder.h" #include "device.h" #include "events.h" +#include "file_handler.h" #include "frames.h" #include "fpscounter.h" #include "inputmanager.h" @@ -22,14 +23,13 @@ #include "screen.h" #include "server.h" #include "tinyxpm.h" -#include "installer.h" static struct server server = SERVER_INITIALIZER; static struct screen screen = SCREEN_INITIALIZER; static struct frames frames; static struct decoder decoder; static struct controller controller; -static struct installer installer; +static struct file_handler file_handler; static struct input_manager input_manager = { .controller = &controller, @@ -105,7 +105,7 @@ static SDL_bool event_loop(void) { input_manager_process_mouse_button(&input_manager, &event.button); break; case SDL_DROPFILE: - installer_install_apk(&installer, event.drop.file); + file_handler_do(&file_handler, event.drop.file); break; } } @@ -175,7 +175,7 @@ SDL_bool scrcpy(const struct scrcpy_options *options) { goto finally_destroy_server; } - if (!installer_init(&installer, server.serial)) { + if (!file_handler_init(&file_handler, server.serial)) { ret = SDL_FALSE; server_stop(&server); goto finally_destroy_frames; @@ -188,7 +188,7 @@ SDL_bool scrcpy(const struct scrcpy_options *options) { if (!decoder_start(&decoder)) { ret = SDL_FALSE; server_stop(&server); - goto finally_destroy_installer; + goto finally_destroy_file_handler; } if (!controller_init(&controller, device_socket)) { @@ -226,10 +226,10 @@ finally_stop_decoder: // stop the server before decoder_join() to wake up the decoder server_stop(&server); decoder_join(&decoder); -finally_destroy_installer: - installer_stop(&installer); - installer_join(&installer); - installer_destroy(&installer); +finally_destroy_file_handler: + file_handler_stop(&file_handler); + file_handler_join(&file_handler); + file_handler_destroy(&file_handler); finally_destroy_frames: frames_destroy(&frames); finally_destroy_server: