From aa97eed24b7571c1c30026528eceba15f382a862 Mon Sep 17 00:00:00 2001 From: npes87184 Date: Sun, 12 Aug 2018 10:13:49 +0800 Subject: [PATCH 01/24] installer -> file_handler Signed-off-by: npes87184 --- app/meson.build | 2 +- app/src/file_handler.c | 181 ++++++++++++++++++++++++++++++++++++++++ app/src/file_handler.h | 39 +++++++++ app/src/installer.c | 182 ----------------------------------------- app/src/installer.h | 40 --------- app/src/scrcpy.c | 18 ++-- 6 files changed, 230 insertions(+), 232 deletions(-) create mode 100644 app/src/file_handler.c create mode 100644 app/src/file_handler.h delete mode 100644 app/src/installer.c delete mode 100644 app/src/installer.h 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: From 66f45f9dae6a9c1a6845c4c87377c9bc235edd7f Mon Sep 17 00:00:00 2001 From: npes87184 Date: Sun, 12 Aug 2018 10:40:00 +0800 Subject: [PATCH 02/24] Support drag&drop a file to transfer it to device Signed-off-by: npes87184 --- app/src/device.h | 1 + app/src/file_handler.c | 119 +++++++++++++++++++++++++++++------------ app/src/file_handler.h | 17 +++--- app/src/scrcpy.c | 16 +++++- 4 files changed, 111 insertions(+), 42 deletions(-) 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 index 2820c825..39175d55 100644 --- a/app/src/file_handler.c +++ b/app/src/file_handler.c @@ -2,57 +2,77 @@ #include #include "command.h" +#include "device.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) +struct request { + file_handler_action_t action; + const char *file; +}; -SDL_bool file_queue_is_empty(const struct file_queue *queue) { +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 file_queue_is_full(const struct file_queue *queue) { - return (queue->head + 1) % FILE_QUEUE_SIZE == queue->tail; +SDL_bool request_queue_is_full(const struct request_queue *queue) { + return (queue->head + 1) % REQUEST_QUEUE_SIZE == queue->tail; } -SDL_bool file_queue_init(struct file_queue *queue) { +SDL_bool request_queue_init(struct request_queue *queue) { queue->head = 0; queue->tail = 0; return SDL_TRUE; } -void file_queue_destroy(struct file_queue *queue) { +void request_queue_destroy(struct request_queue *queue) { int i = queue->tail; while (i != queue->head) { - SDL_free(queue->data[i]); - i = (i + 1) % FILE_QUEUE_SIZE; + request_free(queue->reqs[i]); + i = (i + 1) % REQUEST_QUEUE_SIZE; } } -SDL_bool file_queue_push(struct file_queue *queue, const char *file) { - if (file_queue_is_full(queue)) { +SDL_bool request_queue_push(struct request_queue *queue, struct request *req) { + if (request_queue_is_full(queue)) { return SDL_FALSE; } - queue->data[queue->head] = SDL_strdup(file); - queue->head = (queue->head + 1) % FILE_QUEUE_SIZE; + queue->reqs[queue->head] = req; + queue->head = (queue->head + 1) % REQUEST_QUEUE_SIZE; return SDL_TRUE; } -SDL_bool file_queue_take(struct file_queue *queue, char **file) { - if (file_queue_is_empty(queue)) { +SDL_bool request_queue_take(struct request_queue *queue, struct request **req) { + if (request_queue_is_empty(queue)) { return SDL_FALSE; } // transfer ownership - *file = queue->data[queue->tail]; - queue->tail = (queue->tail + 1) % FILE_QUEUE_SIZE; + *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 (!file_queue_init(&file_handler->queue)) { + if (!request_queue_init(&file_handler->queue)) { return SDL_FALSE; } @@ -88,11 +108,21 @@ SDL_bool file_handler_init(struct file_handler *file_handler, const char *serial 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); + request_queue_destroy(&file_handler->queue); SDL_free((void *) file_handler->serial); } -SDL_bool file_handler_do(struct file_handler *file_handler, const char *file) { +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 @@ -103,9 +133,16 @@ SDL_bool file_handler_do(struct file_handler *file_handler, const char *file) { 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 = file_queue_is_empty(&file_handler->queue); - res = file_queue_push(&file_handler->queue, file); + 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); } @@ -119,7 +156,7 @@ static int run_file_handler(void *data) { for (;;) { mutex_lock(file_handler->mutex); file_handler->current_process = PROCESS_NONE; - while (!file_handler->stopped && file_queue_is_empty(&file_handler->queue)) { + while (!file_handler->stopped && request_queue_is_empty(&file_handler->queue)) { cond_wait(file_handler->event_cond, file_handler->mutex); } if (file_handler->stopped) { @@ -127,25 +164,39 @@ static int run_file_handler(void *data) { mutex_unlock(file_handler->mutex); break; } - char *current_apk; + struct request *req; #ifdef BUILD_DEBUG - bool non_empty = file_queue_take(&file_handler->queue, ¤t_apk); + bool non_empty = request_queue_take(&file_handler->queue, &req); SDL_assert(non_empty); #else - file_queue_take(&file_handler->queue, ¤t_apk); + request_queue_take(&file_handler->queue, &req); #endif - LOGI("Installing %s...", current_apk); - process_t process = adb_install(file_handler->serial, current_apk); + 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 (process_check_success(process, "adb install")) { - LOGI("%s installed successfully", current_apk); + 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 { - LOGE("Failed to install %s", current_apk); + 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); + } } - SDL_free(current_apk); + + request_free(req); } return 0; } diff --git a/app/src/file_handler.h b/app/src/file_handler.h index 9cee9d3e..375db297 100644 --- a/app/src/file_handler.h +++ b/app/src/file_handler.h @@ -6,12 +6,15 @@ #include #include "command.h" -#define FILE_QUEUE_SIZE 16 +#define REQUEST_QUEUE_SIZE 16 -// NOTE(AdoPi) file_queue and control_event can use a generic queue +typedef enum { + ACTION_INSTALL_APK, + ACTION_PUSH_FILE, +} file_handler_action_t; -struct file_queue { - char *data[FILE_QUEUE_SIZE]; +struct request_queue { + struct request *reqs[REQUEST_QUEUE_SIZE]; int tail; int head; }; @@ -24,7 +27,7 @@ struct file_handler { SDL_bool stopped; SDL_bool initialized; process_t current_process; - struct file_queue queue; + struct request_queue queue; }; SDL_bool file_handler_init(struct file_handler *file_handler, const char *serial); @@ -34,6 +37,8 @@ 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); +SDL_bool file_handler_request(struct file_handler *file_handler, + file_handler_action_t action, + const char *file); #endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index c0efa1f4..cee4e123 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -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: - file_handler_do(&file_handler, 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; From 4527be4cde9fddfd8debcb2bcc15d81e52512ca6 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 16:45:22 +0200 Subject: [PATCH 03/24] Add missing include config.h When config.h is not included, BUILD_DEBUG is not set. --- app/src/controller.c | 4 +++- app/src/file_handler.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/controller.c b/app/src/controller.c index 468885f6..f698ebc8 100644 --- a/app/src/controller.c +++ b/app/src/controller.c @@ -1,5 +1,7 @@ #include "controller.h" +#include +#include "config.h" #include "lockutil.h" #include "log.h" @@ -66,7 +68,7 @@ static int run_controller(void *data) { } struct control_event event; #ifdef BUILD_DEBUG - bool non_empty = control_event_queue_take(&controller->queue, &event); + SDL_bool non_empty = control_event_queue_take(&controller->queue, &event); SDL_assert(non_empty); #else control_event_queue_take(&controller->queue, &event); diff --git a/app/src/file_handler.c b/app/src/file_handler.c index 39175d55..f47372c1 100644 --- a/app/src/file_handler.c +++ b/app/src/file_handler.c @@ -1,6 +1,8 @@ #include "file_handler.h" #include +#include +#include "config.h" #include "command.h" #include "device.h" #include "lockutil.h" @@ -166,7 +168,7 @@ static int run_file_handler(void *data) { } struct request *req; #ifdef BUILD_DEBUG - bool non_empty = request_queue_take(&file_handler->queue, &req); + SDL_bool non_empty = request_queue_take(&file_handler->queue, &req); SDL_assert(non_empty); #else request_queue_take(&file_handler->queue, &req); From 359685b1db8f355a6d7ade3a99989b03558ff907 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 16:46:53 +0200 Subject: [PATCH 04/24] Simplify SDL_assert() calls SDL_assert() already prevents "unused variable" warnings. --- app/src/controller.c | 4 ---- app/src/file_handler.c | 5 +---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/src/controller.c b/app/src/controller.c index f698ebc8..d4919c56 100644 --- a/app/src/controller.c +++ b/app/src/controller.c @@ -67,12 +67,8 @@ static int run_controller(void *data) { break; } struct control_event event; -#ifdef BUILD_DEBUG SDL_bool non_empty = control_event_queue_take(&controller->queue, &event); SDL_assert(non_empty); -#else - control_event_queue_take(&controller->queue, &event); -#endif mutex_unlock(controller->mutex); SDL_bool ok = process_event(controller, &event); diff --git a/app/src/file_handler.c b/app/src/file_handler.c index f47372c1..bf68b0f8 100644 --- a/app/src/file_handler.c +++ b/app/src/file_handler.c @@ -167,12 +167,9 @@ static int run_file_handler(void *data) { break; } struct request *req; -#ifdef BUILD_DEBUG SDL_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); From 6581f9feb9e0b4cc0c33bca15492166b0c8e0702 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 16:51:05 +0200 Subject: [PATCH 05/24] Make request_queue functions static These functions are local to file_handler.c. --- app/src/file_handler.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/file_handler.c b/app/src/file_handler.c index bf68b0f8..24741b28 100644 --- a/app/src/file_handler.c +++ b/app/src/file_handler.c @@ -31,21 +31,21 @@ static void request_free(struct request *req) { SDL_free((void *) req); } -SDL_bool request_queue_is_empty(const struct request_queue *queue) { +static 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) { +static 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) { +static 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) { +static void request_queue_destroy(struct request_queue *queue) { int i = queue->tail; while (i != queue->head) { request_free(queue->reqs[i]); @@ -53,7 +53,7 @@ void request_queue_destroy(struct request_queue *queue) { } } -SDL_bool request_queue_push(struct request_queue *queue, struct request *req) { +static SDL_bool request_queue_push(struct request_queue *queue, struct request *req) { if (request_queue_is_full(queue)) { return SDL_FALSE; } @@ -62,7 +62,7 @@ SDL_bool request_queue_push(struct request_queue *queue, struct request *req) { return SDL_TRUE; } -SDL_bool request_queue_take(struct request_queue *queue, struct request **req) { +static SDL_bool request_queue_take(struct request_queue *queue, struct request **req) { if (request_queue_is_empty(queue)) { return SDL_FALSE; } From f3f704d1ed574477c744e01ae14673cca111d24e Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 16:52:29 +0200 Subject: [PATCH 06/24] Document "push file" feature Document how to push a file to /sdcard/ in the shortcuts list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6eb0e974..08e47344 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,7 @@ To run without installing: | paste computer clipboard to device | `Ctrl`+`v` | | enable/disable FPS counter (on stdout) | `Ctrl`+`i` | | install APK from computer | drag & drop APK file | + | push file to `/sdcard/` | drag & drop non-APK file | _¹Double-click on black borders to remove them._ _²Right-click turns the screen on if it was off, presses BACK otherwise._ From 536b31829ab400277c30088c3b61827ebb99ca03 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 17:01:54 +0200 Subject: [PATCH 07/24] Separate multi-words filenames by '_' Rename foobar.ext to foo_bar.ext. --- app/meson.build | 18 +++++++++--------- app/src/{controlevent.c => control_event.c} | 4 ++-- app/src/{controlevent.h => control_event.h} | 0 app/src/controller.c | 2 +- app/src/controller.h | 2 +- app/src/convert.h | 2 +- app/src/decoder.c | 2 +- app/src/file_handler.c | 2 +- app/src/{fpscounter.c => fps_counter.c} | 2 +- app/src/{fpscounter.h => fps_counter.h} | 0 app/src/frames.c | 2 +- app/src/frames.h | 2 +- app/src/{inputmanager.c => input_manager.c} | 4 ++-- app/src/{inputmanager.h => input_manager.h} | 2 +- app/src/{lockutil.c => lock_util.c} | 0 app/src/{lockutil.h => lock_util.h} | 0 app/src/scrcpy.c | 8 ++++---- app/src/screen.c | 4 ++-- app/src/{strutil.c => str_util.c} | 2 +- app/src/{strutil.h => str_util.h} | 0 app/src/sys/win/command.c | 2 +- app/src/{tinyxpm.c => tiny_xpm.c} | 2 +- app/src/{tinyxpm.h => tiny_xpm.h} | 0 app/tests/test_control_event_queue.c | 2 +- app/tests/test_control_event_serialize.c | 2 +- app/tests/test_strutil.c | 2 +- 26 files changed, 34 insertions(+), 34 deletions(-) rename app/src/{controlevent.c => control_event.c} (98%) rename app/src/{controlevent.h => control_event.h} (100%) rename app/src/{fpscounter.c => fps_counter.c} (98%) rename app/src/{fpscounter.h => fps_counter.h} (100%) rename app/src/{inputmanager.c => input_manager.c} (99%) rename app/src/{inputmanager.h => input_manager.h} (97%) rename app/src/{lockutil.c => lock_util.c} (100%) rename app/src/{lockutil.h => lock_util.h} (100%) rename app/src/{strutil.c => str_util.c} (97%) rename app/src/{strutil.h => str_util.h} (100%) rename app/src/{tinyxpm.c => tiny_xpm.c} (99%) rename app/src/{tinyxpm.h => tiny_xpm.h} (100%) diff --git a/app/meson.build b/app/meson.build index c09d772d..f2f1e339 100644 --- a/app/meson.build +++ b/app/meson.build @@ -1,22 +1,22 @@ src = [ 'src/main.c', 'src/command.c', - 'src/controlevent.c', + 'src/control_event.c', 'src/controller.c', 'src/convert.c', 'src/decoder.c', 'src/device.c', 'src/file_handler.c', - 'src/fpscounter.c', + 'src/fps_counter.c', 'src/frames.c', - 'src/inputmanager.c', - 'src/lockutil.c', + 'src/input_manager.c', + 'src/lock_util.c', 'src/net.c', 'src/scrcpy.c', 'src/screen.c', 'src/server.c', - 'src/strutil.c', - 'src/tinyxpm.c', + 'src/str_util.c', + 'src/tiny_xpm.c', ] if not get_option('crossbuild_windows') @@ -147,9 +147,9 @@ executable('scrcpy', src, dependencies: dependencies, include_directories: src_d ### TESTS tests = [ - ['test_control_event_queue', ['tests/test_control_event_queue.c', 'src/controlevent.c']], - ['test_control_event_serialize', ['tests/test_control_event_serialize.c', 'src/controlevent.c']], - ['test_strutil', ['tests/test_strutil.c', 'src/strutil.c']], + ['test_control_event_queue', ['tests/test_control_event_queue.c', 'src/control_event.c']], + ['test_control_event_serialize', ['tests/test_control_event_serialize.c', 'src/control_event.c']], + ['test_strutil', ['tests/test_strutil.c', 'src/str_util.c']], ] foreach t : tests diff --git a/app/src/controlevent.c b/app/src/control_event.c similarity index 98% rename from app/src/controlevent.c rename to app/src/control_event.c index edb7a1a2..78b6a4c6 100644 --- a/app/src/controlevent.c +++ b/app/src/control_event.c @@ -1,9 +1,9 @@ -#include "controlevent.h" +#include "control_event.h" #include #include -#include "lockutil.h" +#include "lock_util.h" #include "log.h" static inline void write16(Uint8 *buf, Uint16 value) { diff --git a/app/src/controlevent.h b/app/src/control_event.h similarity index 100% rename from app/src/controlevent.h rename to app/src/control_event.h diff --git a/app/src/controller.c b/app/src/controller.c index d4919c56..f659d4b9 100644 --- a/app/src/controller.c +++ b/app/src/controller.c @@ -2,7 +2,7 @@ #include #include "config.h" -#include "lockutil.h" +#include "lock_util.h" #include "log.h" SDL_bool controller_init(struct controller *controller, socket_t video_socket) { diff --git a/app/src/controller.h b/app/src/controller.h index 7c22fe07..08d639a6 100644 --- a/app/src/controller.h +++ b/app/src/controller.h @@ -1,7 +1,7 @@ #ifndef CONTROL_H #define CONTROL_H -#include "controlevent.h" +#include "control_event.h" #include #include diff --git a/app/src/convert.h b/app/src/convert.h index 5a535a37..30f0dd3d 100644 --- a/app/src/convert.h +++ b/app/src/convert.h @@ -3,7 +3,7 @@ #include #include -#include "controlevent.h" +#include "control_event.h" struct complete_mouse_motion_event { SDL_MouseMotionEvent *mouse_motion_event; diff --git a/app/src/decoder.c b/app/src/decoder.c index 94dc9401..9e90e4b7 100644 --- a/app/src/decoder.c +++ b/app/src/decoder.c @@ -9,7 +9,7 @@ #include "config.h" #include "events.h" #include "frames.h" -#include "lockutil.h" +#include "lock_util.h" #include "log.h" #define BUFSIZE 0x10000 diff --git a/app/src/file_handler.c b/app/src/file_handler.c index 24741b28..d1e0fd7b 100644 --- a/app/src/file_handler.c +++ b/app/src/file_handler.c @@ -5,7 +5,7 @@ #include "config.h" #include "command.h" #include "device.h" -#include "lockutil.h" +#include "lock_util.h" #include "log.h" struct request { diff --git a/app/src/fpscounter.c b/app/src/fps_counter.c similarity index 98% rename from app/src/fpscounter.c rename to app/src/fps_counter.c index 4cb512f3..27aa4ee0 100644 --- a/app/src/fpscounter.c +++ b/app/src/fps_counter.c @@ -1,4 +1,4 @@ -#include "fpscounter.h" +#include "fps_counter.h" #include diff --git a/app/src/fpscounter.h b/app/src/fps_counter.h similarity index 100% rename from app/src/fpscounter.h rename to app/src/fps_counter.h diff --git a/app/src/frames.c b/app/src/frames.c index 27fcc1fb..514d4788 100644 --- a/app/src/frames.c +++ b/app/src/frames.c @@ -6,7 +6,7 @@ #include #include "config.h" -#include "lockutil.h" +#include "lock_util.h" #include "log.h" SDL_bool frames_init(struct frames *frames) { diff --git a/app/src/frames.h b/app/src/frames.h index 7a52d2c8..437838fc 100644 --- a/app/src/frames.h +++ b/app/src/frames.h @@ -5,7 +5,7 @@ #include #include "config.h" -#include "fpscounter.h" +#include "fps_counter.h" // forward declarations typedef struct AVFrame AVFrame; diff --git a/app/src/inputmanager.c b/app/src/input_manager.c similarity index 99% rename from app/src/inputmanager.c rename to app/src/input_manager.c index 9dd95264..797fd764 100644 --- a/app/src/inputmanager.c +++ b/app/src/input_manager.c @@ -1,7 +1,7 @@ -#include "inputmanager.h" +#include "input_manager.h" #include "convert.h" -#include "lockutil.h" +#include "lock_util.h" #include "log.h" // Convert window coordinates (as provided by SDL_GetMouseState() to renderer coordinates (as provided in SDL mouse events) diff --git a/app/src/inputmanager.h b/app/src/input_manager.h similarity index 97% rename from app/src/inputmanager.h rename to app/src/input_manager.h index d7eaccbc..b9037aa1 100644 --- a/app/src/inputmanager.h +++ b/app/src/input_manager.h @@ -3,7 +3,7 @@ #include "common.h" #include "controller.h" -#include "fpscounter.h" +#include "fps_counter.h" #include "frames.h" #include "screen.h" diff --git a/app/src/lockutil.c b/app/src/lock_util.c similarity index 100% rename from app/src/lockutil.c rename to app/src/lock_util.c diff --git a/app/src/lockutil.h b/app/src/lock_util.h similarity index 100% rename from app/src/lockutil.h rename to app/src/lock_util.h diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index cee4e123..3eaa45c0 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -15,14 +15,14 @@ #include "events.h" #include "file_handler.h" #include "frames.h" -#include "fpscounter.h" -#include "inputmanager.h" +#include "fps_counter.h" +#include "input_manager.h" #include "log.h" -#include "lockutil.h" +#include "lock_util.h" #include "net.h" #include "screen.h" #include "server.h" -#include "tinyxpm.h" +#include "tiny_xpm.h" static struct server server = SERVER_INITIALIZER; static struct screen screen = SCREEN_INITIALIZER; diff --git a/app/src/screen.c b/app/src/screen.c index 5b4f0634..5d7a4009 100644 --- a/app/src/screen.c +++ b/app/src/screen.c @@ -4,9 +4,9 @@ #include #include "icon.xpm" -#include "lockutil.h" +#include "lock_util.h" #include "log.h" -#include "tinyxpm.h" +#include "tiny_xpm.h" #define DISPLAY_MARGINS 96 diff --git a/app/src/strutil.c b/app/src/str_util.c similarity index 97% rename from app/src/strutil.c rename to app/src/str_util.c index 0b5ac12d..0e090403 100644 --- a/app/src/strutil.c +++ b/app/src/str_util.c @@ -1,4 +1,4 @@ -#include "strutil.h" +#include "str_util.h" size_t xstrncpy(char *dest, const char *src, size_t n) { size_t i; diff --git a/app/src/strutil.h b/app/src/str_util.h similarity index 100% rename from app/src/strutil.h rename to app/src/str_util.h diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index 32d2a8b8..2552eeca 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -2,7 +2,7 @@ #include "config.h" #include "log.h" -#include "strutil.h" +#include "str_util.h" HANDLE cmd_execute(const char *path, const char *const argv[]) { STARTUPINFO si; diff --git a/app/src/tinyxpm.c b/app/src/tiny_xpm.c similarity index 99% rename from app/src/tinyxpm.c rename to app/src/tiny_xpm.c index 949aeeae..3b80b88e 100644 --- a/app/src/tinyxpm.c +++ b/app/src/tiny_xpm.c @@ -1,4 +1,4 @@ -#include "tinyxpm.h" +#include "tiny_xpm.h" #include #include diff --git a/app/src/tinyxpm.h b/app/src/tiny_xpm.h similarity index 100% rename from app/src/tinyxpm.h rename to app/src/tiny_xpm.h diff --git a/app/tests/test_control_event_queue.c b/app/tests/test_control_event_queue.c index dc89fa2d..07e547c1 100644 --- a/app/tests/test_control_event_queue.c +++ b/app/tests/test_control_event_queue.c @@ -1,7 +1,7 @@ #include #include -#include "controlevent.h" +#include "control_event.h" static void test_control_event_queue_empty() { struct control_event_queue queue; diff --git a/app/tests/test_control_event_serialize.c b/app/tests/test_control_event_serialize.c index e15c64bf..ad472776 100644 --- a/app/tests/test_control_event_serialize.c +++ b/app/tests/test_control_event_serialize.c @@ -1,7 +1,7 @@ #include #include -#include "controlevent.h" +#include "control_event.h" static void test_serialize_keycode_event() { struct control_event event = { diff --git a/app/tests/test_strutil.c b/app/tests/test_strutil.c index 6dbc2ff3..b00e01fe 100644 --- a/app/tests/test_strutil.c +++ b/app/tests/test_strutil.c @@ -1,7 +1,7 @@ #include #include -#include "strutil.h" +#include "str_util.h" static void test_xstrncpy_simple() { char s[] = "xxxxxxxxxx"; From 89e02036821a83635a71cf90162d713411530671 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 17:13:39 +0200 Subject: [PATCH 08/24] Add missing include for lock_util.h lock_util.c did not include lock_util.h. This was catched by the gcc option -Wmissing-prototypes. --- app/src/lock_util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/lock_util.c b/app/src/lock_util.c index 67183d71..723d3b13 100644 --- a/app/src/lock_util.c +++ b/app/src/lock_util.c @@ -1,3 +1,4 @@ +#include #include #include From dd3ba685e4a21ff8db8cdcee65acfa1793a485e8 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 17:48:37 +0200 Subject: [PATCH 09/24] Update platform-tools (28.0.0) for Windows Include the last version of adb in Windows releases. Fixes . --- prebuilt-deps/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prebuilt-deps/Makefile b/prebuilt-deps/Makefile index 557cc675..9762f422 100644 --- a/prebuilt-deps/Makefile +++ b/prebuilt-deps/Makefile @@ -35,6 +35,6 @@ prepare-sdl2: SDL2-2.0.8 prepare-adb: - @./prepare-dep https://dl.google.com/android/repository/platform-tools_r27.0.1-windows.zip \ - 880662adfb0d6911ff250b9e13930ae1a4110fc36d5866afd4f8f56d935f7939 \ + @./prepare-dep https://dl.google.com/android/repository/platform-tools_r28.0.0-windows.zip \ + e2c1ec7c8e9b71cf1c8befd3bff91d06b26dd334c3f32b3817e9d46ba260b0e8 \ platform-tools From 3b5e54278ece2edbff2d8823395274f9d3fc7c16 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 15 Aug 2018 17:49:35 +0200 Subject: [PATCH 10/24] Update FFmpeg (4.0.2) for Windows Include the last version of FFmpeg in Windows releases. --- Makefile.CrossWindows | 16 ++++++++-------- cross_win32.txt | 4 ++-- cross_win64.txt | 4 ++-- prebuilt-deps/Makefile | 24 ++++++++++++------------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Makefile.CrossWindows b/Makefile.CrossWindows index 2dd6c192..9beb44e8 100644 --- a/Makefile.CrossWindows +++ b/Makefile.CrossWindows @@ -100,10 +100,10 @@ dist-win32: build-server build-win32 build-win32-noconsole cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN32_TARGET_DIR)/" cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/" cp "$(WIN32_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/scrcpy-noconsole.exe" - cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/" - cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" - cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" - cp prebuilt-deps/ffmpeg-4.0-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win32-shared/bin/swresample-3.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/" @@ -114,10 +114,10 @@ dist-win64: build-server build-win64 build-win64-noconsole cp "$(SERVER_BUILD_DIR)"/server/scrcpy-server.jar "$(DIST)/$(WIN64_TARGET_DIR)/" cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/" cp "$(WIN64_NOCONSOLE_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/scrcpy-noconsole.exe" - cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/" - cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" - cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" - cp prebuilt-deps/ffmpeg-4.0-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/" + cp prebuilt-deps/ffmpeg-4.0.2-win64-shared/bin/swresample-3.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/platform-tools/adb.exe "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/" cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/" diff --git a/cross_win32.txt b/cross_win32.txt index 8cb650aa..05167a33 100644 --- a/cross_win32.txt +++ b/cross_win32.txt @@ -15,6 +15,6 @@ cpu = 'i686' endian = 'little' [properties] -prebuilt_ffmpeg_shared = 'ffmpeg-4.0-win32-shared' -prebuilt_ffmpeg_dev = 'ffmpeg-4.0-win32-dev' +prebuilt_ffmpeg_shared = 'ffmpeg-4.0.2-win32-shared' +prebuilt_ffmpeg_dev = 'ffmpeg-4.0.2-win32-dev' prebuilt_sdl2 = 'SDL2-2.0.8/i686-w64-mingw32' diff --git a/cross_win64.txt b/cross_win64.txt index 0110d5bb..f358b56d 100644 --- a/cross_win64.txt +++ b/cross_win64.txt @@ -15,6 +15,6 @@ cpu = 'x86_64' endian = 'little' [properties] -prebuilt_ffmpeg_shared = 'ffmpeg-4.0-win64-shared' -prebuilt_ffmpeg_dev = 'ffmpeg-4.0-win64-dev' +prebuilt_ffmpeg_shared = 'ffmpeg-4.0.2-win64-shared' +prebuilt_ffmpeg_dev = 'ffmpeg-4.0.2-win64-dev' prebuilt_sdl2 = 'SDL2-2.0.8/x86_64-w64-mingw32' diff --git a/prebuilt-deps/Makefile b/prebuilt-deps/Makefile index 9762f422..2ac6b26f 100644 --- a/prebuilt-deps/Makefile +++ b/prebuilt-deps/Makefile @@ -10,24 +10,24 @@ prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb prepare-ffmpeg-shared-win32: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0-win32-shared.zip \ - 530c92df0ca14c35901b4b681847d62da3c50a0cc9b7ced37b04968f6b5c243d \ - ffmpeg-4.0-win32-shared + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0.2-win32-shared.zip \ + cc190a3a4cf7bfbd4fbaa92609c1501a1de458055e6cfea8b745c1d515013aa8 \ + ffmpeg-4.0.2-win32-shared prepare-ffmpeg-dev-win32: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.0-win32-dev.zip \ - e2f5200b5e73c4d0abb9b89c4ffc0438f92a0aadc54c81cf57e18c81a9f11c6b \ - ffmpeg-4.0-win32-dev + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/dev/ffmpeg-4.0.2-win32-dev.zip \ + c72c74bad74ac0541f1b43090c26a50017c49041c182a703abd2057bb8cdc238 \ + ffmpeg-4.0.2-win32-dev prepare-ffmpeg-shared-win64: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.0-win64-shared.zip \ - 8fe2d344463dbefc2db4239a4203a55ed0324faceaae57276a40c4fabda84c37 \ - ffmpeg-4.0-win64-shared + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/shared/ffmpeg-4.0.2-win64-shared.zip \ + ede566aca8b5348dff85570f9638c6bad33209f9419f79db7bde7daa37599bff \ + ffmpeg-4.0.2-win64-shared prepare-ffmpeg-dev-win64: - @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.0-win64-dev.zip \ - facced738eabfc53fa92834dea8b24426f64db61298688fed480145945be07fa \ - ffmpeg-4.0-win64-dev + @./prepare-dep https://ffmpeg.zeranoe.com/builds/win64/dev/ffmpeg-4.0.2-win64-dev.zip \ + 23ee994161c51285cb956b98d3caa499d48083dae7b26c1fdf77f22e98df1c5f \ + ffmpeg-4.0.2-win64-dev prepare-sdl2: @./prepare-dep https://libsdl.org/release/SDL2-devel-2.0.8-mingw.tar.gz \ From 6d2d803003c231df9df3343eb73edf97d9ac3c76 Mon Sep 17 00:00:00 2001 From: yuchenlin Date: Sat, 1 Sep 2018 09:18:06 +0800 Subject: [PATCH 11/24] Notify adb missing There are many user who encounters missing adb. To stop things happens again, we check it and show sexy response to user. Signed-off-by: yuchenlin --- app/src/command.c | 31 +++++++++++++++++++- app/src/command.h | 2 +- app/src/sys/unix/command.c | 58 ++++++++++++++++++++++++++++++++------ app/src/sys/win/command.c | 11 +++++--- 4 files changed, 88 insertions(+), 14 deletions(-) diff --git a/app/src/command.c b/app/src/command.c index b7ea67d2..011a2b85 100644 --- a/app/src/command.c +++ b/app/src/command.c @@ -1,5 +1,8 @@ #include "command.h" +#ifndef __WINDOWS__ +# include +#endif #include #include #include @@ -18,9 +21,30 @@ static inline const char *get_adb_command() { return adb_command; } +static void show_err_msg(int err) { +#ifdef __WINDOWS__ + (void) err; // unused + LOGE("Failed to execute adb"); +#else + switch (err) { + case -1: + LOGE("Failed to execute adb"); + break; + case ENOENT: + LOGE("'adb' command not found (make it accessible from your PATH " + "or define its full path in the ADB environment variable)"); + break; + default: + LOGE("Failed to execute adb: %s", strerror(err)); + break; + } +#endif +} + process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) { const char *cmd[len + 4]; int i; + process_t process; cmd[0] = get_adb_command(); if (serial) { cmd[1] = "-s"; @@ -32,7 +56,12 @@ process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) memcpy(&cmd[i], adb_cmd, len * sizeof(const char *)); cmd[len + i] = NULL; - return cmd_execute(cmd[0], cmd); + int r = cmd_execute(cmd[0], cmd, &process); + if (r != 0) { + show_err_msg(r); + return PROCESS_NONE; + } + return process; } process_t adb_forward(const char *serial, uint16_t local_port, const char *device_socket_name) { diff --git a/app/src/command.h b/app/src/command.h index 4113f251..dfc0d58a 100644 --- a/app/src/command.h +++ b/app/src/command.h @@ -32,7 +32,7 @@ #endif # define NO_EXIT_CODE -1 -process_t cmd_execute(const char *path, const char *const argv[]); +int cmd_execute(const char *path, const char *const argv[], process_t *process); SDL_bool cmd_terminate(process_t pid); SDL_bool cmd_simple_wait(process_t pid, exit_code_t *exit_code); diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index 0083a93b..20218cb9 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -1,5 +1,7 @@ #include "command.h" +#include +#include #include #include #include @@ -7,18 +9,58 @@ #include #include "log.h" -pid_t cmd_execute(const char *path, const char *const argv[]) { - pid_t pid = fork(); - if (pid == -1) { - perror("fork"); +int cmd_execute(const char *path, const char *const argv[], pid_t *pid) { + int fd[2]; + int ret = 0; + + if (pipe(fd) == -1) { + perror("pipe"); return -1; } - if (pid == 0) { - execvp(path, (char *const *)argv); - perror("exec"); + + *pid = fork(); + if (*pid == -1) { + perror("fork"); + ret = -1; + goto end; + } + + if (*pid > 0) { + // parent close write side + close(fd[1]); + fd[1] = -1; + // wait for EOF or receive errno from child + if (read(fd[0], &ret, sizeof(int)) == -1) { + perror("read"); + ret = -1; + goto end; + } + } else if (*pid == 0) { + // child close read side + close(fd[0]); + if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) { + execvp(path, (char *const *)argv); + } else { + perror("fcntl"); + } + // send errno to the parent + ret = errno; + if (write(fd[1], &ret, sizeof(int)) == -1) { + perror("write"); + } + // close write side before exiting + close(fd[1]); _exit(1); } - return pid; + +end: + if (fd[0] != -1) { + close(fd[0]); + } + if (fd[1] != -1) { + close(fd[1]); + } + return ret; } SDL_bool cmd_terminate(pid_t pid) { diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index 2552eeca..cea928b4 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -4,7 +4,7 @@ #include "log.h" #include "str_util.h" -HANDLE cmd_execute(const char *path, const char *const argv[]) { +int cmd_execute(const char *path, const char *const argv[], HANDLE *handle) { STARTUPINFO si; PROCESS_INFORMATION pi; memset(&si, 0, sizeof(si)); @@ -18,7 +18,8 @@ HANDLE cmd_execute(const char *path, const char *const argv[]) { size_t ret = xstrjoin(cmd, argv, ' ', sizeof(cmd)); if (ret >= sizeof(cmd)) { LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1); - return NULL; + *handle = NULL; + return -1; } #ifdef WINDOWS_NOCONSOLE @@ -27,10 +28,12 @@ HANDLE cmd_execute(const char *path, const char *const argv[]) { int flags = 0; #endif if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) { - return NULL; + *handle = NULL; + return -1; } - return pi.hProcess; + *handle = pi.hProcess; + return 0; } SDL_bool cmd_terminate(HANDLE handle) { From 55d33ddd5fb0b559eed33d8f47d9b07a2456979a Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 4 Sep 2018 08:42:25 +0200 Subject: [PATCH 12/24] Do not handle system-specific values in command.c The common command.c handled process errors from system-specific int values (errno). Rather, expose a new enum process_result to handle error cause in a generic way. --- app/src/command.c | 24 ++++++++---------------- app/src/command.h | 8 +++++++- app/src/sys/unix/command.c | 25 ++++++++++++++++--------- app/src/sys/win/command.c | 8 ++++---- 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/app/src/command.c b/app/src/command.c index 011a2b85..cc11a0de 100644 --- a/app/src/command.c +++ b/app/src/command.c @@ -1,8 +1,5 @@ #include "command.h" -#ifndef __WINDOWS__ -# include -#endif #include #include #include @@ -21,24 +18,19 @@ static inline const char *get_adb_command() { return adb_command; } -static void show_err_msg(int err) { -#ifdef __WINDOWS__ - (void) err; // unused - LOGE("Failed to execute adb"); -#else +static void show_adb_err_msg(enum process_result err) { switch (err) { - case -1: + case PROCESS_ERROR_GENERIC: LOGE("Failed to execute adb"); break; - case ENOENT: + case PROCESS_ERROR_MISSING_BINARY: LOGE("'adb' command not found (make it accessible from your PATH " "or define its full path in the ADB environment variable)"); break; - default: - LOGE("Failed to execute adb: %s", strerror(err)); + case PROCESS_SUCCESS: + /* do nothing */ break; } -#endif } process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) { @@ -56,9 +48,9 @@ process_t adb_execute(const char *serial, const char *const adb_cmd[], int len) memcpy(&cmd[i], adb_cmd, len * sizeof(const char *)); cmd[len + i] = NULL; - int r = cmd_execute(cmd[0], cmd, &process); - if (r != 0) { - show_err_msg(r); + enum process_result r = cmd_execute(cmd[0], cmd, &process); + if (r != PROCESS_SUCCESS) { + show_adb_err_msg(r); return PROCESS_NONE; } return process; diff --git a/app/src/command.h b/app/src/command.h index dfc0d58a..3e0fcca6 100644 --- a/app/src/command.h +++ b/app/src/command.h @@ -32,7 +32,13 @@ #endif # define NO_EXIT_CODE -1 -int cmd_execute(const char *path, const char *const argv[], process_t *process); +enum process_result { + PROCESS_SUCCESS, + PROCESS_ERROR_GENERIC, + PROCESS_ERROR_MISSING_BINARY, +}; + +enum process_result cmd_execute(const char *path, const char *const argv[], process_t *process); SDL_bool cmd_terminate(process_t pid); SDL_bool cmd_simple_wait(process_t pid, exit_code_t *exit_code); diff --git a/app/src/sys/unix/command.c b/app/src/sys/unix/command.c index 20218cb9..ed9bbeac 100644 --- a/app/src/sys/unix/command.c +++ b/app/src/sys/unix/command.c @@ -9,19 +9,20 @@ #include #include "log.h" -int cmd_execute(const char *path, const char *const argv[], pid_t *pid) { +enum process_result cmd_execute(const char *path, const char *const argv[], pid_t *pid) { int fd[2]; - int ret = 0; if (pipe(fd) == -1) { perror("pipe"); - return -1; + return PROCESS_ERROR_GENERIC; } + enum process_result ret = PROCESS_SUCCESS; + *pid = fork(); if (*pid == -1) { perror("fork"); - ret = -1; + ret = PROCESS_ERROR_GENERIC; goto end; } @@ -30,9 +31,9 @@ int cmd_execute(const char *path, const char *const argv[], pid_t *pid) { close(fd[1]); fd[1] = -1; // wait for EOF or receive errno from child - if (read(fd[0], &ret, sizeof(int)) == -1) { + if (read(fd[0], &ret, sizeof(ret)) == -1) { perror("read"); - ret = -1; + ret = PROCESS_ERROR_GENERIC; goto end; } } else if (*pid == 0) { @@ -40,12 +41,18 @@ int cmd_execute(const char *path, const char *const argv[], pid_t *pid) { close(fd[0]); if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) == 0) { execvp(path, (char *const *)argv); + if (errno == ENOENT) { + ret = PROCESS_ERROR_MISSING_BINARY; + } else { + ret = PROCESS_ERROR_GENERIC; + } + perror("exec"); } else { perror("fcntl"); + ret = PROCESS_ERROR_GENERIC; } - // send errno to the parent - ret = errno; - if (write(fd[1], &ret, sizeof(int)) == -1) { + // send ret to the parent + if (write(fd[1], &ret, sizeof(ret)) == -1) { perror("write"); } // close write side before exiting diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index cea928b4..7a29442d 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -4,7 +4,7 @@ #include "log.h" #include "str_util.h" -int cmd_execute(const char *path, const char *const argv[], HANDLE *handle) { +enum process_result cmd_execute(const char *path, const char *const argv[], HANDLE *handle) { STARTUPINFO si; PROCESS_INFORMATION pi; memset(&si, 0, sizeof(si)); @@ -19,7 +19,7 @@ int cmd_execute(const char *path, const char *const argv[], HANDLE *handle) { if (ret >= sizeof(cmd)) { LOGE("Command too long (%" PRIsizet " chars)", sizeof(cmd) - 1); *handle = NULL; - return -1; + return PROCESS_ERROR_GENERIC; } #ifdef WINDOWS_NOCONSOLE @@ -29,11 +29,11 @@ int cmd_execute(const char *path, const char *const argv[], HANDLE *handle) { #endif if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) { *handle = NULL; - return -1; + return PROCESS_ERROR_GENERIC; } *handle = pi.hProcess; - return 0; + return PROCESS_SUCCESS; } SDL_bool cmd_terminate(HANDLE handle) { From af9808cf02c63c93ad82f1de111b7c85dc2e8d75 Mon Sep 17 00:00:00 2001 From: Philipp Sandhaus Date: Tue, 4 Sep 2018 15:55:20 +0200 Subject: [PATCH 13/24] Add option to start in fullscreen Signed-off-by: Romain Vimont --- app/src/main.c | 11 ++++++++++- app/src/scrcpy.c | 4 ++++ app/src/scrcpy.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/src/main.c b/app/src/main.c index 1317da79..634c7bb4 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -11,6 +11,7 @@ struct args { const char *serial; const char *crop; + SDL_bool fullscreen; SDL_bool help; SDL_bool version; SDL_bool show_touches; @@ -36,6 +37,9 @@ static void usage(const char *arg0) { " (typically, portrait for a phone, landscape for a tablet).\n" " Any --max-size value is computed on the cropped size.\n" "\n" + " -f, --fullscreen\n" + " Start in fullscreen.\n" + "\n" " -h, --help\n" " Print this help.\n" "\n" @@ -200,6 +204,7 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) { static const struct option long_options[] = { {"bit-rate", required_argument, NULL, 'b'}, {"crop", required_argument, NULL, 'c'}, + {"fullscreen", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"max-size", required_argument, NULL, 'm'}, {"port", required_argument, NULL, 'p'}, @@ -209,7 +214,7 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) { {NULL, 0, NULL, 0 }, }; int c; - while ((c = getopt_long(argc, argv, "b:c:hm:p:s:tv", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "b:c:fhm:p:s:tv", long_options, NULL)) != -1) { switch (c) { case 'b': if (!parse_bit_rate(optarg, &args->bit_rate)) { @@ -219,6 +224,9 @@ static SDL_bool parse_args(struct args *args, int argc, char *argv[]) { case 'c': args->crop = optarg; break; + case 'f': + args->fullscreen = SDL_TRUE; + break; case 'h': args->help = SDL_TRUE; break; @@ -305,6 +313,7 @@ int main(int argc, char *argv[]) { .max_size = args.max_size, .bit_rate = args.bit_rate, .show_touches = args.show_touches, + .fullscreen = args.fullscreen, }; int res = scrcpy(&options) ? 0 : 1; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 3eaa45c0..54a7f993 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -223,6 +223,10 @@ SDL_bool scrcpy(const struct scrcpy_options *options) { show_touches_waited = SDL_TRUE; } + if (options->fullscreen) { + screen_switch_fullscreen(&screen); + } + ret = event_loop(); LOGD("quit..."); diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 7ddabf28..f64d4c02 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -10,6 +10,7 @@ struct scrcpy_options { Uint16 max_size; Uint32 bit_rate; SDL_bool show_touches; + SDL_bool fullscreen; }; SDL_bool scrcpy(const struct scrcpy_options *options); From 28015c3ee460eec477dccba80c9063ef6fddcffe Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 4 Sep 2018 18:40:57 +0200 Subject: [PATCH 14/24] Present fullscreen option in README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 08e47344..f1f94d5b 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,12 @@ To show physical touches while scrcpy is running: scrcpy -t ``` +The app may be started directly in fullscreen: + +``` +scrcpy -f +``` + To run without installing: ```bash From 66def38b734829170654647086bd76f99efdec70 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 9 Sep 2018 15:01:55 +0200 Subject: [PATCH 15/24] Avoid additional buffer copy in userspace Directly send the data from MediaCodec buffers to the LocalSocket, without an intermediate copy in userspace. --- .../genymobile/scrcpy/DesktopConnection.java | 12 +++---- .../main/java/com/genymobile/scrcpy/IO.java | 31 +++++++++++++++++++ .../com/genymobile/scrcpy/ScreenEncoder.java | 21 ++++--------- .../java/com/genymobile/scrcpy/Server.java | 2 +- 4 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 server/src/main/java/com/genymobile/scrcpy/IO.java diff --git a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java index d5740c15..d87a7fd8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java +++ b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java @@ -5,9 +5,9 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import java.io.Closeable; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.charset.StandardCharsets; public final class DesktopConnection implements Closeable { @@ -18,14 +18,14 @@ public final class DesktopConnection implements Closeable { private final LocalSocket socket; private final InputStream inputStream; - private final OutputStream outputStream; + private final FileDescriptor fd; private final ControlEventReader reader = new ControlEventReader(); private DesktopConnection(LocalSocket socket) throws IOException { this.socket = socket; inputStream = socket.getInputStream(); - outputStream = socket.getOutputStream(); + fd = socket.getFileDescriptor(); } private static LocalSocket connect(String abstractName) throws IOException { @@ -78,11 +78,11 @@ public final class DesktopConnection implements Closeable { buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width; buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8); buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height; - outputStream.write(buffer, 0, buffer.length); + IO.writeFully(fd, buffer, 0, buffer.length); } - public OutputStream getOutputStream() { - return outputStream; + public FileDescriptor getFd() { + return fd; } public ControlEvent receiveControlEvent() throws IOException { diff --git a/server/src/main/java/com/genymobile/scrcpy/IO.java b/server/src/main/java/com/genymobile/scrcpy/IO.java new file mode 100644 index 00000000..bfd48be2 --- /dev/null +++ b/server/src/main/java/com/genymobile/scrcpy/IO.java @@ -0,0 +1,31 @@ +package com.genymobile.scrcpy; + +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; + +public class IO { + private IO() { + // not instantiable + } + + public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException { + while (from.hasRemaining()) { + try { + Os.write(fd, from); + } catch (ErrnoException e) { + if (e.errno != OsConstants.EINTR) { + throw new IOException(e); + } + } + } + } + + public static void writeFully(FileDescriptor fd, byte[] buffer, int offset, int len) throws IOException { + writeFully(fd, ByteBuffer.wrap(buffer, offset, len)); + } +} diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index e2ee8122..636bbb00 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -9,8 +9,8 @@ import android.media.MediaFormat; import android.os.IBinder; import android.view.Surface; +import java.io.FileDescriptor; import java.io.IOException; -import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicBoolean; @@ -48,7 +48,7 @@ public class ScreenEncoder implements Device.RotationListener { return rotationChanged.getAndSet(false); } - public void streamScreen(Device device, OutputStream outputStream) throws IOException { + public void streamScreen(Device device, FileDescriptor fd) throws IOException { MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval); device.setRotationListener(this); boolean alive; @@ -64,7 +64,7 @@ public class ScreenEncoder implements Device.RotationListener { setDisplaySurface(display, surface, contentRect, videoRect); codec.start(); try { - alive = encode(codec, outputStream); + alive = encode(codec, fd); } finally { codec.stop(); destroyDisplay(display); @@ -77,9 +77,7 @@ public class ScreenEncoder implements Device.RotationListener { } } - private boolean encode(MediaCodec codec, OutputStream outputStream) throws IOException { - @SuppressWarnings("checkstyle:MagicNumber") - byte[] buf = new byte[bitRate / 8]; // may contain up to 1 second of video + private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException { boolean eof = false; MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); while (!consumeRotationChange() && !eof) { @@ -91,15 +89,8 @@ public class ScreenEncoder implements Device.RotationListener { break; } if (outputBufferId >= 0) { - ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); - while (outputBuffer.hasRemaining()) { - int remaining = outputBuffer.remaining(); - int len = Math.min(buf.length, remaining); - // the outputBuffer is probably direct (it has no underlying array), and LocalSocket does not expose channels, - // so we must copy the data locally to write them manually to the output stream - outputBuffer.get(buf, 0, len); - outputStream.write(buf, 0, len); - } + ByteBuffer codecBuffer = codec.getOutputBuffer(outputBufferId); + IO.writeFully(fd, codecBuffer); } } finally { if (outputBufferId >= 0) { diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 9fd93863..b218e83d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -21,7 +21,7 @@ public final class Server { try { // synchronous - screenEncoder.streamScreen(device, connection.getOutputStream()); + screenEncoder.streamScreen(device, connection.getFd()); } catch (IOException e) { // this is expected on close Ln.d("Screen streaming stopped"); From 27bed948d4ddfffbb6ea5ad80a2a58394fa96b75 Mon Sep 17 00:00:00 2001 From: yuchenlin Date: Sat, 8 Sep 2018 09:16:53 +0800 Subject: [PATCH 16/24] Use specific error for missing binary on Windows Signed-off-by: yuchenlin Signed-off-by: Romain Vimont --- app/src/sys/win/command.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/sys/win/command.c b/app/src/sys/win/command.c index 7a29442d..1a14d89a 100644 --- a/app/src/sys/win/command.c +++ b/app/src/sys/win/command.c @@ -29,6 +29,9 @@ enum process_result cmd_execute(const char *path, const char *const argv[], HAND #endif if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, flags, NULL, NULL, &si, &pi)) { *handle = NULL; + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + return PROCESS_ERROR_MISSING_BINARY; + } return PROCESS_ERROR_GENERIC; } From eca99d5af7d27115bb232f01826d3c5fc4fedd47 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 13 Sep 2018 16:27:19 +0200 Subject: [PATCH 17/24] Fix header guard name --- app/src/file_handler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/file_handler.h b/app/src/file_handler.h index 375db297..9ce39367 100644 --- a/app/src/file_handler.h +++ b/app/src/file_handler.h @@ -1,5 +1,5 @@ #ifndef FILE_HANDLER_H -#define FILE_HADNELR_H +#define FILE_HANDLER_H #include #include From 140b1ef6a5f2de489f99c6f7f63dc2a49bc404f6 Mon Sep 17 00:00:00 2001 From: yuchenlin Date: Fri, 14 Sep 2018 20:34:59 +0800 Subject: [PATCH 18/24] prevent closing console right after process error in windows Signed-off-by: yuchenlin --- app/src/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main.c b/app/src/main.c index 634c7bb4..e1d6782e 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -319,5 +319,11 @@ int main(int argc, char *argv[]) { avformat_network_deinit(); // ignore failure +#if defined (__WINDOWS__) && ! defined (WINDOWS_NOCONSOLE) + if (res != 0) { + fprintf(stderr, "Press any key to continue...\n"); + getchar(); + } +#endif return res; } From 411aa4fcfd6ed245252be29660777f9c286448ec Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 9 Sep 2018 16:31:41 +0200 Subject: [PATCH 19/24] Handle alpha and space chars as raw events To handle special chars, text is handled as text input instead of key events. However, this breaks the separation of DOWN and UP key events. As a compromise, send letters and space as key events, to preserve original DOWN/UP events, but send other text input events as text, to be able to send "special" characters. Fixes . Suggested-by: pete1414 Suggested-by: King-Slide --- app/src/convert.c | 40 +++++++++++++++++++++++++++++++++++++--- app/src/input_manager.c | 7 +++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/app/src/convert.c b/app/src/convert.c index 9d947cb3..7d40b938 100644 --- a/app/src/convert.c +++ b/app/src/convert.c @@ -70,7 +70,7 @@ static enum android_metastate convert_meta_state(SDL_Keymod mod) { return autocomplete_metastate(metastate); } -static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to) { +static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to, Uint16 mod) { switch (from) { MAP(SDLK_RETURN, AKEYCODE_ENTER); MAP(SDLK_KP_ENTER, AKEYCODE_NUMPAD_ENTER); @@ -86,6 +86,39 @@ static SDL_bool convert_keycode(SDL_Keycode from, enum android_keycode *to) { MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT); MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN); MAP(SDLK_UP, AKEYCODE_DPAD_UP); + } + if (mod & (KMOD_LALT | KMOD_RALT | KMOD_LGUI | KMOD_RGUI)) { + return SDL_FALSE; + } + // if ALT and META are not pressed, also handle letters and space + switch (from) { + MAP(SDLK_a, AKEYCODE_A); + MAP(SDLK_b, AKEYCODE_B); + MAP(SDLK_c, AKEYCODE_C); + MAP(SDLK_d, AKEYCODE_D); + MAP(SDLK_e, AKEYCODE_E); + MAP(SDLK_f, AKEYCODE_F); + MAP(SDLK_g, AKEYCODE_G); + MAP(SDLK_h, AKEYCODE_H); + MAP(SDLK_i, AKEYCODE_I); + MAP(SDLK_j, AKEYCODE_J); + MAP(SDLK_k, AKEYCODE_K); + MAP(SDLK_l, AKEYCODE_L); + MAP(SDLK_m, AKEYCODE_M); + MAP(SDLK_n, AKEYCODE_N); + MAP(SDLK_o, AKEYCODE_O); + MAP(SDLK_p, AKEYCODE_P); + MAP(SDLK_q, AKEYCODE_Q); + MAP(SDLK_r, AKEYCODE_R); + MAP(SDLK_s, AKEYCODE_S); + MAP(SDLK_t, AKEYCODE_T); + MAP(SDLK_u, AKEYCODE_U); + MAP(SDLK_v, AKEYCODE_V); + MAP(SDLK_w, AKEYCODE_W); + MAP(SDLK_x, AKEYCODE_X); + MAP(SDLK_y, AKEYCODE_Y); + MAP(SDLK_z, AKEYCODE_Z); + MAP(SDLK_SPACE, AKEYCODE_SPACE); FAIL; } } @@ -126,11 +159,12 @@ SDL_bool input_key_from_sdl_to_android(const SDL_KeyboardEvent *from, return SDL_FALSE; } - if (!convert_keycode(from->keysym.sym, &to->keycode_event.keycode)) { + Uint16 mod = from->keysym.mod; + if (!convert_keycode(from->keysym.sym, &to->keycode_event.keycode, mod)) { return SDL_FALSE; } - to->keycode_event.metastate = convert_meta_state(from->keysym.mod); + to->keycode_event.metastate = convert_meta_state(mod); return SDL_TRUE; } diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 797fd764..af84c8f3 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -1,5 +1,6 @@ #include "input_manager.h" +#include #include "convert.h" #include "lock_util.h" #include "log.h" @@ -129,6 +130,12 @@ static void clipboard_paste(struct controller *controller) { void input_manager_process_text_input(struct input_manager *input_manager, const SDL_TextInputEvent *event) { + char c = event->text[0]; + if (isalpha(c) || c == ' ') { + SDL_assert(event->text[1] == '\0'); + // letters and space are handled as raw key event + return; + } struct control_event control_event; control_event.type = CONTROL_EVENT_TYPE_TEXT; control_event.text_event.text = SDL_strdup(event->text); From 24d107d017cd297965f1e86d49c8e58b6d38a762 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 3 Oct 2018 23:03:27 +0200 Subject: [PATCH 20/24] Bump version to 1.4 --- app/meson.build | 2 +- server/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/meson.build b/app/meson.build index f2f1e339..3e309cdf 100644 --- a/app/meson.build +++ b/app/meson.build @@ -85,7 +85,7 @@ conf = configuration_data() conf.set('BUILD_DEBUG', get_option('buildtype') == 'debug') # the version, updated on release -conf.set_quoted('SCRCPY_VERSION', '1.3') +conf.set_quoted('SCRCPY_VERSION', '1.4') # the prefix used during configuration (meson --prefix=PREFIX) conf.set_quoted('PREFIX', get_option('prefix')) diff --git a/server/build.gradle b/server/build.gradle index 3e1c67e4..957a61d7 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -6,8 +6,8 @@ android { applicationId "com.genymobile.scrcpy" minSdkVersion 21 targetSdkVersion 27 - versionCode 4 - versionName "1.3" + versionCode 5 + versionName "1.4" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { From f613752606d56241a1eb4e771157b78e5ce7fdb6 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 3 Oct 2018 23:18:37 +0200 Subject: [PATCH 21/24] Update platform-tools (28.0.1) for Windows Include the latest version of adb in Windows releases. --- prebuilt-deps/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prebuilt-deps/Makefile b/prebuilt-deps/Makefile index 2ac6b26f..c49b9211 100644 --- a/prebuilt-deps/Makefile +++ b/prebuilt-deps/Makefile @@ -35,6 +35,6 @@ prepare-sdl2: SDL2-2.0.8 prepare-adb: - @./prepare-dep https://dl.google.com/android/repository/platform-tools_r28.0.0-windows.zip \ - e2c1ec7c8e9b71cf1c8befd3bff91d06b26dd334c3f32b3817e9d46ba260b0e8 \ + @./prepare-dep https://dl.google.com/android/repository/platform-tools_r28.0.1-windows.zip \ + db78f726d5dc653706dcd15a462ab1b946c643f598df76906c4c1858411c54df \ platform-tools From cea176c210661f856bf2ac764f3d0794c8f2c47e Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Wed, 3 Oct 2018 23:32:48 +0200 Subject: [PATCH 22/24] Update links to v1.4 in README and BUILD --- BUILD.md | 6 +++--- README.md | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/BUILD.md b/BUILD.md index 745ac50c..6d4d6431 100644 --- a/BUILD.md +++ b/BUILD.md @@ -219,10 +219,10 @@ You can then [run](README.md#run) _scrcpy_. ## Prebuilt server - - [`scrcpy-server-v1.3.jar`][direct-scrcpy-server]. - _(SHA-256: 0f9a5a217f33f0ed7a1498ceb3c0cccf31c53533893aa952e674c1571d2740c1)_ + - [`scrcpy-server-v1.4.jar`][direct-scrcpy-server] + _(SHA-256: 1ff7a72fcfe81dadccfab9d6f86c971cd7c7f38f17196748fe05480e301b443d)_ -[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.3/scrcpy-server-v1.3.jar +[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.4/scrcpy-server-v1.4.jar Download the prebuilt server somewhere, and specify its path during the Meson configuration: diff --git a/README.md b/README.md index 33eeb527..28e76e24 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# scrcpy (v1.3) +# scrcpy (v1.4) This application provides display and control of Android devices connected on USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access. @@ -42,13 +42,13 @@ For Gentoo, an [Ebuild] is available: [`scrcpy/`][ebuild-link]. For Windows, for simplicity, prebuilt archives with all the dependencies (including `adb`) are available: - - [`scrcpy-win32-v1.3.zip`][direct-win32]. - _(SHA-256: 51a2990e631ed469a7a86ff38107d517a91d313fb3f8327eb7bc71dde40870b5)_ - - [`scrcpy-win64-v1.3.zip`][direct-win64]. - _(SHA-256: 0768a80d3d600d0bbcd220ca150ae88a3a58d1fe85c308a8c61f44480b711e43)_ + - [`scrcpy-win32-v1.4.zip`][direct-win32] + _(SHA-256: 1f72fa520980727e8943b7214b64c66b00b9b5267f7cffefb64fa37c3ca803cf)_ + - [`scrcpy-win64-v1.4.zip`][direct-win64] + _(SHA-256: 382f02bd8ed3db2cc7ab15aabdb83674744993b936d602b01e6959a150584a79)_ -[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.3/scrcpy-win32-v1.3.zip -[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.3/scrcpy-win64-v1.3.zip +[direct-win32]: https://github.com/Genymobile/scrcpy/releases/download/v1.4/scrcpy-win32-v1.4.zip +[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.4/scrcpy-win64-v1.4.zip You can also [build the app manually][BUILD]. From ff4430b2a397ecb5064f2a1aec0af725af27ce9b Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 4 Oct 2018 17:03:24 +0200 Subject: [PATCH 23/24] Declare fun(void) functions with no parameters This is not C++. --- app/src/command.c | 2 +- app/tests/test_control_event_queue.c | 8 ++++---- app/tests/test_control_event_serialize.c | 13 ++++++------- app/tests/test_strutil.c | 18 +++++++++--------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/src/command.c b/app/src/command.c index cc11a0de..e6e63ca7 100644 --- a/app/src/command.c +++ b/app/src/command.c @@ -9,7 +9,7 @@ static const char *adb_command; -static inline const char *get_adb_command() { +static inline const char *get_adb_command(void) { if (!adb_command) { adb_command = getenv("ADB"); if (!adb_command) diff --git a/app/tests/test_control_event_queue.c b/app/tests/test_control_event_queue.c index 07e547c1..a27181b6 100644 --- a/app/tests/test_control_event_queue.c +++ b/app/tests/test_control_event_queue.c @@ -3,7 +3,7 @@ #include "control_event.h" -static void test_control_event_queue_empty() { +static void test_control_event_queue_empty(void) { struct control_event_queue queue; SDL_bool init_ok = control_event_queue_init(&queue); assert(init_ok); @@ -25,7 +25,7 @@ static void test_control_event_queue_empty() { control_event_queue_destroy(&queue); } -static void test_control_event_queue_full() { +static void test_control_event_queue_full(void) { struct control_event_queue queue; SDL_bool init_ok = control_event_queue_init(&queue); assert(init_ok); @@ -43,7 +43,7 @@ static void test_control_event_queue_full() { control_event_queue_destroy(&queue); } -static void test_control_event_queue_push_take() { +static void test_control_event_queue_push_take(void) { struct control_event_queue queue; SDL_bool init_ok = control_event_queue_init(&queue); assert(init_ok); @@ -87,7 +87,7 @@ static void test_control_event_queue_push_take() { control_event_queue_destroy(&queue); } -int main() { +int main(void) { test_control_event_queue_empty(); test_control_event_queue_full(); test_control_event_queue_push_take(); diff --git a/app/tests/test_control_event_serialize.c b/app/tests/test_control_event_serialize.c index ad472776..21d7909b 100644 --- a/app/tests/test_control_event_serialize.c +++ b/app/tests/test_control_event_serialize.c @@ -3,7 +3,7 @@ #include "control_event.h" -static void test_serialize_keycode_event() { +static void test_serialize_keycode_event(void) { struct control_event event = { .type = CONTROL_EVENT_TYPE_KEYCODE, .keycode_event = { @@ -26,7 +26,7 @@ static void test_serialize_keycode_event() { assert(!memcmp(buf, expected, sizeof(expected))); } -static void test_serialize_text_event() { +static void test_serialize_text_event(void) { struct control_event event = { .type = CONTROL_EVENT_TYPE_TEXT, .text_event = { @@ -46,7 +46,7 @@ static void test_serialize_text_event() { assert(!memcmp(buf, expected, sizeof(expected))); } -static void test_serialize_long_text_event() { +static void test_serialize_long_text_event(void) { struct control_event event; event.type = CONTROL_EVENT_TYPE_TEXT; char text[TEXT_MAX_LENGTH]; @@ -66,7 +66,7 @@ static void test_serialize_long_text_event() { assert(!memcmp(buf, expected, sizeof(expected))); } -static void test_serialize_mouse_event() { +static void test_serialize_mouse_event(void) { struct control_event event = { .type = CONTROL_EVENT_TYPE_MOUSE, .mouse_event = { @@ -99,7 +99,7 @@ static void test_serialize_mouse_event() { assert(!memcmp(buf, expected, sizeof(expected))); } -static void test_serialize_scroll_event() { +static void test_serialize_scroll_event(void) { struct control_event event = { .type = CONTROL_EVENT_TYPE_SCROLL, .scroll_event = { @@ -132,11 +132,10 @@ static void test_serialize_scroll_event() { assert(!memcmp(buf, expected, sizeof(expected))); } -int main() { +int main(void) { test_serialize_keycode_event(); test_serialize_text_event(); test_serialize_long_text_event(); test_serialize_mouse_event(); test_serialize_scroll_event(); - return 0; } diff --git a/app/tests/test_strutil.c b/app/tests/test_strutil.c index b00e01fe..1dd7fbbe 100644 --- a/app/tests/test_strutil.c +++ b/app/tests/test_strutil.c @@ -3,7 +3,7 @@ #include "str_util.h" -static void test_xstrncpy_simple() { +static void test_xstrncpy_simple(void) { char s[] = "xxxxxxxxxx"; size_t w = xstrncpy(s, "abcdef", sizeof(s)); @@ -20,7 +20,7 @@ static void test_xstrncpy_simple() { assert(!strcmp("abcdef", s)); } -static void test_xstrncpy_just_fit() { +static void test_xstrncpy_just_fit(void) { char s[] = "xxxxxx"; size_t w = xstrncpy(s, "abcdef", sizeof(s)); @@ -34,7 +34,7 @@ static void test_xstrncpy_just_fit() { assert(!strcmp("abcdef", s)); } -static void test_xstrncpy_truncated() { +static void test_xstrncpy_truncated(void) { char s[] = "xxx"; size_t w = xstrncpy(s, "abcdef", sizeof(s)); @@ -48,7 +48,7 @@ static void test_xstrncpy_truncated() { assert(!strncmp("abcdef", s, 3)); } -static void test_xstrjoin_simple() { +static void test_xstrjoin_simple(void) { const char *const tokens[] = { "abc", "de", "fghi", NULL }; char s[] = "xxxxxxxxxxxxxx"; size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); @@ -66,7 +66,7 @@ static void test_xstrjoin_simple() { assert(!strcmp("abc de fghi", s)); } -static void test_xstrjoin_just_fit() { +static void test_xstrjoin_just_fit(void) { const char *const tokens[] = { "abc", "de", "fghi", NULL }; char s[] = "xxxxxxxxxxx"; size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); @@ -81,7 +81,7 @@ static void test_xstrjoin_just_fit() { assert(!strcmp("abc de fghi", s)); } -static void test_xstrjoin_truncated_in_token() { +static void test_xstrjoin_truncated_in_token(void) { const char *const tokens[] = { "abc", "de", "fghi", NULL }; char s[] = "xxxxx"; size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); @@ -96,7 +96,7 @@ static void test_xstrjoin_truncated_in_token() { assert(!strcmp("abc d", s)); } -static void test_xstrjoin_truncated_before_sep() { +static void test_xstrjoin_truncated_before_sep(void) { const char *const tokens[] = { "abc", "de", "fghi", NULL }; char s[] = "xxxxxx"; size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); @@ -111,7 +111,7 @@ static void test_xstrjoin_truncated_before_sep() { assert(!strcmp("abc de", s)); } -static void test_xstrjoin_truncated_after_sep() { +static void test_xstrjoin_truncated_after_sep(void) { const char *const tokens[] = { "abc", "de", "fghi", NULL }; char s[] = "xxxxxxx"; size_t w = xstrjoin(s, tokens, ' ', sizeof(s)); @@ -126,7 +126,7 @@ static void test_xstrjoin_truncated_after_sep() { assert(!strcmp("abc de ", s)); } -int main() { +int main(void) { test_xstrncpy_simple(); test_xstrncpy_just_fit(); test_xstrncpy_truncated(); From 887595592167c09a7c44ce8754f97f4475ae26c6 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 4 Oct 2018 20:47:53 +0200 Subject: [PATCH 24/24] Support paths containing spaces on Windows Quote the arguments of "adb push" to support paths which contain spaces on Windows. Fixes . --- app/src/command.c | 47 ++++++++++++++++++++++++++++++++++++---------- app/src/str_util.c | 16 ++++++++++++++++ app/src/str_util.h | 4 ++++ 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/app/src/command.c b/app/src/command.c index e6e63ca7..b5bb9572 100644 --- a/app/src/command.c +++ b/app/src/command.c @@ -6,6 +6,7 @@ #include "common.h" #include "log.h" +#include "str_util.h" static const char *adb_command; @@ -89,23 +90,49 @@ process_t adb_reverse_remove(const char *serial, const char *device_socket_name) } process_t adb_push(const char *serial, const char *local, const char *remote) { +#ifdef __WINDOWS__ + // Windows will parse the string, so the paths must be quoted + // (see sys/win/command.c) + local = strquote(local); + if (!local) { + return PROCESS_NONE; + } + remote = strquote(remote); + if (!remote) { + free((void *) local); + return PROCESS_NONE; + } +#endif + const char *const adb_cmd[] = {"push", local, remote}; - return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); + process_t proc = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); + +#ifdef __WINDOWS__ + free((void *) remote); + free((void *) local); +#endif + + return proc; } process_t adb_install(const char *serial, const char *local) { #ifdef __WINDOWS__ - // Windows will parse the string, so the local name must be quoted (see sys/win/command.c) - size_t len = strlen(local); - char quoted[len + 3]; - memcpy("ed[1], local, len); - quoted[0] = '"'; - quoted[len + 1] = '"'; - quoted[len + 2] = '\0'; - local = quoted; + // Windows will parse the string, so the local name must be quoted + // (see sys/win/command.c) + local = strquote(local); + if (!local) { + return PROCESS_NONE; + } #endif + const char *const adb_cmd[] = {"install", "-r", local}; - return adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); + process_t proc = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd)); + +#ifdef __WINDOWS__ + free((void *) local); +#endif + + return proc; } process_t adb_remove_path(const char *serial, const char *path) { diff --git a/app/src/str_util.c b/app/src/str_util.c index 0e090403..f234346e 100644 --- a/app/src/str_util.c +++ b/app/src/str_util.c @@ -1,5 +1,8 @@ #include "str_util.h" +#include +#include + size_t xstrncpy(char *dest, const char *src, size_t n) { size_t i; for (i = 0; i < n - 1 && src[i] != '\0'; ++i) @@ -31,3 +34,16 @@ truncated: dst[n - 1] = '\0'; return n; } + +char *strquote(const char *src) { + size_t len = strlen(src); + char *quoted = malloc(len + 3); + if (!quoted) { + return NULL; + } + memcpy("ed[1], src, len); + quoted[0] = '"'; + quoted[len + 1] = '"'; + quoted[len + 2] = '\0'; + return quoted; +} diff --git a/app/src/str_util.h b/app/src/str_util.h index 1bf8088e..9433f768 100644 --- a/app/src/str_util.h +++ b/app/src/str_util.h @@ -16,4 +16,8 @@ size_t xstrncpy(char *dest, const char *src, size_t n); // occurred, or n if truncated size_t xstrjoin(char *dst, const char *const tokens[], char sep, size_t n); +// quote a string +// returns the new allocated string, to be freed by the caller +char *strquote(const char *src); + #endif