Compare commits

..

2 Commits

Author SHA1 Message Date
fdc58722b3 Adapt to display API changes
The method SurfaceControl.createDisplay() has been removed in AOSP.

Use DisplayManager to create a VirtualDisplay object instead.

Fixes #4646 <https://github.com/Genymobile/scrcpy/issues/4646>
Fixes #4656 <https://github.com/Genymobile/scrcpy/issues/4656>
PR #4657 <https://github.com/Genymobile/scrcpy/pull/4657>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-02-09 18:31:20 +01:00
5a6b8310ca Add note about official website 2023-12-13 12:55:14 +01:00
22 changed files with 134 additions and 300 deletions

View File

@ -1,3 +1,7 @@
**This GitHub repo (<https://github.com/Genymobile/scrcpy>) is the only official
source for the project. Do not download releases from random websites, even if
their name contains `scrcpy`.**
# scrcpy (v2.3.1)
<img src="app/data/icon.svg" width="128" height="128" alt="scrcpy" align="right" />

View File

@ -27,8 +27,8 @@ _scrcpy() {
--force-adb-forward
--forward-all-clicks
-h --help
--keyboard-input-mode=
--kill-adb-on-close
-K --hid-keyboard
--legacy-paste
--list-camera-sizes
--list-cameras
@ -115,16 +115,13 @@ _scrcpy() {
COMPREPLY=($(compgen -W 'front back external' -- "$cur"))
return
;;
--keyboard-input-mode)
COMPREPLY=($(compgen -W 'inject aoa' -- "$cur"))
return
;;
--orientation|--display-orientation)
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
--orientation
--display-orientation)
COMPREPLY=($(compgen -> '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
return
;;
--record-orientation)
COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur"))
COMPREPLY=($(compgen -> '0 90 180 270' -- "$cur"))
return
;;
--lock-video-orientation)

View File

@ -34,8 +34,8 @@ arguments=(
'--force-adb-forward[Do not attempt to use \"adb reverse\" to connect to the device]'
'--forward-all-clicks[Forward clicks to device]'
{-h,--help}'[Print the help]'
'--keyboard-input-mode=[Set the keyboard input mode]:mode:(inject aoa)'
'--kill-adb-on-close[Kill adb when scrcpy terminates]'
{-K,--hid-keyboard}'[Simulate a physical keyboard by using HID over AOAv2]'
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
'--list-camera-sizes[List the valid camera capture sizes]'
'--list-cameras[List cameras available on the device]'

View File

@ -124,7 +124,7 @@ Use USB device (if there is exactly one, like adb -d).
Also see \fB\-e\fR (\fB\-\-select\-tcpip\fR).
.TP
.BI "\-\-disable\-screensaver"
.BI "\-\-disable-screensaver"
Disable screensaver while scrcpy is running.
.TP
@ -642,11 +642,7 @@ Enable/disable FPS counter (print frames/second in logs)
.TP
.B Ctrl+click-and-move
Pinch-to-zoom and rotate from the center of the screen
.TP
.B Shift+click-and-move
Tilt (slide vertically with two fingers)
Pinch-to-zoom from the center of the screen
.TP
.B Drag & drop APK file

View File

@ -4,16 +4,16 @@
#include "common.h"
#include <stdbool.h>
#include "trait/frame_sink.h"
#include <util/audiobuf.h>
#include <util/average.h>
#include <util/thread.h>
#include <util/tick.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <SDL2/SDL.h>
#include "trait/frame_sink.h"
#include "util/audiobuf.h"
#include "util/average.h"
#include "util/thread.h"
#include "util/tick.h"
struct sc_audio_player {
struct sc_frame_sink frame_sink;

View File

@ -93,8 +93,6 @@ enum {
OPT_DISPLAY_ORIENTATION,
OPT_RECORD_ORIENTATION,
OPT_ORIENTATION,
OPT_KEYBOARD_INPUT_MODE,
OPT_MOUSE_INPUT_MODE,
};
struct sc_option {
@ -361,19 +359,14 @@ static const struct sc_option options[] = {
.text = "Print this help.",
},
{
.longopt_id = OPT_KEYBOARD_INPUT_MODE,
.longopt = "keyboard-input-mode",
.argdesc = "value",
.text = "Select how to send keyboard inputs to the device.\n"
"Possible values are \"disable\", \"inject\" and \"aoa\".\n"
"\n"
"\"disable\" does not send keyboard inputs to the device.\n"
"\n"
"\"inject\" uses the Android system API to deliver keyboard\n"
"events to applications.\n"
"\n"
"\"aoa\" simulates a physical keyboard using the AOAv2\n"
"protocol. It may only work over USB.\n"
.longopt_id = OPT_KILL_ADB_ON_CLOSE,
.longopt = "kill-adb-on-close",
.text = "Kill adb when scrcpy terminates.",
},
{
.shortopt = 'K',
.longopt = "hid-keyboard",
.text = "Simulate a physical keyboard by using HID over AOAv2.\n"
"It provides a better experience for IME users, and allows to "
"generate non-ASCII characters, contrary to the default "
"injection method.\n"
@ -385,19 +378,7 @@ static const struct sc_option options[] = {
"android.settings.HARD_KEYBOARD_SETTINGS`.\n"
"However, the option is only available when the HID keyboard "
"is enabled (or a physical keyboard is connected).\n"
"Also see --mouse-input-mode and --otg."
"\n"
"Default is \"inject\" (or \"aoa\" if --otg is set).",
},
{
.longopt_id = OPT_KILL_ADB_ON_CLOSE,
.longopt = "kill-adb-on-close",
.text = "Kill adb when scrcpy terminates.",
},
{
// deprecated
.shortopt = 'K',
.longopt = "hid-keyboard",
"Also see --hid-mouse.",
},
{
.longopt_id = OPT_LEGACY_PASTE,
@ -459,7 +440,7 @@ static const struct sc_option options[] = {
"LAlt, LSuper or RSuper toggle the capture mode, to give "
"control of the mouse back to the computer.\n"
"It may only work over USB.\n"
"Also see --keyboard-input-mode and --otg.",
"Also see --hid-keyboard.",
},
{
.longopt_id = OPT_MAX_FPS,
@ -468,26 +449,6 @@ static const struct sc_option options[] = {
.text = "Limit the frame rate of screen capture (officially supported "
"since Android 10, but may work on earlier versions).",
},
{
.longopt_id = OPT_MOUSE_INPUT_MODE,
.longopt = "mouse-input-mode",
.argdesc = "value",
.text = "Select how to send mouse inputs to the device.\n"
"Possible values are \"disable\", \"inject\" and \"aoa\".\n"
"\n"
"\"disable\" does not send mouse inputs to the device.\n"
"\n"
"\"inject\" uses the Android system API to deliver mouse\n"
"events to applications.\n"
"\n"
"\"aoa\" simulates a physical mouse using the AOAv2\n"
"protocol. It may only work over USB.\n"
"In this mode, the computer mouse is captured to control the "
"device directly (relative mouse mode).\n"
"LAlt, LSuper or RSuper toggle the capture mode, to give "
"control of the mouse back to the computer.\n"
"Also see --keyboard-input-mode and --otg.",
},
{
.shortopt = 'n',
.longopt = "no-control",
@ -582,11 +543,10 @@ static const struct sc_option options[] = {
"mirroring is disabled.\n"
"LAlt, LSuper or RSuper toggle the mouse capture mode, to give "
"control of the mouse back to the computer.\n"
"Keyboard and mouse may be disabled separately using\n"
"--keyboard-input-mode=disable and\n"
"--mouse-input-mode=disable.\n"
"If any of --hid-keyboard or --hid-mouse is set, only enable "
"keyboard or mouse respectively, otherwise enable both.\n"
"It may only work over USB.\n"
"See --keyboard-input-mode and --mouse-input-mode.",
"See --hid-keyboard and --hid-mouse.",
},
{
.shortopt = 'p',
@ -987,11 +947,7 @@ static const struct sc_shortcut shortcuts[] = {
},
{
.shortcuts = { "Ctrl+click-and-move" },
.text = "Pinch-to-zoom and rotate from the center of the screen",
},
{
.shortcuts = { "Shift+click-and-move" },
.text = "Tilt (slide vertically with two fingers)",
.text = "Pinch-to-zoom from the center of the screen",
},
{
.shortcuts = { "Drag & drop APK file" },
@ -1942,61 +1898,6 @@ parse_camera_fps(const char *s, uint16_t *camera_fps) {
return true;
}
static bool
parse_keyboard_input_mode(const char *optarg,
enum sc_keyboard_input_mode *mode) {
if (!strcmp(optarg, "disable")) {
*mode = SC_KEYBOARD_INPUT_MODE_DISABLED;
return true;
}
if (!strcmp(optarg, "inject")) {
*mode = SC_KEYBOARD_INPUT_MODE_INJECT;
return true;
}
if (!strcmp(optarg, "aoa")) {
#ifdef HAVE_USB
*mode = SC_KEYBOARD_INPUT_MODE_AOA;
return true;
#else
LOGE("--keyboard-input-mode=aoa is disabled.");
return false;
#endif
}
LOGE("Unsupported keyboard input mode: %s (expected disable, inject, aoa)",
optarg);
return false;
}
static bool
parse_mouse_input_mode(const char *optarg, enum sc_mouse_input_mode *mode) {
if (!strcmp(optarg, "disable")) {
*mode = SC_MOUSE_INPUT_MODE_DISABLED;
return true;
}
if (!strcmp(optarg, "inject")) {
*mode = SC_MOUSE_INPUT_MODE_INJECT;
return true;
}
if (!strcmp(optarg, "aoa")) {
#ifdef HAVE_USB
*mode = SC_MOUSE_INPUT_MODE_AOA;
return true;
#else
LOGE("--mouse-input-mode=aoa is disabled.");
return false;
#endif
}
LOGE("Unsupported mouse input mode: %s (expected disable, inject, aoa)",
optarg);
return false;
}
static bool
parse_time_limit(const char *s, sc_tick *tick) {
long value;
@ -2086,20 +1987,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
break;
case 'K':
#ifdef HAVE_USB
LOGW("-K/--hid-keyboard is deprecated, use "
"--keyboard-input-mode=aoa instead.");
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AOA;
opts->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_HID;
break;
#else
LOGE("HID over AOA (-K/--hid-keyboard) is disabled.");
return false;
#endif
case OPT_KEYBOARD_INPUT_MODE:
if (!parse_keyboard_input_mode(optarg,
&opts->keyboard_input_mode)) {
return false;
}
break;
case OPT_MAX_FPS:
if (!parse_max_fps(optarg, &opts->max_fps)) {
return false;
@ -2112,19 +2005,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
break;
case 'M':
#ifdef HAVE_USB
LOGW("-M/--hid-mouse is deprecated, use --mouse-input-mode=aoa "
"instead.");
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_HID;
break;
#else
LOGE("HID over AOA (-M/--hid-mouse) is disabled.");
return false;
#endif
case OPT_MOUSE_INPUT_MODE:
if (!parse_mouse_input_mode(optarg, &opts->mouse_input_mode)) {
return false;
}
break;
case OPT_LOCK_VIDEO_ORIENTATION:
if (!parse_lock_video_orientation(optarg,
&opts->lock_video_orientation)) {
@ -2508,8 +2394,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
if (!opts->video) {
opts->video_playback = false;
// Do not power on the device on start if video capture is disabled
opts->power_on = false;
}
if (!opts->audio) {
@ -2571,15 +2455,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
#endif
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
: SC_KEYBOARD_INPUT_MODE_INJECT;
}
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA
: SC_MOUSE_INPUT_MODE_INJECT;
}
if ((opts->tunnel_host || opts->tunnel_port) && !opts->force_adb_forward) {
LOGI("Tunnel host/port is set, "
"--force-adb-forward automatically enabled.");
@ -2740,12 +2615,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
# ifdef _WIN32
if (!otg && (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AOA
|| opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA)) {
if (!otg && (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID
|| opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID)) {
LOGE("On Windows, it is not possible to open a USB device already open "
"by another process (like adb).");
LOGE("Therefore, --keyboard-input-mode=aoa and --mouse-input-mode=aoa "
"may only work in OTG mode (--otg).");
LOGE("Therefore, -K/--hid-keyboard and -M/--hid-mouse may only work in "
"OTG mode (--otg).");
return false;
}
# endif

View File

@ -76,8 +76,6 @@ sc_input_manager_init(struct sc_input_manager *im,
im->sdl_shortcut_mods.count = shortcut_mods->count;
im->vfinger_down = false;
im->vfinger_invert_x = false;
im->vfinger_invert_y = false;
im->last_keycode = SDLK_UNKNOWN;
im->last_mod = 0;
@ -349,14 +347,9 @@ simulate_virtual_finger(struct sc_input_manager *im,
}
static struct sc_point
inverse_point(struct sc_point point, struct sc_size size,
bool invert_x, bool invert_y) {
if (invert_x) {
inverse_point(struct sc_point point, struct sc_size size) {
point.x = size.width - point.x;
}
if (invert_y) {
point.y = size.height - point.y;
}
return point;
}
@ -612,9 +605,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
struct sc_point mouse =
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
event->y);
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size,
im->vfinger_invert_x,
im->vfinger_invert_y);
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
}
}
@ -735,7 +726,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
return;
}
// Pinch-to-zoom, rotate and tilt simulation.
// Pinch-to-zoom simulation.
//
// If Ctrl is hold when the left-click button is pressed, then
// pinch-to-zoom mode is enabled: on every mouse event until the left-click
@ -744,29 +735,14 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
//
// In other words, the center of the rotation/scaling is the center of the
// screen.
//
// To simulate a tilt gesture (a vertical slide with two fingers), Shift
// can be used instead of Ctrl. The "virtual finger" has a position
// inverted with respect to the vertical axis of symmetry in the middle of
// the screen.
const SDL_Keymod keymod = SDL_GetModState();
const bool ctrl_pressed = keymod & KMOD_CTRL;
const bool shift_pressed = keymod & KMOD_SHIFT;
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
if (event->button == SDL_BUTTON_LEFT &&
((down && !im->vfinger_down &&
((ctrl_pressed && !shift_pressed) ||
(!ctrl_pressed && shift_pressed))) ||
((down && !im->vfinger_down && CTRL_PRESSED) ||
(!down && im->vfinger_down))) {
struct sc_point mouse =
sc_screen_convert_window_to_frame_coords(im->screen, event->x,
event->y);
if (down) {
im->vfinger_invert_x = ctrl_pressed || shift_pressed;
im->vfinger_invert_y = ctrl_pressed;
}
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size,
im->vfinger_invert_x,
im->vfinger_invert_y);
struct sc_point vfinger = inverse_point(mouse, im->screen->frame_size);
enum android_motionevent_action action = down
? AMOTION_EVENT_ACTION_DOWN
: AMOTION_EVENT_ACTION_UP;

View File

@ -32,8 +32,6 @@ struct sc_input_manager {
} sdl_shortcut_mods;
bool vfinger_down;
bool vfinger_invert_x;
bool vfinger_invert_y;
// Tracks the number of identical consecutive shortcut key down events.
// Not to be confused with event->repeat, which counts the number of

View File

@ -21,8 +21,8 @@ const struct scrcpy_options scrcpy_options_default = {
.video_source = SC_VIDEO_SOURCE_DISPLAY,
.audio_source = SC_AUDIO_SOURCE_AUTO,
.record_format = SC_RECORD_FORMAT_AUTO,
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_AUTO,
.mouse_input_mode = SC_MOUSE_INPUT_MODE_AUTO,
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT,
.mouse_input_mode = SC_MOUSE_INPUT_MODE_INJECT,
.camera_facing = SC_CAMERA_FACING_ANY,
.port_range = {
.first = DEFAULT_LOCAL_PORT_RANGE_FIRST,

View File

@ -140,17 +140,13 @@ enum sc_lock_video_orientation {
};
enum sc_keyboard_input_mode {
SC_KEYBOARD_INPUT_MODE_AUTO,
SC_KEYBOARD_INPUT_MODE_DISABLED,
SC_KEYBOARD_INPUT_MODE_INJECT,
SC_KEYBOARD_INPUT_MODE_AOA,
SC_KEYBOARD_INPUT_MODE_HID,
};
enum sc_mouse_input_mode {
SC_MOUSE_INPUT_MODE_AUTO,
SC_MOUSE_INPUT_MODE_DISABLED,
SC_MOUSE_INPUT_MODE_INJECT,
SC_MOUSE_INPUT_MODE_AOA,
SC_MOUSE_INPUT_MODE_HID,
};
enum sc_key_inject_mode {

View File

@ -543,11 +543,11 @@ scrcpy(struct scrcpy_options *options) {
if (options->control) {
#ifdef HAVE_USB
bool use_aoa_keyboard =
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AOA;
bool use_aoa_mouse =
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA;
if (use_aoa_keyboard || use_aoa_mouse) {
bool use_hid_keyboard =
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID;
bool use_hid_mouse =
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID;
if (use_hid_keyboard || use_hid_mouse) {
bool ok = sc_acksync_init(&s->acksync);
if (!ok) {
goto end;
@ -590,7 +590,7 @@ scrcpy(struct scrcpy_options *options) {
goto aoa_hid_end;
}
if (use_aoa_keyboard) {
if (use_hid_keyboard) {
if (sc_hid_keyboard_init(&s->keyboard_hid, &s->aoa)) {
hid_keyboard_initialized = true;
kp = &s->keyboard_hid.key_processor;
@ -599,7 +599,7 @@ scrcpy(struct scrcpy_options *options) {
}
}
if (use_aoa_mouse) {
if (use_hid_mouse) {
if (sc_hid_mouse_init(&s->mouse_hid, &s->aoa)) {
hid_mouse_initialized = true;
mp = &s->mouse_hid.mouse_processor;
@ -636,19 +636,19 @@ aoa_hid_end:
if (use_hid_keyboard && !hid_keyboard_initialized) {
LOGE("Fallback to default keyboard injection method "
"(--keyboard-input-mode=aoa ignored)");
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
}
if (use_hid_mouse && !hid_mouse_initialized) {
LOGE("Fallback to default mouse injection method "
"(--mouse-input-mode=aoa ignored)");
"(-M/--hid-mouse ignored)");
options->mouse_input_mode = SC_MOUSE_INPUT_MODE_INJECT;
}
}
#else
assert(options->keyboard_input_mode != SC_KEYBOARD_INPUT_MODE_AOA);
assert(options->mouse_input_mode != SC_MOUSE_INPUT_MODE_AOA);
assert(options->keyboard_input_mode != SC_KEYBOARD_INPUT_MODE_HID);
assert(options->mouse_input_mode != SC_MOUSE_INPUT_MODE_HID);
#endif
// keyboard_input_mode may have been reset if HID mode failed

View File

@ -53,25 +53,6 @@ scrcpy_otg(struct scrcpy_options *options) {
static struct scrcpy_otg scrcpy_otg;
struct scrcpy_otg *s = &scrcpy_otg;
bool enable_keyboard;
assert(options->keyboard_input_mode != SC_KEYBOARD_INPUT_MODE_AUTO);
switch (options->keyboard_input_mode) {
case SC_KEYBOARD_INPUT_MODE_AOA:
enable_keyboard = true;
break;
case SC_KEYBOARD_INPUT_MODE_DISABLED:
enable_keyboard = false;
break;
default:
LOGE("In --otg mode, --keyboard-input-mode must be either aoa or "
"disable");
goto end;
}
if (options->keyboard_input_mode != SC_KEYBOARD_INPUT_MODE_AOA
&& options->keyboard_input_mode != SC_KEYBOARD_INPUT_MODE_DISABLED) {
return SCRCPY_EXIT_FAILURE;
}
const char *serial = options->serial;
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
@ -81,7 +62,7 @@ scrcpy_otg(struct scrcpy_options *options) {
// Minimal SDL initialization
if (SDL_Init(SDL_INIT_EVENTS)) {
LOGE("Could not initialize SDL: %s", SDL_GetError());
return SCRCPY_EXIT_FAILURE;
return false;
}
atexit(SDL_Quit);
@ -137,9 +118,9 @@ scrcpy_otg(struct scrcpy_options *options) {
aoa_initialized = true;
bool enable_keyboard =
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AOA;
options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_HID;
bool enable_mouse =
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA;
options->mouse_input_mode == SC_MOUSE_INPUT_MODE_HID;
// If neither --hid-keyboard or --hid-mouse is passed, enable both
if (!enable_keyboard && !enable_mouse) {

View File

@ -85,7 +85,7 @@ way as <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>).
To disable automatic clipboard synchronization, use
`--no-clipboard-autosync`.
## Pinch-to-zoom, rotate and tilt simulation
## Pinch-to-zoom
To simulate "pinch-to-zoom": <kbd>Ctrl</kbd>+_click-and-move_.
@ -93,12 +93,8 @@ More precisely, hold down <kbd>Ctrl</kbd> while pressing the left-click button.
Until the left-click button is released, all mouse movements scale and rotate
the content (if supported by the app) relative to the center of the screen.
To simulate a tilt gesture: <kbd>Shift</kbd>+_click-and-move-up-or-down_.
Technically, _scrcpy_ generates additional touch events from a "virtual finger"
at a location inverted through the center of the screen. When pressing
<kbd>Ctrl</kbd> the x and y coordinates are inverted. Using <kbd>Shift</kbd>
only inverts x.
at a location inverted through the center of the screen.
## Key repeat

View File

@ -49,8 +49,7 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
| Synchronize clipboards and paste⁵ | <kbd>MOD</kbd>+<kbd>v</kbd>
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
| Pinch-to-zoom/rotate | <kbd>Ctrl</kbd>+_click-and-move_
| Tilt (slide vertically with 2 fingers) | <kbd>Shift</kbd>+_click-and-move_
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_
| Drag & drop APK file | Install APK from computer
| Drag & drop non-APK file | [Push file to device](control.md#push-file-to-device)

View File

@ -153,14 +153,13 @@ public final class AudioCapture {
previousRecorderTimestamp = timestamp.nanoTime;
} else {
if (nextPts == 0) {
Ln.w("Could not get initial audio timestamp");
nextPts = System.nanoTime() / 1000;
Ln.w("Could not get any audio timestamp");
}
// compute from previous timestamp and packet size
pts = nextPts;
}
long durationUs = r * 1000000L / (CHANNELS * BYTES_PER_SAMPLE * SAMPLE_RATE);
long durationUs = r * 1000000 / (CHANNELS * BYTES_PER_SAMPLE * SAMPLE_RATE);
nextPts = pts + durationUs;
if (previousPts != 0 && pts < previousPts + ONE_SAMPLE_US) {

View File

@ -187,7 +187,5 @@ public final class CleanUp {
Device.setScreenPowerMode(Device.POWER_MODE_NORMAL);
}
}
System.exit(0);
}
}

View File

@ -45,11 +45,11 @@ public final class Device {
void onClipboardTextChanged(String text);
}
private final Size deviceSize;
private final Rect crop;
private int maxSize;
private final int lockVideoOrientation;
private Size deviceSize;
private ScreenInfo screenInfo;
private RotationListener rotationListener;
private FoldListener foldListener;
@ -116,8 +116,8 @@ public final class Device {
return;
}
deviceSize = displayInfo.getSize();
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), deviceSize, crop, maxSize, lockVideoOrientation);
screenInfo = ScreenInfo.computeScreenInfo(displayInfo.getRotation(), displayInfo.getSize(), options.getCrop(),
options.getMaxSize(), options.getLockVideoOrientation());
// notify
if (foldListener != null) {
foldListener.onFoldChanged(displayId, folded);
@ -164,6 +164,10 @@ public final class Device {
}
}
public int getDisplayId() {
return displayId;
}
public synchronized void setMaxSize(int newMaxSize) {
maxSize = newMaxSize;
screenInfo = ScreenInfo.computeScreenInfo(screenInfo.getReverseVideoRotation(), deviceSize, crop, newMaxSize, lockVideoOrientation);

View File

@ -1,8 +1,10 @@
package com.genymobile.scrcpy;
import com.genymobile.scrcpy.wrappers.ServiceManager;
import com.genymobile.scrcpy.wrappers.SurfaceControl;
import android.graphics.Rect;
import android.hardware.display.VirtualDisplay;
import android.os.Build;
import android.os.IBinder;
import android.view.Surface;
@ -11,6 +13,7 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
private final Device device;
private IBinder display;
private VirtualDisplay virtualDisplay;
public ScreenCapture(Device device) {
this.device = device;
@ -34,9 +37,29 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
if (display != null) {
SurfaceControl.destroyDisplay(display);
display = null;
}
if (virtualDisplay != null) {
virtualDisplay.release();
virtualDisplay = null;
}
try {
display = createDisplay();
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
Ln.d("Display: using SurfaceControl API");
} catch (Exception surfaceControlException) {
Rect videoRect = screenInfo.getVideoSize().toRect();
try {
virtualDisplay = ServiceManager.getDisplayManager()
.createVirtualDisplay("scrcpy", videoRect.width(), videoRect.height(), device.getDisplayId(), surface);
Ln.d("Display: using DisplayManager API");
} catch (Exception displayManagerException) {
Ln.e("Could not create display using SurfaceControl", surfaceControlException);
Ln.e("Could not create display using DisplayManager", displayManagerException);
throw new AssertionError("Could not create display");
}
}
}
@Override
@ -69,7 +92,7 @@ public class ScreenCapture extends SurfaceCapture implements Device.RotationList
requestReset();
}
private static IBinder createDisplay() {
private static IBinder createDisplay() throws Exception {
// Since Android 12 (preview), secure displays could not be created with shell permissions anymore.
// On Android 12 preview, SDK_INT is still R (not S), but CODENAME is "S".
boolean secure = Build.VERSION.SDK_INT < Build.VERSION_CODES.R || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && !"S".equals(

View File

@ -285,28 +285,16 @@ public final class Workarounds {
Method getParcelMethod = attributionSourceState.getClass().getDeclaredMethod("getParcel");
Parcel attributionSourceParcel = (Parcel) getParcelMethod.invoke(attributionSourceState);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// private native int native_setup(Object audiorecordThis,
// Object /*AudioAttributes*/ attributes,
// int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
// int buffSizeInBytes, int[] sessionId, @NonNull Parcel attributionSource,
// long nativeRecordInJavaObj, int maxSharedAudioHistoryMs);
Method nativeSetupMethod = AudioRecord.class.getDeclaredMethod("native_setup", Object.class, Object.class, int[].class,
int.class, int.class, int.class, int.class, int[].class, Parcel.class, long.class, int.class);
Method nativeSetupMethod = AudioRecord.class.getDeclaredMethod("native_setup", Object.class, Object.class, int[].class, int.class,
int.class, int.class, int.class, int[].class, Parcel.class, long.class, int.class);
nativeSetupMethod.setAccessible(true);
initResult = (int) nativeSetupMethod.invoke(audioRecord, new WeakReference<AudioRecord>(audioRecord), attributes,
sampleRateArray, channelMask, channelIndexMask, audioRecord.getAudioFormat(), bufferSizeInBytes, session,
attributionSourceParcel, 0L, 0);
} else {
// Android 14 added a new int parameter "halInputFlags"
// <https://github.com/aosp-mirror/platform_frameworks_base/commit/f6135d75db79b1d48fad3a3b3080d37be20a2313>
Method nativeSetupMethod = AudioRecord.class.getDeclaredMethod("native_setup", Object.class, Object.class, int[].class,
int.class, int.class, int.class, int.class, int[].class, Parcel.class, long.class, int.class, int.class);
nativeSetupMethod.setAccessible(true);
initResult = (int) nativeSetupMethod.invoke(audioRecord, new WeakReference<AudioRecord>(audioRecord), attributes,
sampleRateArray, channelMask, channelIndexMask, audioRecord.getAudioFormat(), bufferSizeInBytes, session,
attributionSourceParcel, 0L, 0, 0);
}
initResult = (int) nativeSetupMethod.invoke(audioRecord, new WeakReference<AudioRecord>(audioRecord), attributes, sampleRateArray,
channelMask, channelIndexMask, audioRecord.getAudioFormat(), bufferSizeInBytes, session, attributionSourceParcel, 0L, 0);
}
}

View File

@ -41,14 +41,8 @@ public final class ClipboardManager {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, String.class, int.class, int.class);
getMethodVersion = 2;
} catch (NoSuchMethodException e3) {
try {
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class, String.class);
getMethodVersion = 3;
} catch (NoSuchMethodException e4) {
getPrimaryClipMethod = manager.getClass()
.getMethod("getPrimaryClip", String.class, String.class, int.class, int.class, boolean.class);
getMethodVersion = 4;
}
}
}
}
@ -93,11 +87,8 @@ public final class ClipboardManager {
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID);
case 2:
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0);
case 3:
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID, null);
default:
// The last boolean parameter is "userOperate"
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, true);
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, FakeContext.ROOT_UID, null);
}
}

View File

@ -5,14 +5,18 @@ import com.genymobile.scrcpy.DisplayInfo;
import com.genymobile.scrcpy.Ln;
import com.genymobile.scrcpy.Size;
import android.hardware.display.VirtualDisplay;
import android.view.Display;
import android.view.Surface;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class DisplayManager {
private final Object manager; // instance of hidden class android.hardware.display.DisplayManagerGlobal
private Method createVirtualDisplayMethod;
public DisplayManager(Object manager) {
this.manager = manager;
@ -94,4 +98,17 @@ public final class DisplayManager {
throw new AssertionError(e);
}
}
private Method getCreateVirtualDisplayMethod() throws NoSuchMethodException {
if (createVirtualDisplayMethod == null) {
createVirtualDisplayMethod = android.hardware.display.DisplayManager.class
.getMethod("createVirtualDisplay", String.class, int.class, int.class, int.class, Surface.class);
}
return createVirtualDisplayMethod;
}
public VirtualDisplay createVirtualDisplay(String name, int width, int height, int displayIdToMirror, Surface surface) throws Exception {
Method method = getCreateVirtualDisplayMethod();
return (VirtualDisplay) method.invoke(null, name, width, height, displayIdToMirror, surface);
}
}

View File

@ -78,12 +78,8 @@ public final class SurfaceControl {
}
}
public static IBinder createDisplay(String name, boolean secure) {
try {
public static IBinder createDisplay(String name, boolean secure) throws Exception {
return (IBinder) CLASS.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure);
} catch (Exception e) {
throw new AssertionError(e);
}
}
private static Method getGetBuiltInDisplayMethod() throws NoSuchMethodException {