diff --git a/README.md b/README.md index a36c81f8..ab0c472f 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,17 @@ _scrcpy_ window. 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 Audio is not forwarded by _scrcpy_. diff --git a/app/src/input_manager.c b/app/src/input_manager.c index f76618d0..33b5b0d3 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -202,6 +202,9 @@ input_manager_process_key(struct input_manager *input_manager, return; } + // false if the user requested not to interact with the device + SDL_bool control = input_manager->control; + // capture all Ctrl events if (ctrl | meta) { 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); switch (keycode) { case SDLK_h: - if (ctrl && !meta && !shift && !repeat) { + if (control && ctrl && !meta && !shift && !repeat) { action_home(input_manager->controller, action); } return; case SDLK_b: // fall-through case SDLK_BACKSPACE: - if (ctrl && !meta && !shift && !repeat) { + if (control && ctrl && !meta && !shift && !repeat) { action_back(input_manager->controller, action); } return; case SDLK_s: - if (ctrl && !meta && !shift && !repeat) { + if (control && ctrl && !meta && !shift && !repeat) { action_app_switch(input_manager->controller, action); } return; case SDLK_m: - if (ctrl && !meta && !shift && !repeat) { + if (control && ctrl && !meta && !shift && !repeat) { action_menu(input_manager->controller, action); } return; case SDLK_p: - if (ctrl && !meta && !shift && !repeat) { + if (control && ctrl && !meta && !shift && !repeat) { action_power(input_manager->controller, action); } return; case SDLK_DOWN: #ifdef __APPLE__ - if (!ctrl && meta && !shift) { + if (control && !ctrl && meta && !shift) { #else - if (ctrl && !meta && !shift) { + if (control && ctrl && !meta && !shift) { #endif // forward repeated events action_volume_down(input_manager->controller, action); @@ -247,16 +250,16 @@ input_manager_process_key(struct input_manager *input_manager, return; case SDLK_UP: #ifdef __APPLE__ - if (!ctrl && meta && !shift) { + if (control && !ctrl && meta && !shift) { #else - if (ctrl && !meta && !shift) { + if (control && ctrl && !meta && !shift) { #endif // forward repeated events action_volume_up(input_manager->controller, action); } return; case SDLK_v: - if (ctrl && !meta && !shift && !repeat + if (control && ctrl && !meta && !shift && !repeat && event->type == SDL_KEYDOWN) { clipboard_paste(input_manager->controller); } @@ -286,7 +289,8 @@ input_manager_process_key(struct input_manager *input_manager, } return; case SDLK_n: - if (ctrl && !meta && !repeat && event->type == SDL_KEYDOWN) { + if (control && ctrl && !meta + && !repeat && event->type == SDL_KEYDOWN) { if (shift) { collapse_notification_panel(input_manager->controller); } else { @@ -299,6 +303,10 @@ input_manager_process_key(struct input_manager *input_manager, return; } + if (!control) { + return; + } + struct control_event control_event; if (input_key_from_sdl_to_android(event, &control_event)) { if (!controller_push_event(input_manager->controller, &control_event)) { @@ -334,20 +342,23 @@ is_outside_device_screen(struct input_manager *input_manager, int x, int y) void input_manager_process_mouse_button(struct input_manager *input_manager, 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->button == SDL_BUTTON_RIGHT) { + if (control && event->button == SDL_BUTTON_RIGHT) { press_back_or_turn_screen_on(input_manager->controller); return; } - if (event->button == SDL_BUTTON_MIDDLE) { + if (control && event->button == SDL_BUTTON_MIDDLE) { action_home(input_manager->controller, ACTION_DOWN | ACTION_UP); return; } // double-click on black borders resize to fit the device screen if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) { - SDL_bool outside= is_outside_device_screen(input_manager, - event->x, - event->y); + SDL_bool outside = is_outside_device_screen(input_manager, + event->x, + event->y); if (outside) { screen_resize_to_fit(input_manager->screen); return; @@ -356,6 +367,10 @@ input_manager_process_mouse_button(struct input_manager *input_manager, // otherwise, send the click event to the device } + if (!control) { + return; + } + struct control_event control_event; if (mouse_button_from_sdl_to_android(event, input_manager->screen->frame_size, diff --git a/app/src/input_manager.h b/app/src/input_manager.h index 62923e1e..28a9a879 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -11,6 +11,7 @@ struct input_manager { struct controller *controller; struct video_buffer *video_buffer; struct screen *screen; + SDL_bool control; }; void diff --git a/app/src/main.c b/app/src/main.c index d39fcd77..a4d705ce 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -16,6 +16,7 @@ struct args { const char *record_filename; enum recorder_format record_format; SDL_bool fullscreen; + SDL_bool no_control; SDL_bool no_display; SDL_bool help; SDL_bool version; @@ -58,6 +59,9 @@ static void usage(const char *arg0) { " is preserved.\n" " Default is %d%s.\n" "\n" + " -n, --no-control\n" + " Disable device control (mirror the device in read-only).\n" + "\n" " -N, --no-display\n" " Do not display device (only when screen recording is\n" " enabled).\n" @@ -277,6 +281,7 @@ parse_args(struct args *args, int argc, char *argv[]) { {"fullscreen", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"max-size", required_argument, NULL, 'm'}, + {"no-control", no_argument, NULL, 'n'}, {"no-display", no_argument, NULL, 'N'}, {"port", required_argument, NULL, 'p'}, {"record", required_argument, NULL, 'r'}, @@ -287,7 +292,7 @@ parse_args(struct args *args, int argc, char *argv[]) { {NULL, 0, NULL, 0 }, }; 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) { switch (c) { case 'b': @@ -314,6 +319,9 @@ parse_args(struct args *args, int argc, char *argv[]) { return SDL_FALSE; } break; + case 'n': + args->no_control = SDL_TRUE; + break; case 'N': args->no_display = SDL_TRUE; break; @@ -396,6 +404,7 @@ main(int argc, char *argv[]) { .max_size = DEFAULT_MAX_SIZE, .bit_rate = DEFAULT_BIT_RATE, .always_on_top = SDL_FALSE, + .no_control = SDL_FALSE, .no_display = SDL_FALSE, }; if (!parse_args(&args, argc, argv)) { @@ -435,6 +444,7 @@ main(int argc, char *argv[]) { .show_touches = args.show_touches, .fullscreen = args.fullscreen, .always_on_top = args.always_on_top, + .no_control = args.no_control, .no_display = args.no_display, }; int res = scrcpy(&options) ? 0 : 1; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 11349807..15ffdd32 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -39,6 +39,7 @@ static struct input_manager input_manager = { .controller = &controller, .video_buffer = &video_buffer, .screen = &screen, + .control = SDL_TRUE, }; #if defined(__APPLE__) || defined(__WINDOWS__) @@ -75,7 +76,7 @@ enum event_result { }; static enum event_result -handle_event(SDL_Event *event) { +handle_event(SDL_Event *event, SDL_bool control) { switch (event->type) { case EVENT_STREAM_STOPPED: LOGD("Video stream stopped"); @@ -102,23 +103,39 @@ handle_event(SDL_Event *event) { } break; case SDL_TEXTINPUT: + if (!control) { + break; + } input_manager_process_text_input(&input_manager, &event->text); break; case SDL_KEYDOWN: 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); break; case SDL_MOUSEMOTION: + if (!control) { + break; + } input_manager_process_mouse_motion(&input_manager, &event->motion); break; case SDL_MOUSEWHEEL: + if (!control) { + break; + } input_manager_process_mouse_wheel(&input_manager, &event->wheel); break; case SDL_MOUSEBUTTONDOWN: 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); break; case SDL_DROPFILE: { + if (!control) { + break; + } file_handler_action_t action; if (is_apk(event->drop.file)) { action = ACTION_INSTALL_APK; @@ -133,7 +150,7 @@ handle_event(SDL_Event *event) { } static SDL_bool -event_loop(SDL_bool display) { +event_loop(SDL_bool display, SDL_bool control) { #ifdef CONTINUOUS_RESIZING_WORKAROUND if (display) { SDL_AddEventWatch(event_watcher, NULL); @@ -141,7 +158,7 @@ event_loop(SDL_bool display) { #endif SDL_Event event; while (SDL_WaitEvent(&event)) { - enum event_result result = handle_event(&event); + enum event_result result = handle_event(&event, control); switch (result) { case EVENT_RESULT_STOPPED_BY_USER: return SDL_TRUE; @@ -248,6 +265,9 @@ scrcpy(const struct scrcpy_options *options) { } SDL_bool display = !options->no_display; + SDL_bool control = !options->no_control; + + input_manager.control = control; struct decoder *dec = NULL; if (display) { @@ -257,7 +277,7 @@ scrcpy(const struct scrcpy_options *options) { goto finally_destroy_server; } - if (!file_handler_init(&file_handler, server.serial)) { + if (control && !file_handler_init(&file_handler, server.serial)) { ret = SDL_FALSE; server_stop(&server); goto finally_destroy_video_buffer; @@ -293,14 +313,16 @@ scrcpy(const struct scrcpy_options *options) { } if (display) { - if (!controller_init(&controller, device_socket)) { - ret = SDL_FALSE; - goto finally_stop_stream; - } + if (control) { + if (!controller_init(&controller, device_socket)) { + ret = SDL_FALSE; + goto finally_stop_stream; + } - if (!controller_start(&controller)) { - ret = SDL_FALSE; - goto finally_destroy_controller; + if (!controller_start(&controller)) { + ret = SDL_FALSE; + goto finally_destroy_controller; + } } if (!screen_init_rendering(&screen, device_name, frame_size, @@ -319,18 +341,18 @@ scrcpy(const struct scrcpy_options *options) { show_touches_waited = SDL_TRUE; } - ret = event_loop(display); + ret = event_loop(display, control); LOGD("quit..."); screen_destroy(&screen); finally_stop_and_join_controller: - if (display) { + if (display && control) { controller_stop(&controller); controller_join(&controller); } finally_destroy_controller: - if (display) { + if (display && control) { controller_destroy(&controller); } finally_stop_stream: @@ -343,7 +365,7 @@ finally_destroy_recorder: recorder_destroy(&recorder); } finally_destroy_file_handler: - if (display) { + if (display && control) { file_handler_stop(&file_handler); file_handler_join(&file_handler); file_handler_destroy(&file_handler); diff --git a/app/src/scrcpy.h b/app/src/scrcpy.h index 7f4fc313..f5b6be26 100644 --- a/app/src/scrcpy.h +++ b/app/src/scrcpy.h @@ -15,6 +15,7 @@ struct scrcpy_options { SDL_bool show_touches; SDL_bool fullscreen; SDL_bool always_on_top; + SDL_bool no_control; SDL_bool no_display; };