Add option to mirror in read-only
Add an option to disable device control: -n/--no-control.
This commit is contained in:
parent
163cd36ccc
commit
8655ba7197
6 changed files with 92 additions and 32 deletions
11
README.md
11
README.md
|
@ -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_.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue