Add option to mirror in read-only

Add an option to disable device control: -n/--no-control.
This commit is contained in:
Romain Vimont 2019-03-02 22:40:51 +01:00
parent 163cd36ccc
commit 8655ba7197
6 changed files with 92 additions and 32 deletions

View file

@ -247,6 +247,17 @@ _scrcpy_ window.
There is no visual feedback, a log is printed to the console. There is no visual feedback, a log is printed to the console.
### Read-only
To disable controls (everything which can interact with the device: input keys,
mouse events, drag&drop files):
```bash
scrcpy --no-control
scrcpy -n
```
### Forward audio ### Forward audio
Audio is not forwarded by _scrcpy_. Audio is not forwarded by _scrcpy_.

View file

@ -202,6 +202,9 @@ input_manager_process_key(struct input_manager *input_manager,
return; return;
} }
// false if the user requested not to interact with the device
SDL_bool control = input_manager->control;
// capture all Ctrl events // capture all Ctrl events
if (ctrl | meta) { if (ctrl | meta) {
SDL_Keycode keycode = event->keysym.sym; SDL_Keycode keycode = event->keysym.sym;
@ -210,36 +213,36 @@ input_manager_process_key(struct input_manager *input_manager,
SDL_bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT); SDL_bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
switch (keycode) { switch (keycode) {
case SDLK_h: case SDLK_h:
if (ctrl && !meta && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_home(input_manager->controller, action); action_home(input_manager->controller, action);
} }
return; return;
case SDLK_b: // fall-through case SDLK_b: // fall-through
case SDLK_BACKSPACE: case SDLK_BACKSPACE:
if (ctrl && !meta && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_back(input_manager->controller, action); action_back(input_manager->controller, action);
} }
return; return;
case SDLK_s: case SDLK_s:
if (ctrl && !meta && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_app_switch(input_manager->controller, action); action_app_switch(input_manager->controller, action);
} }
return; return;
case SDLK_m: case SDLK_m:
if (ctrl && !meta && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_menu(input_manager->controller, action); action_menu(input_manager->controller, action);
} }
return; return;
case SDLK_p: case SDLK_p:
if (ctrl && !meta && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_power(input_manager->controller, action); action_power(input_manager->controller, action);
} }
return; return;
case SDLK_DOWN: case SDLK_DOWN:
#ifdef __APPLE__ #ifdef __APPLE__
if (!ctrl && meta && !shift) { if (control && !ctrl && meta && !shift) {
#else #else
if (ctrl && !meta && !shift) { if (control && ctrl && !meta && !shift) {
#endif #endif
// forward repeated events // forward repeated events
action_volume_down(input_manager->controller, action); action_volume_down(input_manager->controller, action);
@ -247,16 +250,16 @@ input_manager_process_key(struct input_manager *input_manager,
return; return;
case SDLK_UP: case SDLK_UP:
#ifdef __APPLE__ #ifdef __APPLE__
if (!ctrl && meta && !shift) { if (control && !ctrl && meta && !shift) {
#else #else
if (ctrl && !meta && !shift) { if (control && ctrl && !meta && !shift) {
#endif #endif
// forward repeated events // forward repeated events
action_volume_up(input_manager->controller, action); action_volume_up(input_manager->controller, action);
} }
return; return;
case SDLK_v: case SDLK_v:
if (ctrl && !meta && !shift && !repeat if (control && ctrl && !meta && !shift && !repeat
&& event->type == SDL_KEYDOWN) { && event->type == SDL_KEYDOWN) {
clipboard_paste(input_manager->controller); clipboard_paste(input_manager->controller);
} }
@ -286,7 +289,8 @@ input_manager_process_key(struct input_manager *input_manager,
} }
return; return;
case SDLK_n: case SDLK_n:
if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) { if (control && ctrl && !meta
&& !repeat && event->type == SDL_KEYDOWN) {
if (shift) { if (shift) {
collapse_notification_panel(input_manager->controller); collapse_notification_panel(input_manager->controller);
} else { } else {
@ -299,6 +303,10 @@ input_manager_process_key(struct input_manager *input_manager,
return; return;
} }
if (!control) {
return;
}
struct control_event control_event; struct control_event control_event;
if (input_key_from_sdl_to_android(event, &control_event)) { if (input_key_from_sdl_to_android(event, &control_event)) {
if (!controller_push_event(input_manager->controller, &control_event)) { if (!controller_push_event(input_manager->controller, &control_event)) {
@ -334,18 +342,21 @@ is_outside_device_screen(struct input_manager *input_manager, int x, int y)
void void
input_manager_process_mouse_button(struct input_manager *input_manager, input_manager_process_mouse_button(struct input_manager *input_manager,
const SDL_MouseButtonEvent *event) { const SDL_MouseButtonEvent *event) {
// false if the user requested not to interact with the device
SDL_bool control = input_manager->control;
if (event->type == SDL_MOUSEBUTTONDOWN) { if (event->type == SDL_MOUSEBUTTONDOWN) {
if (event->button == SDL_BUTTON_RIGHT) { if (control && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(input_manager->controller); press_back_or_turn_screen_on(input_manager->controller);
return; return;
} }
if (event->button == SDL_BUTTON_MIDDLE) { if (control && event->button == SDL_BUTTON_MIDDLE) {
action_home(input_manager->controller, ACTION_DOWN | ACTION_UP); action_home(input_manager->controller, ACTION_DOWN | ACTION_UP);
return; return;
} }
// double-click on black borders resize to fit the device screen // double-click on black borders resize to fit the device screen
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) { if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
SDL_bool outside= is_outside_device_screen(input_manager, SDL_bool outside = is_outside_device_screen(input_manager,
event->x, event->x,
event->y); event->y);
if (outside) { if (outside) {
@ -356,6 +367,10 @@ input_manager_process_mouse_button(struct input_manager *input_manager,
// otherwise, send the click event to the device // otherwise, send the click event to the device
} }
if (!control) {
return;
}
struct control_event control_event; struct control_event control_event;
if (mouse_button_from_sdl_to_android(event, if (mouse_button_from_sdl_to_android(event,
input_manager->screen->frame_size, input_manager->screen->frame_size,

View file

@ -11,6 +11,7 @@ struct input_manager {
struct controller *controller; struct controller *controller;
struct video_buffer *video_buffer; struct video_buffer *video_buffer;
struct screen *screen; struct screen *screen;
SDL_bool control;
}; };
void void

View file

@ -16,6 +16,7 @@ struct args {
const char *record_filename; const char *record_filename;
enum recorder_format record_format; enum recorder_format record_format;
SDL_bool fullscreen; SDL_bool fullscreen;
SDL_bool no_control;
SDL_bool no_display; SDL_bool no_display;
SDL_bool help; SDL_bool help;
SDL_bool version; SDL_bool version;
@ -58,6 +59,9 @@ static void usage(const char *arg0) {
" is preserved.\n" " is preserved.\n"
" Default is %d%s.\n" " Default is %d%s.\n"
"\n" "\n"
" -n, --no-control\n"
" Disable device control (mirror the device in read-only).\n"
"\n"
" -N, --no-display\n" " -N, --no-display\n"
" Do not display device (only when screen recording is\n" " Do not display device (only when screen recording is\n"
" enabled).\n" " enabled).\n"
@ -277,6 +281,7 @@ parse_args(struct args *args, int argc, char *argv[]) {
{"fullscreen", no_argument, NULL, 'f'}, {"fullscreen", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'}, {"help", no_argument, NULL, 'h'},
{"max-size", required_argument, NULL, 'm'}, {"max-size", required_argument, NULL, 'm'},
{"no-control", no_argument, NULL, 'n'},
{"no-display", no_argument, NULL, 'N'}, {"no-display", no_argument, NULL, 'N'},
{"port", required_argument, NULL, 'p'}, {"port", required_argument, NULL, 'p'},
{"record", required_argument, NULL, 'r'}, {"record", required_argument, NULL, 'r'},
@ -287,7 +292,7 @@ parse_args(struct args *args, int argc, char *argv[]) {
{NULL, 0, NULL, 0 }, {NULL, 0, NULL, 0 },
}; };
int c; int c;
while ((c = getopt_long(argc, argv, "b:c:fF:hm:Np:r:s:tTv", long_options, while ((c = getopt_long(argc, argv, "b:c:fF:hm:nNp:r:s:tTv", long_options,
NULL)) != -1) { NULL)) != -1) {
switch (c) { switch (c) {
case 'b': case 'b':
@ -314,6 +319,9 @@ parse_args(struct args *args, int argc, char *argv[]) {
return SDL_FALSE; return SDL_FALSE;
} }
break; break;
case 'n':
args->no_control = SDL_TRUE;
break;
case 'N': case 'N':
args->no_display = SDL_TRUE; args->no_display = SDL_TRUE;
break; break;
@ -396,6 +404,7 @@ main(int argc, char *argv[]) {
.max_size = DEFAULT_MAX_SIZE, .max_size = DEFAULT_MAX_SIZE,
.bit_rate = DEFAULT_BIT_RATE, .bit_rate = DEFAULT_BIT_RATE,
.always_on_top = SDL_FALSE, .always_on_top = SDL_FALSE,
.no_control = SDL_FALSE,
.no_display = SDL_FALSE, .no_display = SDL_FALSE,
}; };
if (!parse_args(&args, argc, argv)) { if (!parse_args(&args, argc, argv)) {
@ -435,6 +444,7 @@ main(int argc, char *argv[]) {
.show_touches = args.show_touches, .show_touches = args.show_touches,
.fullscreen = args.fullscreen, .fullscreen = args.fullscreen,
.always_on_top = args.always_on_top, .always_on_top = args.always_on_top,
.no_control = args.no_control,
.no_display = args.no_display, .no_display = args.no_display,
}; };
int res = scrcpy(&options) ? 0 : 1; int res = scrcpy(&options) ? 0 : 1;

View file

@ -39,6 +39,7 @@ static struct input_manager input_manager = {
.controller = &controller, .controller = &controller,
.video_buffer = &video_buffer, .video_buffer = &video_buffer,
.screen = &screen, .screen = &screen,
.control = SDL_TRUE,
}; };
#if defined(__APPLE__) || defined(__WINDOWS__) #if defined(__APPLE__) || defined(__WINDOWS__)
@ -75,7 +76,7 @@ enum event_result {
}; };
static enum event_result static enum event_result
handle_event(SDL_Event *event) { handle_event(SDL_Event *event, SDL_bool control) {
switch (event->type) { switch (event->type) {
case EVENT_STREAM_STOPPED: case EVENT_STREAM_STOPPED:
LOGD("Video stream stopped"); LOGD("Video stream stopped");
@ -102,23 +103,39 @@ handle_event(SDL_Event *event) {
} }
break; break;
case SDL_TEXTINPUT: case SDL_TEXTINPUT:
if (!control) {
break;
}
input_manager_process_text_input(&input_manager, &event->text); input_manager_process_text_input(&input_manager, &event->text);
break; break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: case SDL_KEYUP:
// some key events do not interact with the device, so process the
// event even if control is disabled
input_manager_process_key(&input_manager, &event->key); input_manager_process_key(&input_manager, &event->key);
break; break;
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
if (!control) {
break;
}
input_manager_process_mouse_motion(&input_manager, &event->motion); input_manager_process_mouse_motion(&input_manager, &event->motion);
break; break;
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
if (!control) {
break;
}
input_manager_process_mouse_wheel(&input_manager, &event->wheel); input_manager_process_mouse_wheel(&input_manager, &event->wheel);
break; break;
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
// some mouse events do not interact with the device, so process
// the event even if control is disabled
input_manager_process_mouse_button(&input_manager, &event->button); input_manager_process_mouse_button(&input_manager, &event->button);
break; break;
case SDL_DROPFILE: { case SDL_DROPFILE: {
if (!control) {
break;
}
file_handler_action_t action; file_handler_action_t action;
if (is_apk(event->drop.file)) { if (is_apk(event->drop.file)) {
action = ACTION_INSTALL_APK; action = ACTION_INSTALL_APK;
@ -133,7 +150,7 @@ handle_event(SDL_Event *event) {
} }
static SDL_bool static SDL_bool
event_loop(SDL_bool display) { event_loop(SDL_bool display, SDL_bool control) {
#ifdef CONTINUOUS_RESIZING_WORKAROUND #ifdef CONTINUOUS_RESIZING_WORKAROUND
if (display) { if (display) {
SDL_AddEventWatch(event_watcher, NULL); SDL_AddEventWatch(event_watcher, NULL);
@ -141,7 +158,7 @@ event_loop(SDL_bool display) {
#endif #endif
SDL_Event event; SDL_Event event;
while (SDL_WaitEvent(&event)) { while (SDL_WaitEvent(&event)) {
enum event_result result = handle_event(&event); enum event_result result = handle_event(&event, control);
switch (result) { switch (result) {
case EVENT_RESULT_STOPPED_BY_USER: case EVENT_RESULT_STOPPED_BY_USER:
return SDL_TRUE; return SDL_TRUE;
@ -248,6 +265,9 @@ scrcpy(const struct scrcpy_options *options) {
} }
SDL_bool display = !options->no_display; SDL_bool display = !options->no_display;
SDL_bool control = !options->no_control;
input_manager.control = control;
struct decoder *dec = NULL; struct decoder *dec = NULL;
if (display) { if (display) {
@ -257,7 +277,7 @@ scrcpy(const struct scrcpy_options *options) {
goto finally_destroy_server; goto finally_destroy_server;
} }
if (!file_handler_init(&file_handler, server.serial)) { if (control && !file_handler_init(&file_handler, server.serial)) {
ret = SDL_FALSE; ret = SDL_FALSE;
server_stop(&server); server_stop(&server);
goto finally_destroy_video_buffer; goto finally_destroy_video_buffer;
@ -293,6 +313,7 @@ scrcpy(const struct scrcpy_options *options) {
} }
if (display) { if (display) {
if (control) {
if (!controller_init(&controller, device_socket)) { if (!controller_init(&controller, device_socket)) {
ret = SDL_FALSE; ret = SDL_FALSE;
goto finally_stop_stream; goto finally_stop_stream;
@ -302,6 +323,7 @@ scrcpy(const struct scrcpy_options *options) {
ret = SDL_FALSE; ret = SDL_FALSE;
goto finally_destroy_controller; goto finally_destroy_controller;
} }
}
if (!screen_init_rendering(&screen, device_name, frame_size, if (!screen_init_rendering(&screen, device_name, frame_size,
options->always_on_top)) { options->always_on_top)) {
@ -319,18 +341,18 @@ scrcpy(const struct scrcpy_options *options) {
show_touches_waited = SDL_TRUE; show_touches_waited = SDL_TRUE;
} }
ret = event_loop(display); ret = event_loop(display, control);
LOGD("quit..."); LOGD("quit...");
screen_destroy(&screen); screen_destroy(&screen);
finally_stop_and_join_controller: finally_stop_and_join_controller:
if (display) { if (display && control) {
controller_stop(&controller); controller_stop(&controller);
controller_join(&controller); controller_join(&controller);
} }
finally_destroy_controller: finally_destroy_controller:
if (display) { if (display && control) {
controller_destroy(&controller); controller_destroy(&controller);
} }
finally_stop_stream: finally_stop_stream:
@ -343,7 +365,7 @@ finally_destroy_recorder:
recorder_destroy(&recorder); recorder_destroy(&recorder);
} }
finally_destroy_file_handler: finally_destroy_file_handler:
if (display) { if (display && control) {
file_handler_stop(&file_handler); file_handler_stop(&file_handler);
file_handler_join(&file_handler); file_handler_join(&file_handler);
file_handler_destroy(&file_handler); file_handler_destroy(&file_handler);

View file

@ -15,6 +15,7 @@ struct scrcpy_options {
SDL_bool show_touches; SDL_bool show_touches;
SDL_bool fullscreen; SDL_bool fullscreen;
SDL_bool always_on_top; SDL_bool always_on_top;
SDL_bool no_control;
SDL_bool no_display; SDL_bool no_display;
}; };