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/device.h b/app/src/device.h index d01d6ed2..125dda3a 100644 --- a/app/src/device.h +++ b/app/src/device.h @@ -7,6 +7,7 @@ #include "net.h" #define DEVICE_NAME_FIELD_LENGTH 64 +#define DEVICE_SDCARD_PATH "/sdcard/" // name must be at least DEVICE_NAME_FIELD_LENGTH bytes SDL_bool device_read_info(socket_t device_socket, char *name, struct size *frame_size); diff --git a/app/src/file_handler.c b/app/src/file_handler.c new file mode 100644 index 00000000..39175d55 --- /dev/null +++ b/app/src/file_handler.c @@ -0,0 +1,232 @@ +#include "file_handler.h" + +#include +#include "command.h" +#include "device.h" +#include "lockutil.h" +#include "log.h" + +struct request { + file_handler_action_t action; + const char *file; +}; + +static struct request *request_new(file_handler_action_t action, const char *file) { + struct request *req = SDL_malloc(sizeof(*req)); + if (!req) { + return NULL; + } + req->action = action; + req->file = file; + return req; +} + +static void request_free(struct request *req) { + if (!req) { + return; + } + SDL_free((void *) req->file); + SDL_free((void *) req); +} + +SDL_bool request_queue_is_empty(const struct request_queue *queue) { + return queue->head == queue->tail; +} + +SDL_bool request_queue_is_full(const struct request_queue *queue) { + return (queue->head + 1) % REQUEST_QUEUE_SIZE == queue->tail; +} + +SDL_bool request_queue_init(struct request_queue *queue) { + queue->head = 0; + queue->tail = 0; + return SDL_TRUE; +} + +void request_queue_destroy(struct request_queue *queue) { + int i = queue->tail; + while (i != queue->head) { + request_free(queue->reqs[i]); + i = (i + 1) % REQUEST_QUEUE_SIZE; + } +} + +SDL_bool request_queue_push(struct request_queue *queue, struct request *req) { + if (request_queue_is_full(queue)) { + return SDL_FALSE; + } + queue->reqs[queue->head] = req; + queue->head = (queue->head + 1) % REQUEST_QUEUE_SIZE; + return SDL_TRUE; +} + +SDL_bool request_queue_take(struct request_queue *queue, struct request **req) { + if (request_queue_is_empty(queue)) { + return SDL_FALSE; + } + // transfer ownership + *req = queue->reqs[queue->tail]; + queue->tail = (queue->tail + 1) % REQUEST_QUEUE_SIZE; + return SDL_TRUE; +} + +SDL_bool file_handler_init(struct file_handler *file_handler, const char *serial) { + + if (!request_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); + request_queue_destroy(&file_handler->queue); + SDL_free((void *) file_handler->serial); +} + +static process_t install_apk(const char *serial, const char *file) { + return adb_install(serial, file); +} + +static process_t push_file(const char *serial, const char *file) { + return adb_push(serial, file, DEVICE_SDCARD_PATH); +} + +SDL_bool file_handler_request(struct file_handler *file_handler, + file_handler_action_t action, + 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; + } + + LOGI("Request to %s %s", action == ACTION_INSTALL_APK ? "install" : "push", file); + struct request *req = request_new(action, file); + if (!req) { + LOGE("Could not create request"); + return SDL_FALSE; + } + + mutex_lock(file_handler->mutex); + SDL_bool was_empty = request_queue_is_empty(&file_handler->queue); + res = request_queue_push(&file_handler->queue, req); + 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 && request_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; + } + struct request *req; +#ifdef BUILD_DEBUG + bool non_empty = request_queue_take(&file_handler->queue, &req); + SDL_assert(non_empty); +#else + request_queue_take(&file_handler->queue, &req); +#endif + process_t process; + if (req->action == ACTION_INSTALL_APK) { + LOGI("Installing %s...", req->file); + process = install_apk(file_handler->serial, req->file); + } else { + LOGI("Pushing %s...", req->file); + process = push_file(file_handler->serial, req->file); + } + file_handler->current_process = process; + mutex_unlock(file_handler->mutex); + + if (req->action == ACTION_INSTALL_APK) { + if (process_check_success(process, "adb install")) { + LOGI("%s successfully installed", req->file); + } else { + LOGE("Failed to install %s", req->file); + } + } else { + if (process_check_success(process, "adb push")) { + LOGI("%s successfully pushed to /sdcard/", req->file); + } else { + LOGE("Failed to push %s to /sdcard/", req->file); + } + } + + request_free(req); + } + 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..375db297 --- /dev/null +++ b/app/src/file_handler.h @@ -0,0 +1,44 @@ +#ifndef FILE_HANDLER_H +#define FILE_HADNELR_H + +#include +#include +#include +#include "command.h" + +#define REQUEST_QUEUE_SIZE 16 + +typedef enum { + ACTION_INSTALL_APK, + ACTION_PUSH_FILE, +} file_handler_action_t; + +struct request_queue { + struct request *reqs[REQUEST_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 request_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_request(struct file_handler *file_handler, + file_handler_action_t action, + const char *file); + +#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..cee4e123 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, @@ -56,6 +56,11 @@ static int event_watcher(void *data, SDL_Event *event) { } #endif +static SDL_bool is_apk(const char *file) { + const char *ext = strrchr(file, '.'); + return ext && !strcmp(ext, ".apk"); +} + static SDL_bool event_loop(void) { #ifdef CONTINUOUS_RESIZING_WORKAROUND SDL_AddEventWatch(event_watcher, NULL); @@ -104,9 +109,16 @@ static SDL_bool event_loop(void) { case SDL_MOUSEBUTTONUP: input_manager_process_mouse_button(&input_manager, &event.button); break; - case SDL_DROPFILE: - installer_install_apk(&installer, event.drop.file); + case SDL_DROPFILE: { + file_handler_action_t action; + if (is_apk(event.drop.file)) { + action = ACTION_INSTALL_APK; + } else { + action = ACTION_PUSH_FILE; + } + file_handler_request(&file_handler, action, event.drop.file); break; + } } } return SDL_FALSE; @@ -175,7 +187,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 +200,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 +238,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: