Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support drag & drop file to device /sdcard #226

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions app/src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
210 changes: 210 additions & 0 deletions app/src/file_handler.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#include "file_handler.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<nitpick>
filehandler.c/filehandler.h for "consistency" with the other files (but still with _ for the structure). That's not a great convention, but at least it would be more consistent 😉
</nitpick>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a good idea. But I will do it to maintain consistency.

Thanks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it's weird. Don't do it, I will rename the other files instead.


#include <string.h>
#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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDL_free((void *) req)

}

SDL_bool request_queue_is_empty(const struct request_queue *queue) {
return queue->head == queue->tail;
}

SDL_bool request_queue_is_full(const struct request_queue *queue) {
return (queue->head + 1) % REQUEST_QUEUE_SIZE == queue->tail;
}

SDL_bool request_queue_init(struct request_queue *queue) {
queue->head = 0;
queue->tail = 0;
return SDL_TRUE;
}

void request_queue_destroy(struct request_queue *queue) {
int i = queue->tail;
while (i != queue->head) {
request_free(queue->reqs[i]);
i = (i + 1) % REQUEST_QUEUE_SIZE;
}
}

SDL_bool request_queue_push(struct request_queue *queue, struct request *req) {
if (request_queue_is_full(queue)) {
return SDL_FALSE;
}
queue->reqs[queue->head] = req;
queue->head = (queue->head + 1) % REQUEST_QUEUE_SIZE;
return SDL_TRUE;
}

SDL_bool request_queue_take(struct request_queue *queue, struct request **req) {
if (request_queue_is_empty(queue)) {
return SDL_FALSE;
}
// transfer ownership
*req = queue->reqs[queue->tail];
queue->tail = (queue->tail + 1) % REQUEST_QUEUE_SIZE;
return SDL_TRUE;
}

SDL_bool file_handler_init(struct file_handler *file_handler, const char *serial) {

if (!request_queue_init(&file_handler->queue)) {
return SDL_FALSE;
}

if (!(file_handler->mutex = SDL_CreateMutex())) {
return SDL_FALSE;
}

if (!(file_handler->event_cond = SDL_CreateCond())) {
SDL_DestroyMutex(file_handler->mutex);
return SDL_FALSE;
}

if (serial) {
file_handler->serial = SDL_strdup(serial);
if (!file_handler->serial) {
LOGW("Cannot strdup serial");
SDL_DestroyMutex(file_handler->mutex);
return SDL_FALSE;
}
} else {
file_handler->serial = NULL;
}

// lazy initialization
file_handler->initialized = SDL_FALSE;

file_handler->stopped = SDL_FALSE;
file_handler->current_process = PROCESS_NONE;

return SDL_TRUE;
}

void file_handler_destroy(struct file_handler *file_handler) {
SDL_DestroyCond(file_handler->event_cond);
SDL_DestroyMutex(file_handler->mutex);
request_queue_destroy(&file_handler->queue);
SDL_free((void *) file_handler->serial);
}

static process_t install_apk(const char *serial, const char *file) {
return adb_install(serial, file);
}

static process_t push_file(const char *serial, const char *file) {
return adb_push(serial, file, DEVICE_SDCARD_PATH);
}

SDL_bool file_handler_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));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check malloc failure

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not leak req if file_handler_start() fails


// 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should log the action too.

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different log messages for install and push would be better for the user.

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);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different log messages for install and push would be better for the user.

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);
}
48 changes: 48 additions & 0 deletions app/src/file_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef FILE_HANDLER_H
#define FILE_HADNELR_H

#include <SDL2/SDL_mutex.h>
#include <SDL2/SDL_stdinc.h>
#include <SDL2/SDL_thread.h>
#include "command.h"

#define REQUEST_QUEUE_SIZE 16

typedef enum {
INSTALL_APK = 0,
PUSH_FILE,
} req_action_t;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since they are actions associated by the file_handler, I would prefer something like file_handler_action_t (especially once request is moved to the .c).


struct request {
Copy link
Collaborator

@rom1v rom1v Aug 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request is used only internally, it could be moved to file_handler.c.

I'll do the same later with request_queue.

EDIT: ah no, request_queue is used by file_handler in the .h.

process_t (*func)(const char *, const char *);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either store func+file or action+file, but not both. Since the action is useful later (for error messages), I suggest to remove func.

char *file;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const char *

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's subjective, but I would reverse action and file (we request an action, and pass its parameter afterwards).


#endif
Loading