diff --git a/app/meson.build b/app/meson.build index b74b75fa79..c09d772dc9 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 d01d6ed294..125dda3a91 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 0000000000..4dc23ce355 --- /dev/null +++ b/app/src/file_handler.c @@ -0,0 +1,210 @@ +#include "file_handler.h" + +#include +#include "command.h" +#include "device.h" +#include "lockutil.h" +#include "log.h" + +void request_free(struct request *req) { + if (!req) { + return; + } + SDL_free(req->file); + free(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_add_request(struct file_handler *file_handler, const char *file, req_action_t action) { + SDL_bool res; + struct request *req = (struct request *) malloc(sizeof(struct request)); + + // 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("Adding request %s", file); + req->file = SDL_strdup(file); + req->action = action; + if (action == INSTALL_APK) { + req->func = install_apk; + } else { + req->func = push_file; + } + + 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); + 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; + process_t process; + const char *cmd; +#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 + LOGI("Processing %s...", req->file); + process = req->func(file_handler->serial, req->file); + file_handler->current_process = process; + mutex_unlock(file_handler->mutex); + + if (req->action == INSTALL_APK) { + cmd = "adb install"; + } else { + cmd = "adb push"; + } + + if (process_check_success(process, cmd)) { + LOGI("Process %s successfully", req->file); + } else { + LOGE("Failed to process %s", 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 0000000000..39dbf02a97 --- /dev/null +++ b/app/src/file_handler.h @@ -0,0 +1,48 @@ +#ifndef FILE_HANDLER_H +#define FILE_HADNELR_H + +#include +#include +#include +#include "command.h" + +#define REQUEST_QUEUE_SIZE 16 + +typedef enum { + INSTALL_APK = 0, + PUSH_FILE, +} req_action_t; + +struct request { + process_t (*func)(const char *, const char *); + char *file; + req_action_t action; +}; + +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_add_request(struct file_handler *file_handler, const char *filename, req_action_t action); + +#endif diff --git a/app/src/installer.c b/app/src/installer.c deleted file mode 100644 index b47892975d..0000000000 --- a/app/src/installer.c +++ /dev/null @@ -1,181 +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); - 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 0ff5f3803b..0000000000 --- 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 1cc6ee879a..fc5377f5ca 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 int is_apk(const char *file) { + const char *ext = strrchr(file, '.'); + return ext && !strcmp(ext, ".apk"); +} + static void event_loop(void) { #ifdef CONTINUOUS_RESIZING_WORKAROUND SDL_AddEventWatch(event_watcher, NULL); @@ -105,7 +110,11 @@ static void event_loop(void) { input_manager_process_mouse_button(&input_manager, &event.button); break; case SDL_DROPFILE: - installer_install_apk(&installer, event.drop.file); + if (is_apk(event.drop.file)) { + file_handler_add_request(&file_handler, event.drop.file, INSTALL_APK); + } else { + file_handler_add_request(&file_handler, event.drop.file, PUSH_FILE); + } break; } } @@ -174,7 +183,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; @@ -187,7 +196,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)) { @@ -225,10 +234,10 @@ SDL_bool scrcpy(const struct scrcpy_options *options) { // 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: