Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
e250628fae | |||
a26c4e76da |
@ -316,6 +316,10 @@ Disable video forwarding.
|
|||||||
.B \-\-no\-video\-playback
|
.B \-\-no\-video\-playback
|
||||||
Disable video playback on the computer.
|
Disable video playback on the computer.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-\-no\-window
|
||||||
|
Disable scrcpy window. Implies --no-video-playback and --no-control.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-orientation " value
|
.BI "\-\-orientation " value
|
||||||
Same as --display-orientation=value --record-orientation=value.
|
Same as --display-orientation=value --record-orientation=value.
|
||||||
|
@ -97,6 +97,7 @@ enum {
|
|||||||
OPT_MOUSE,
|
OPT_MOUSE,
|
||||||
OPT_HID_KEYBOARD_DEPRECATED,
|
OPT_HID_KEYBOARD_DEPRECATED,
|
||||||
OPT_HID_MOUSE_DEPRECATED,
|
OPT_HID_MOUSE_DEPRECATED,
|
||||||
|
OPT_NO_WINDOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
@ -566,6 +567,12 @@ static const struct sc_option options[] = {
|
|||||||
.longopt = "no-video-playback",
|
.longopt = "no-video-playback",
|
||||||
.text = "Disable video playback on the computer.",
|
.text = "Disable video playback on the computer.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_NO_WINDOW,
|
||||||
|
.longopt = "no-window",
|
||||||
|
.text = "Disable scrcpy window. Implies --no-video-playback and "
|
||||||
|
"--no-control.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.longopt_id = OPT_ORIENTATION,
|
.longopt_id = OPT_ORIENTATION,
|
||||||
.longopt = "orientation",
|
.longopt = "orientation",
|
||||||
@ -2486,6 +2493,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
case OPT_CAMERA_HIGH_SPEED:
|
case OPT_CAMERA_HIGH_SPEED:
|
||||||
opts->camera_high_speed = true;
|
opts->camera_high_speed = true;
|
||||||
break;
|
break;
|
||||||
|
case OPT_NO_WINDOW:
|
||||||
|
opts->window = false;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// getopt prints the error message on stderr
|
// getopt prints the error message on stderr
|
||||||
return false;
|
return false;
|
||||||
@ -2523,6 +2533,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
v4l2 = !!opts->v4l2_device;
|
v4l2 = !!opts->v4l2_device;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!opts->window) {
|
||||||
|
// Without window, there cannot be any video playback or control
|
||||||
|
opts->video_playback = false;
|
||||||
|
opts->control = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!opts->video) {
|
if (!opts->video) {
|
||||||
opts->video_playback = false;
|
opts->video_playback = false;
|
||||||
// Do not power on the device on start if video capture is disabled
|
// Do not power on the device on start if video capture is disabled
|
||||||
@ -2544,8 +2560,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
opts->audio = false;
|
opts->audio = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts->video && !opts->audio && !otg) {
|
if (!opts->video && !opts->audio && !opts->control && !otg) {
|
||||||
LOGE("No video, no audio, no OTG: nothing to do");
|
LOGE("No video, no audio, no control, no OTG: nothing to do");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2588,13 +2604,26 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
|
if (opts->control) {
|
||||||
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
|
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
|
||||||
: SC_KEYBOARD_INPUT_MODE_SDK;
|
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
|
||||||
}
|
: SC_KEYBOARD_INPUT_MODE_SDK;
|
||||||
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
|
}
|
||||||
opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA
|
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
|
||||||
: SC_MOUSE_INPUT_MODE_SDK;
|
if (otg) {
|
||||||
|
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
|
||||||
|
} else if (!opts->video_playback) {
|
||||||
|
LOGI("No video mirroring, SDK mouse disabled (you might want "
|
||||||
|
"--mouse=uhid)");
|
||||||
|
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_DISABLED;
|
||||||
|
} else {
|
||||||
|
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
|
||||||
|
}
|
||||||
|
} else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK
|
||||||
|
&& !opts->video_playback) {
|
||||||
|
LOGE("SDK mouse mode requires video playback. Try --mouse=uhid.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otg) {
|
if (otg) {
|
||||||
|
@ -5,8 +5,30 @@
|
|||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_display_init_novideo_icon(struct sc_display *display,
|
||||||
|
SDL_Surface *icon_novideo) {
|
||||||
|
assert(icon_novideo);
|
||||||
|
|
||||||
|
if (SDL_RenderSetLogicalSize(display->renderer,
|
||||||
|
icon_novideo->w, icon_novideo->h)) {
|
||||||
|
LOGW("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
|
// don't fail
|
||||||
|
}
|
||||||
|
|
||||||
|
display->texture = SDL_CreateTextureFromSurface(display->renderer,
|
||||||
|
icon_novideo);
|
||||||
|
if (!display->texture) {
|
||||||
|
LOGE("Could not create texture: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
|
sc_display_init(struct sc_display *display, SDL_Window *window,
|
||||||
|
SDL_Surface *icon_novideo, bool mipmaps) {
|
||||||
display->renderer =
|
display->renderer =
|
||||||
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
if (!display->renderer) {
|
if (!display->renderer) {
|
||||||
@ -68,6 +90,18 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
|
|||||||
display->pending.frame = NULL;
|
display->pending.frame = NULL;
|
||||||
display->has_frame = false;
|
display->has_frame = false;
|
||||||
|
|
||||||
|
if (icon_novideo) {
|
||||||
|
// Without video, set a static scrcpy icon as window content
|
||||||
|
bool ok = sc_display_init_novideo_icon(display, icon_novideo);
|
||||||
|
if (!ok) {
|
||||||
|
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
|
||||||
|
SDL_GL_DeleteContext(display->gl_context);
|
||||||
|
#endif
|
||||||
|
SDL_DestroyRenderer(display->renderer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,8 @@ enum sc_display_result {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps);
|
sc_display_init(struct sc_display *display, SDL_Window *window,
|
||||||
|
SDL_Surface *icon_novideo, bool mipmaps);
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_display_destroy(struct sc_display *display);
|
sc_display_destroy(struct sc_display *display);
|
||||||
|
@ -403,6 +403,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
// controller is NULL if --no-control is requested
|
// controller is NULL if --no-control is requested
|
||||||
bool control = im->controller;
|
bool control = im->controller;
|
||||||
bool paused = im->screen->paused;
|
bool paused = im->screen->paused;
|
||||||
|
bool video = im->screen->video;
|
||||||
|
|
||||||
SDL_Keycode keycode = event->keysym.sym;
|
SDL_Keycode keycode = event->keysym.sym;
|
||||||
uint16_t mod = event->keysym.mod;
|
uint16_t mod = event->keysym.mod;
|
||||||
@ -462,13 +463,13 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_z:
|
case SDLK_z:
|
||||||
if (down && !repeat) {
|
if (video && down && !repeat) {
|
||||||
sc_screen_set_paused(im->screen, !shift);
|
sc_screen_set_paused(im->screen, !shift);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
if (shift) {
|
if (shift) {
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_180);
|
SC_ORIENTATION_FLIP_180);
|
||||||
}
|
}
|
||||||
@ -479,7 +480,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
return;
|
return;
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
if (shift) {
|
if (shift) {
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_180);
|
SC_ORIENTATION_FLIP_180);
|
||||||
}
|
}
|
||||||
@ -489,7 +490,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_0);
|
SC_ORIENTATION_FLIP_0);
|
||||||
@ -500,7 +501,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_0);
|
SC_ORIENTATION_FLIP_0);
|
||||||
@ -533,22 +534,22 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_f:
|
case SDLK_f:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
sc_screen_switch_fullscreen(im->screen);
|
sc_screen_switch_fullscreen(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_w:
|
case SDLK_w:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
sc_screen_resize_to_fit(im->screen);
|
sc_screen_resize_to_fit(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
sc_screen_resize_to_pixel_perfect(im->screen);
|
sc_screen_resize_to_pixel_perfect(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_i:
|
case SDLK_i:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
switch_fps_counter_state(im);
|
switch_fps_counter_state(im);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -625,6 +626,23 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
|
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct sc_position
|
||||||
|
sc_input_manager_get_position(struct sc_input_manager *im, int32_t x,
|
||||||
|
int32_t y) {
|
||||||
|
if (im->mp->relative_mode) {
|
||||||
|
// No absolute position
|
||||||
|
return (struct sc_position) {
|
||||||
|
.screen_size = {0, 0},
|
||||||
|
.point = {0, 0},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (struct sc_position) {
|
||||||
|
.screen_size = im->screen->frame_size,
|
||||||
|
.point = sc_screen_convert_window_to_frame_coords(im->screen, x, y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
||||||
const SDL_MouseMotionEvent *event) {
|
const SDL_MouseMotionEvent *event) {
|
||||||
@ -634,12 +652,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct sc_mouse_motion_event evt = {
|
struct sc_mouse_motion_event evt = {
|
||||||
.position = {
|
.position = sc_input_manager_get_position(im, event->x, event->y),
|
||||||
.screen_size = im->screen->frame_size,
|
|
||||||
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
||||||
event->x,
|
|
||||||
event->y),
|
|
||||||
},
|
|
||||||
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
||||||
: POINTER_ID_GENERIC_FINGER,
|
: POINTER_ID_GENERIC_FINGER,
|
||||||
.xrel = event->xrel,
|
.xrel = event->xrel,
|
||||||
@ -759,12 +772,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||||||
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
||||||
|
|
||||||
struct sc_mouse_click_event evt = {
|
struct sc_mouse_click_event evt = {
|
||||||
.position = {
|
.position = sc_input_manager_get_position(im, event->x, event->y),
|
||||||
.screen_size = im->screen->frame_size,
|
|
||||||
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
||||||
event->x,
|
|
||||||
event->y),
|
|
||||||
},
|
|
||||||
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
||||||
.button = sc_mouse_button_from_sdl(event->button),
|
.button = sc_mouse_button_from_sdl(event->button),
|
||||||
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
||||||
@ -839,11 +847,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
|||||||
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||||
|
|
||||||
struct sc_mouse_scroll_event evt = {
|
struct sc_mouse_scroll_event evt = {
|
||||||
.position = {
|
.position = sc_input_manager_get_position(im, mouse_x, mouse_y),
|
||||||
.screen_size = im->screen->frame_size,
|
|
||||||
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
||||||
mouse_x, mouse_y),
|
|
||||||
},
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||||
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
|
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
|
||||||
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
|
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
|
||||||
|
@ -89,6 +89,7 @@ const struct scrcpy_options scrcpy_options_default = {
|
|||||||
.kill_adb_on_close = false,
|
.kill_adb_on_close = false,
|
||||||
.camera_high_speed = false,
|
.camera_high_speed = false,
|
||||||
.list = 0,
|
.list = 0,
|
||||||
|
.window = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sc_orientation
|
enum sc_orientation
|
||||||
|
@ -279,6 +279,7 @@ struct scrcpy_options {
|
|||||||
#define SC_OPTION_LIST_CAMERAS 0x4
|
#define SC_OPTION_LIST_CAMERAS 0x4
|
||||||
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
|
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
|
||||||
uint8_t list;
|
uint8_t list;
|
||||||
|
bool window;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct scrcpy_options scrcpy_options_default;
|
extern const struct scrcpy_options scrcpy_options_default;
|
||||||
|
@ -408,7 +408,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
return SCRCPY_EXIT_FAILURE;
|
return SCRCPY_EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->video_playback) {
|
if (options->window) {
|
||||||
// Set hints before starting the server thread to avoid race conditions
|
// Set hints before starting the server thread to avoid race conditions
|
||||||
// in SDL
|
// in SDL
|
||||||
sdl_set_hints(options->render_driver);
|
sdl_set_hints(options->render_driver);
|
||||||
@ -430,7 +430,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
assert(!options->video_playback || options->video);
|
assert(!options->video_playback || options->video);
|
||||||
assert(!options->audio_playback || options->audio);
|
assert(!options->audio_playback || options->audio);
|
||||||
|
|
||||||
if (options->video_playback ||
|
if (options->window ||
|
||||||
(options->control && options->clipboard_autosync)) {
|
(options->control && options->clipboard_autosync)) {
|
||||||
// Initialize the video subsystem even if --no-video or
|
// Initialize the video subsystem even if --no-video or
|
||||||
// --no-video-playback is passed so that clipboard synchronization
|
// --no-video-playback is passed so that clipboard synchronization
|
||||||
@ -684,11 +684,12 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
// There is a controller if and only if control is enabled
|
// There is a controller if and only if control is enabled
|
||||||
assert(options->control == !!controller);
|
assert(options->control == !!controller);
|
||||||
|
|
||||||
if (options->video_playback) {
|
if (options->window) {
|
||||||
const char *window_title =
|
const char *window_title =
|
||||||
options->window_title ? options->window_title : info->device_name;
|
options->window_title ? options->window_title : info->device_name;
|
||||||
|
|
||||||
struct sc_screen_params screen_params = {
|
struct sc_screen_params screen_params = {
|
||||||
|
.video = options->video_playback,
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.fp = fp,
|
.fp = fp,
|
||||||
.kp = kp,
|
.kp = kp,
|
||||||
@ -710,12 +711,15 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
.start_fps_counter = options->start_fps_counter,
|
.start_fps_counter = options->start_fps_counter,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_frame_source *src = &s->video_decoder.frame_source;
|
struct sc_frame_source *src;
|
||||||
if (options->display_buffer) {
|
if (options->video_playback) {
|
||||||
sc_delay_buffer_init(&s->display_buffer, options->display_buffer,
|
src = &s->video_decoder.frame_source;
|
||||||
true);
|
if (options->display_buffer) {
|
||||||
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
|
sc_delay_buffer_init(&s->display_buffer,
|
||||||
src = &s->display_buffer.frame_source;
|
options->display_buffer, true);
|
||||||
|
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
|
||||||
|
src = &s->display_buffer.frame_source;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sc_screen_init(&s->screen, &screen_params)) {
|
if (!sc_screen_init(&s->screen, &screen_params)) {
|
||||||
@ -723,7 +727,9 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
screen_initialized = true;
|
screen_initialized = true;
|
||||||
|
|
||||||
sc_frame_source_add_sink(src, &s->screen.frame_sink);
|
if (options->video_playback) {
|
||||||
|
sc_frame_source_add_sink(src, &s->screen.frame_sink);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->audio_playback) {
|
if (options->audio_playback) {
|
||||||
|
123
app/src/screen.c
123
app/src/screen.c
@ -205,6 +205,8 @@ sc_screen_toggle_mouse_capture(struct sc_screen *screen) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
sc_screen_update_content_rect(struct sc_screen *screen) {
|
sc_screen_update_content_rect(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
int dw;
|
int dw;
|
||||||
int dh;
|
int dh;
|
||||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||||
@ -246,6 +248,8 @@ sc_screen_update_content_rect(struct sc_screen *screen) {
|
|||||||
// changed, so that the content rectangle is recomputed
|
// changed, so that the content rectangle is recomputed
|
||||||
static void
|
static void
|
||||||
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (update_content_rect) {
|
if (update_content_rect) {
|
||||||
sc_screen_update_content_rect(screen);
|
sc_screen_update_content_rect(screen);
|
||||||
}
|
}
|
||||||
@ -255,6 +259,13 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
|||||||
(void) res; // any error already logged
|
(void) res; // any error already logged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_screen_render_novideo(struct sc_screen *screen) {
|
||||||
|
enum sc_display_result res =
|
||||||
|
sc_display_render(&screen->display, NULL, SC_ORIENTATION_0);
|
||||||
|
(void) res; // any error already logged
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__WINDOWS__)
|
#if defined(__APPLE__) || defined(__WINDOWS__)
|
||||||
# define CONTINUOUS_RESIZING_WORKAROUND
|
# define CONTINUOUS_RESIZING_WORKAROUND
|
||||||
#endif
|
#endif
|
||||||
@ -268,6 +279,8 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
|||||||
static int
|
static int
|
||||||
event_watcher(void *data, SDL_Event *event) {
|
event_watcher(void *data, SDL_Event *event) {
|
||||||
struct sc_screen *screen = data;
|
struct sc_screen *screen = data;
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (event->type == SDL_WINDOWEVENT
|
if (event->type == SDL_WINDOWEVENT
|
||||||
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
// In practice, it seems to always be called from the same thread in
|
// In practice, it seems to always be called from the same thread in
|
||||||
@ -326,6 +339,7 @@ sc_screen_frame_sink_close(struct sc_frame_sink *sink) {
|
|||||||
static bool
|
static bool
|
||||||
sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
||||||
struct sc_screen *screen = DOWNCAST(sink);
|
struct sc_screen *screen = DOWNCAST(sink);
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
bool previous_skipped;
|
bool previous_skipped;
|
||||||
bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped);
|
bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped);
|
||||||
@ -364,6 +378,9 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
screen->mouse_capture_key_pressed = 0;
|
screen->mouse_capture_key_pressed = 0;
|
||||||
screen->paused = false;
|
screen->paused = false;
|
||||||
screen->resume_frame = NULL;
|
screen->resume_frame = NULL;
|
||||||
|
screen->orientation = SC_ORIENTATION_0;
|
||||||
|
|
||||||
|
screen->video = params->video;
|
||||||
|
|
||||||
screen->req.x = params->window_x;
|
screen->req.x = params->window_x;
|
||||||
screen->req.y = params->window_y;
|
screen->req.y = params->window_y;
|
||||||
@ -381,41 +398,75 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
goto error_destroy_frame_buffer;
|
goto error_destroy_frame_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
screen->orientation = params->orientation;
|
if (screen->video) {
|
||||||
if (screen->orientation != SC_ORIENTATION_0) {
|
screen->orientation = params->orientation;
|
||||||
LOGI("Initial display orientation set to %s",
|
if (screen->orientation != SC_ORIENTATION_0) {
|
||||||
sc_orientation_get_name(screen->orientation));
|
LOGI("Initial display orientation set to %s",
|
||||||
|
sc_orientation_get_name(screen->orientation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t window_flags = SDL_WINDOW_HIDDEN
|
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
| SDL_WINDOW_RESIZABLE
|
|
||||||
| SDL_WINDOW_ALLOW_HIGHDPI;
|
|
||||||
if (params->always_on_top) {
|
if (params->always_on_top) {
|
||||||
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
||||||
}
|
}
|
||||||
if (params->window_borderless) {
|
if (params->window_borderless) {
|
||||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||||
}
|
}
|
||||||
|
if (params->video) {
|
||||||
|
// The window will be shown on first frame
|
||||||
|
window_flags |= SDL_WINDOW_HIDDEN
|
||||||
|
| SDL_WINDOW_RESIZABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *title = params->window_title;
|
||||||
|
assert(title);
|
||||||
|
|
||||||
|
int x = SDL_WINDOWPOS_UNDEFINED;
|
||||||
|
int y = SDL_WINDOWPOS_UNDEFINED;
|
||||||
|
int width = 256;
|
||||||
|
int height = 256;
|
||||||
|
if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) {
|
||||||
|
x = params->window_x;
|
||||||
|
}
|
||||||
|
if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) {
|
||||||
|
y = params->window_y;
|
||||||
|
}
|
||||||
|
if (params->window_width) {
|
||||||
|
width = params->window_width;
|
||||||
|
}
|
||||||
|
if (params->window_height) {
|
||||||
|
height = params->window_height;
|
||||||
|
}
|
||||||
|
|
||||||
// The window will be positioned and sized on first video frame
|
// The window will be positioned and sized on first video frame
|
||||||
screen->window =
|
screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
|
||||||
SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags);
|
|
||||||
if (!screen->window) {
|
if (!screen->window) {
|
||||||
LOGE("Could not create window: %s", SDL_GetError());
|
LOGE("Could not create window: %s", SDL_GetError());
|
||||||
goto error_destroy_fps_counter;
|
goto error_destroy_fps_counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_display_init(&screen->display, screen->window, params->mipmaps);
|
|
||||||
if (!ok) {
|
|
||||||
goto error_destroy_window;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Surface *icon = scrcpy_icon_load();
|
SDL_Surface *icon = scrcpy_icon_load();
|
||||||
if (icon) {
|
if (icon) {
|
||||||
SDL_SetWindowIcon(screen->window, icon);
|
SDL_SetWindowIcon(screen->window, icon);
|
||||||
scrcpy_icon_destroy(icon);
|
} else if (params->video) {
|
||||||
} else {
|
// just a warning
|
||||||
LOGW("Could not load icon");
|
LOGW("Could not load icon");
|
||||||
|
} else {
|
||||||
|
// without video, the icon is used as window content, it must be present
|
||||||
|
LOGE("Could not load icon");
|
||||||
|
goto error_destroy_fps_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *icon_novideo = params->video ? NULL : icon;
|
||||||
|
bool mipmaps = params->video && params->mipmaps;
|
||||||
|
ok = sc_display_init(&screen->display, screen->window, icon_novideo,
|
||||||
|
mipmaps);
|
||||||
|
if (icon) {
|
||||||
|
scrcpy_icon_destroy(icon);
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
goto error_destroy_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
screen->frame = av_frame_alloc();
|
screen->frame = av_frame_alloc();
|
||||||
@ -439,7 +490,9 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
sc_input_manager_init(&screen->im, &im_params);
|
sc_input_manager_init(&screen->im, &im_params);
|
||||||
|
|
||||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||||
SDL_AddEventWatch(event_watcher, screen);
|
if (screen->video) {
|
||||||
|
SDL_AddEventWatch(event_watcher, screen);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct sc_frame_sink_ops ops = {
|
static const struct sc_frame_sink_ops ops = {
|
||||||
@ -454,6 +507,11 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
screen->open = false;
|
screen->open = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!screen->video && sc_screen_is_relative_mode(screen)) {
|
||||||
|
// Capture mouse immediately if video mirroring is disabled
|
||||||
|
sc_screen_set_mouse_capture(screen, true);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error_destroy_display:
|
error_destroy_display:
|
||||||
@ -524,6 +582,8 @@ sc_screen_destroy(struct sc_screen *screen) {
|
|||||||
static void
|
static void
|
||||||
resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
||||||
struct sc_size new_content_size) {
|
struct sc_size new_content_size) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
struct sc_size window_size = get_window_size(screen);
|
struct sc_size window_size = get_window_size(screen);
|
||||||
struct sc_size target_size = {
|
struct sc_size target_size = {
|
||||||
.width = (uint32_t) window_size.width * new_content_size.width
|
.width = (uint32_t) window_size.width * new_content_size.width
|
||||||
@ -537,6 +597,8 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (!screen->fullscreen && !screen->maximized && !screen->minimized) {
|
if (!screen->fullscreen && !screen->maximized && !screen->minimized) {
|
||||||
resize_for_content(screen, screen->content_size, new_content_size);
|
resize_for_content(screen, screen->content_size, new_content_size);
|
||||||
} else if (!screen->resize_pending) {
|
} else if (!screen->resize_pending) {
|
||||||
@ -551,6 +613,8 @@ set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
apply_pending_resize(struct sc_screen *screen) {
|
apply_pending_resize(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
assert(!screen->fullscreen);
|
assert(!screen->fullscreen);
|
||||||
assert(!screen->maximized);
|
assert(!screen->maximized);
|
||||||
assert(!screen->minimized);
|
assert(!screen->minimized);
|
||||||
@ -564,6 +628,8 @@ apply_pending_resize(struct sc_screen *screen) {
|
|||||||
void
|
void
|
||||||
sc_screen_set_orientation(struct sc_screen *screen,
|
sc_screen_set_orientation(struct sc_screen *screen,
|
||||||
enum sc_orientation orientation) {
|
enum sc_orientation orientation) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (orientation == screen->orientation) {
|
if (orientation == screen->orientation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -598,6 +664,8 @@ sc_screen_init_size(struct sc_screen *screen) {
|
|||||||
// recreate the texture and resize the window if the frame size has changed
|
// recreate the texture and resize the window if the frame size has changed
|
||||||
static enum sc_display_result
|
static enum sc_display_result
|
||||||
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->frame_size.width == new_frame_size.width
|
if (screen->frame_size.width == new_frame_size.width
|
||||||
&& screen->frame_size.height == new_frame_size.height) {
|
&& screen->frame_size.height == new_frame_size.height) {
|
||||||
return SC_DISPLAY_RESULT_OK;
|
return SC_DISPLAY_RESULT_OK;
|
||||||
@ -617,6 +685,8 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
sc_screen_apply_frame(struct sc_screen *screen) {
|
sc_screen_apply_frame(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
sc_fps_counter_add_rendered_frame(&screen->fps_counter);
|
sc_fps_counter_add_rendered_frame(&screen->fps_counter);
|
||||||
|
|
||||||
AVFrame *frame = screen->frame;
|
AVFrame *frame = screen->frame;
|
||||||
@ -656,6 +726,8 @@ sc_screen_apply_frame(struct sc_screen *screen) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
sc_screen_update_frame(struct sc_screen *screen) {
|
sc_screen_update_frame(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->paused) {
|
if (screen->paused) {
|
||||||
if (!screen->resume_frame) {
|
if (!screen->resume_frame) {
|
||||||
screen->resume_frame = av_frame_alloc();
|
screen->resume_frame = av_frame_alloc();
|
||||||
@ -677,6 +749,8 @@ sc_screen_update_frame(struct sc_screen *screen) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_set_paused(struct sc_screen *screen, bool paused) {
|
sc_screen_set_paused(struct sc_screen *screen, bool paused) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (!paused && !screen->paused) {
|
if (!paused && !screen->paused) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
return;
|
return;
|
||||||
@ -704,6 +778,8 @@ sc_screen_set_paused(struct sc_screen *screen, bool paused) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
|
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
|
||||||
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
|
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
|
||||||
@ -721,6 +797,8 @@ sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_resize_to_fit(struct sc_screen *screen) {
|
sc_screen_resize_to_fit(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->fullscreen || screen->maximized || screen->minimized) {
|
if (screen->fullscreen || screen->maximized || screen->minimized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -745,6 +823,8 @@ sc_screen_resize_to_fit(struct sc_screen *screen) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->fullscreen || screen->minimized) {
|
if (screen->fullscreen || screen->minimized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -788,6 +868,13 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
|
if (!screen->video
|
||||||
|
&& event->window.event == SDL_WINDOWEVENT_EXPOSED) {
|
||||||
|
sc_screen_render_novideo(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// !video implies !has_frame
|
||||||
|
assert(screen->video || !screen->has_frame);
|
||||||
if (!screen->has_frame) {
|
if (!screen->has_frame) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return true;
|
return true;
|
||||||
@ -891,6 +978,8 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
|||||||
struct sc_point
|
struct sc_point
|
||||||
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
||||||
int32_t x, int32_t y) {
|
int32_t x, int32_t y) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
enum sc_orientation orientation = screen->orientation;
|
enum sc_orientation orientation = screen->orientation;
|
||||||
|
|
||||||
int32_t w = screen->content_size.width;
|
int32_t w = screen->content_size.width;
|
||||||
|
@ -26,6 +26,8 @@ struct sc_screen {
|
|||||||
bool open; // track the open/close state to assert correct behavior
|
bool open; // track the open/close state to assert correct behavior
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool video;
|
||||||
|
|
||||||
struct sc_display display;
|
struct sc_display display;
|
||||||
struct sc_input_manager im;
|
struct sc_input_manager im;
|
||||||
struct sc_frame_buffer fb;
|
struct sc_frame_buffer fb;
|
||||||
@ -70,6 +72,8 @@ struct sc_screen {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sc_screen_params {
|
struct sc_screen_params {
|
||||||
|
bool video;
|
||||||
|
|
||||||
struct sc_controller *controller;
|
struct sc_controller *controller;
|
||||||
struct sc_file_pusher *fp;
|
struct sc_file_pusher *fp;
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
|
11
doc/audio.md
11
doc/audio.md
@ -28,10 +28,17 @@ To disable only the audio playback, see [no playback](video.md#no-playback).
|
|||||||
|
|
||||||
## Audio only
|
## Audio only
|
||||||
|
|
||||||
To play audio only, disable the video:
|
To play audio only, disable video and control:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --no-video
|
scrcpy --no-video --no-control
|
||||||
|
```
|
||||||
|
|
||||||
|
To play audio without a window:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# --no-video and --no-control are implied by --no-window
|
||||||
|
scrcpy --no-window
|
||||||
# interrupt with Ctrl+C
|
# interrupt with Ctrl+C
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -15,6 +15,16 @@ scrcpy -n # short version
|
|||||||
Read [keyboard](keyboard.md) and [mouse](mouse.md).
|
Read [keyboard](keyboard.md) and [mouse](mouse.md).
|
||||||
|
|
||||||
|
|
||||||
|
## Control only
|
||||||
|
|
||||||
|
To control only with UHID mouse and keyboard:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-video --no-audio --keyboard=uhid --mouse=uhid
|
||||||
|
scrcpy --no-video --no-audio -KM # short version
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Copy-paste
|
## Copy-paste
|
||||||
|
|
||||||
Any time the Android clipboard changes, it is automatically synchronized to the
|
Any time the Android clipboard changes, it is automatically synchronized to the
|
||||||
|
43
doc/otg.md
43
doc/otg.md
@ -1,19 +1,21 @@
|
|||||||
# OTG
|
# OTG
|
||||||
|
|
||||||
By default, _scrcpy_ injects input events at the Android API level. As an
|
By default, _scrcpy_ injects input events at the Android API level. As an
|
||||||
alternative, when connected over USB, it is possible to send HID events, so that
|
alternative, it is possible to send HID events, so that scrcpy behaves as if it
|
||||||
scrcpy behaves as if it was a physical keyboard and/or mouse connected to the
|
was a [physical keyboard] and/or a [physical mouse] connected to the Android
|
||||||
Android device.
|
device (see [keyboard](keyboard.md) and [mouse](mouse.md)).
|
||||||
|
|
||||||
A special mode allows to control the device without mirroring, using AOA
|
[physical keyboard]: keyboard.md#physical-keyboard-simulation
|
||||||
[keyboard](keyboard.md#aoa) and [mouse](mouse.md#aoa). Therefore, it is possible
|
[physical mouse]: physical-keyboard-simulation
|
||||||
to run _scrcpy_ with only physical keyboard and mouse simulation (HID), as if
|
|
||||||
the computer keyboard and mouse were plugged directly to the device via an OTG
|
|
||||||
cable.
|
|
||||||
|
|
||||||
In this mode, `adb` (USB debugging) is not necessary, and mirroring is disabled.
|
A special mode (OTG) allows to control the device using AOA
|
||||||
|
[keyboard](keyboard.md#aoa) and [mouse](mouse.md#aoa), without using _adb_ at
|
||||||
|
all (so USB debugging is not necessary). In this mode, video and audio are
|
||||||
|
disabled, and `--keyboard=aoa and `--mouse=aoa` are implicitly set.
|
||||||
|
|
||||||
This is similar to `--keyboard=aoa --mouse=aoa`, but without mirroring.
|
Therefore, it is possible to run _scrcpy_ with only physical keyboard and mouse
|
||||||
|
simulation, as if the computer keyboard and mouse were plugged directly to the
|
||||||
|
device via an OTG cable.
|
||||||
|
|
||||||
To enable OTG mode:
|
To enable OTG mode:
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ scrcpy --otg
|
|||||||
scrcpy --otg -s 0123456789abcdef
|
scrcpy --otg -s 0123456789abcdef
|
||||||
```
|
```
|
||||||
|
|
||||||
It is possible to disable HID keyboard or HID mouse:
|
It is possible to disable keyboard or mouse:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --otg --keyboard=disabled
|
scrcpy --otg --keyboard=disabled
|
||||||
@ -35,3 +37,22 @@ It only works if the device is connected over USB.
|
|||||||
## OTG issues on Windows
|
## OTG issues on Windows
|
||||||
|
|
||||||
See [FAQ](/FAQ.md#otg-issues-on-windows).
|
See [FAQ](/FAQ.md#otg-issues-on-windows).
|
||||||
|
|
||||||
|
|
||||||
|
## Control-only
|
||||||
|
|
||||||
|
Note that the purpose of OTG is to control the device without USB debugging
|
||||||
|
(adb).
|
||||||
|
|
||||||
|
If you want to only control the device without mirroring while USB debugging is
|
||||||
|
enabled, then OTG mode is not necessary.
|
||||||
|
|
||||||
|
Instead, disable video and audio, and select UHID (or AOA):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-video --no-audio --keyboard=uhid --mouse=uhid
|
||||||
|
scrcpy --no-video --no-audio -KM # short version
|
||||||
|
scrcpy --no-video --no-audio --keyboard=aoa --mouse=aoa
|
||||||
|
```
|
||||||
|
|
||||||
|
One benefit of UHID is that it also works wirelessly.
|
||||||
|
@ -58,12 +58,10 @@ orientation](video.md#orientation).
|
|||||||
|
|
||||||
## No playback
|
## No playback
|
||||||
|
|
||||||
To disable playback while recording:
|
To disable playback and control while recording:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --no-playback --record=file.mp4
|
scrcpy --no-playback --no-control --record=file.mp4
|
||||||
scrcpy -Nr file.mkv
|
|
||||||
# interrupt recording with Ctrl+C
|
|
||||||
```
|
```
|
||||||
|
|
||||||
It is also possible to disable video and audio playback separately:
|
It is also possible to disable video and audio playback separately:
|
||||||
@ -73,6 +71,13 @@ It is also possible to disable video and audio playback separately:
|
|||||||
scrcpy --record=file.mkv --no-audio-playback
|
scrcpy --record=file.mkv --no-audio-playback
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To also disable the window:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-playback --no-window --record=file.mp4
|
||||||
|
# interrupt recording with Ctrl+C
|
||||||
|
```
|
||||||
|
|
||||||
## Time limit
|
## Time limit
|
||||||
|
|
||||||
To limit the recording time:
|
To limit the recording time:
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
# Window
|
# Window
|
||||||
|
|
||||||
|
## Disable window
|
||||||
|
|
||||||
|
To disable window (may be useful for recording or for playing audio only):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-window --record=file.mp4
|
||||||
|
# Ctrl+C to interrupt
|
||||||
|
```
|
||||||
|
|
||||||
## Title
|
## Title
|
||||||
|
|
||||||
By default, the window title is the device model. It can be changed:
|
By default, the window title is the device model. It can be changed:
|
||||||
|
Reference in New Issue
Block a user