Compare commits

..

2 Commits

Author SHA1 Message Date
wuderek
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
Romain Vimont
5a6b8310ca Add note about official website 2023-12-13 12:55:14 +01:00
22 changed files with 269 additions and 457 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
--kill-adb-on-close
-K --hid-keyboard
--legacy-paste
--list-camera-sizes
--list-cameras
@@ -39,7 +39,6 @@ _scrcpy() {
-m --max-size=
-M --hid-mouse
--max-fps=
--mouse=
-n --no-control
-N --no-playback
--no-audio
@@ -116,20 +115,13 @@ _scrcpy() {
COMPREPLY=($(compgen -W 'front back external' -- "$cur"))
return
;;
--keyboard)
COMPREPLY=($(compgen -W 'disabled sdk aoa' -- "$cur"))
return
;;
--mouse)
COMPREPLY=($(compgen -W 'disabled sdk 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[Set the keyboard input mode]:mode:(disabled sdk 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]'
@@ -45,7 +45,6 @@ arguments=(
{-m,--max-size=}'[Limit both the width and height of the video to value]'
{-M,--hid-mouse}'[Simulate a physical mouse by using HID over AOAv2]'
'--max-fps=[Limit the frame rate of screen capture]'
'--mouse[Set the mouse input mode]:mode:(disabled sdk aoa)'
{-n,--no-control}'[Disable device control \(mirror the device in read only\)]'
{-N,--no-playback}'[Disable video and audio playback]'
'--no-audio[Disable audio forwarding]'

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
@@ -172,26 +172,24 @@ By default, right-click triggers BACK (or POWER on) and middle-click triggers HO
Print this help.
.TP
.BI "\-\-keyboard " mode
Select how to send keyboard inputs to the device.
.B \-\-kill\-adb\-on\-close
Kill adb when scrcpy terminates.
Possible values are "disabled", "sdk" and "aoa":
.TP
.B \-K, \-\-hid\-keyboard
Simulate a physical keyboard by using HID over AOAv2.
- "disabled" does not send keyboard inputs to the device.
- "sdk" uses the Android system API to deliver keyboard events to applications.
- "aoa" simulates a physical keyboard using the AOAv2 protocol. It may only work over USB.
This provides a better experience for IME users, and allows to generate non-ASCII characters, contrary to the default injection method.
For "aoa", the keyboard layout must be configured (once and for all) on the device, via Settings -> System -> Languages and input -> Physical keyboard. This settings page can be started directly:
It may only work over USB.
The keyboard layout must be configured (once and for all) on the device, via Settings -> System -> Languages and input -> Physical keyboard. This settings page can be started directly:
adb shell am start -a android.settings.HARD_KEYBOARD_SETTINGS
This option is only available when the HID keyboard is enabled (or a physical keyboard is connected).
However, the option is only available when the HID keyboard is enabled (or a physical keyboard is connected).
Also see \fB\-\-mouse\fR.
.TP
.B \-\-kill\-adb\-on\-close
Kill adb when scrcpy terminates.
Also see \fB\-\-hid\-mouse\fR.
.TP
.B \-\-legacy\-paste
@@ -232,25 +230,20 @@ Limit both the width and height of the video to \fIvalue\fR. The other dimension
Default is 0 (unlimited).
.TP
.BI "\-\-max\-fps " value
Limit the framerate of screen capture (officially supported since Android 10, but may work on earlier versions).
.B \-M, \-\-hid\-mouse
Simulate a physical mouse by using HID over AOAv2.
.TP
.BI "\-\-mouse " mode
Select how to send mouse inputs to the device.
Possible values are "disabled", "sdk" and "aoa":
- "disabled" does not send mouse inputs to the device.
- "sdk" uses the Android system API to deliver mouse events to applications.
- "aoa" simulates a physical mouse using the AOAv2 protocol. It may only work over USB.
In "aoa" mode, the computer mouse is captured to control the device directly (relative mouse mode).
In this mode, the computer mouse is captured to control the device directly (relative mouse mode).
LAlt, LSuper or RSuper toggle the capture mode, to give control of the mouse back to the computer.
Also see \fB\-\-keyboard\fR.
It may only work over USB.
Also see \fB\-\-hid\-keyboard\fR.
.TP
.BI "\-\-max\-fps " value
Limit the framerate of screen capture (officially supported since Android 10, but may work on earlier versions).
.TP
.B \-n, \-\-no\-control
@@ -649,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,
OPT_MOUSE,
};
struct sc_option {
@@ -360,35 +358,27 @@ static const struct sc_option options[] = {
.longopt = "help",
.text = "Print this help.",
},
{
.longopt_id = OPT_KEYBOARD,
.longopt = "keyboard",
.argdesc = "mode",
.text = "Select how to send keyboard inputs to the device.\n"
"Possible values are \"disabled\", \"sdk\" and \"aoa\".\n"
"\"disabled\" does not send keyboard inputs to the device.\n"
"\"sdk\" uses the Android system API to deliver keyboard\n"
"events to applications.\n"
"\"aoa\" simulates a physical keyboard using the AOAv2\n"
"protocol. It may only work over USB.\n"
"For \"aoa\", the keyboard layout must be configured (once and "
"for all) on the device, via Settings -> System -> Languages "
"and input -> Physical keyboard. This settings page can be "
"started directly: `adb shell am start -a "
"android.settings.HARD_KEYBOARD_SETTINGS`.\n"
"This option is only available when the HID keyboard is "
"enabled (or a physical keyboard is connected).\n"
"Also see --mouse.",
},
{
.longopt_id = OPT_KILL_ADB_ON_CLOSE,
.longopt = "kill-adb-on-close",
.text = "Kill adb when scrcpy terminates.",
},
{
// deprecated
.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"
"It may only work over USB.\n"
"The keyboard layout must be configured (once and for all) on "
"the device, via Settings -> System -> Languages and input -> "
"Physical keyboard. This settings page can be started "
"directly: `adb shell am start -a "
"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 --hid-mouse.",
},
{
.longopt_id = OPT_LEGACY_PASTE,
@@ -442,9 +432,15 @@ static const struct sc_option options[] = {
"Default is 0 (unlimited).",
},
{
// deprecated
.shortopt = 'M',
.longopt = "hid-mouse",
.text = "Simulate a physical mouse by using HID over AOAv2.\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"
"It may only work over USB.\n"
"Also see --hid-keyboard.",
},
{
.longopt_id = OPT_MAX_FPS,
@@ -453,23 +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,
.longopt = "mouse",
.argdesc = "mode",
.text = "Select how to send mouse inputs to the device.\n"
"Possible values are \"disabled\", \"sdk\" and \"aoa\".\n"
"\"disabled\" does not send mouse inputs to the device.\n"
"\"sdk\" uses the Android system API to deliver mouse\n"
"events to applications.\n"
"\"aoa\" simulates a physical mouse using the AOAv2 protocol\n"
"It may only work over USB.\n"
"In \"aoa\" 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.",
},
{
.shortopt = 'n',
.longopt = "no-control",
@@ -564,10 +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=disabled and --mouse=disabled.\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 and --mouse.",
"See --hid-keyboard and --hid-mouse.",
},
{
.shortopt = 'p',
@@ -968,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" },
@@ -1923,58 +1898,6 @@ parse_camera_fps(const char *s, uint16_t *camera_fps) {
return true;
}
static bool
parse_keyboard(const char *optarg, enum sc_keyboard_input_mode *mode) {
if (!strcmp(optarg, "disabled")) {
*mode = SC_KEYBOARD_INPUT_MODE_DISABLED;
return true;
}
if (!strcmp(optarg, "sdk")) {
*mode = SC_KEYBOARD_INPUT_MODE_SDK;
return true;
}
if (!strcmp(optarg, "aoa")) {
#ifdef HAVE_USB
*mode = SC_KEYBOARD_INPUT_MODE_AOA;
return true;
#else
LOGE("--keyboard=aoa is disabled.");
return false;
#endif
}
LOGE("Unsupported keyboard: %s (expected disabled, sdk or aoa)", optarg);
return false;
}
static bool
parse_mouse(const char *optarg, enum sc_mouse_input_mode *mode) {
if (!strcmp(optarg, "disabled")) {
*mode = SC_MOUSE_INPUT_MODE_DISABLED;
return true;
}
if (!strcmp(optarg, "sdk")) {
*mode = SC_MOUSE_INPUT_MODE_SDK;
return true;
}
if (!strcmp(optarg, "aoa")) {
#ifdef HAVE_USB
*mode = SC_MOUSE_INPUT_MODE_AOA;
return true;
#else
LOGE("--mouse=aoa is disabled.");
return false;
#endif
}
LOGE("Unsupported mouse: %s (expected disabled, sdk or aoa)", optarg);
return false;
}
static bool
parse_time_limit(const char *s, sc_tick *tick) {
long value;
@@ -2064,19 +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=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:
if (!parse_keyboard(optarg, &opts->keyboard_input_mode)) {
return false;
}
break;
case OPT_MAX_FPS:
if (!parse_max_fps(optarg, &opts->max_fps)) {
return false;
@@ -2089,18 +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=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:
if (!parse_mouse(optarg, &opts->mouse_input_mode)) {
return false;
}
break;
case OPT_LOCK_VIDEO_ORIENTATION:
if (!parse_lock_video_orientation(optarg,
&opts->lock_video_orientation)) {
@@ -2484,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) {
@@ -2547,31 +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_SDK;
}
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA
: SC_MOUSE_INPUT_MODE_SDK;
}
if (otg) {
enum sc_keyboard_input_mode kmode = opts->keyboard_input_mode;
if (kmode != SC_KEYBOARD_INPUT_MODE_AOA
&& kmode != SC_KEYBOARD_INPUT_MODE_DISABLED) {
LOGE("In OTG mode, --keyboard only supports aoa or disabled.");
return false;
}
enum sc_mouse_input_mode mmode = opts->mouse_input_mode;
if (mmode != SC_MOUSE_INPUT_MODE_AOA
&& mmode != SC_MOUSE_INPUT_MODE_DISABLED) {
LOGE("In OTG mode, --mouse only supports aoa or disabled.");
return false;
}
}
if ((opts->tunnel_host || opts->tunnel_port) && !opts->force_adb_forward) {
LOGI("Tunnel host/port is set, "
"--force-adb-forward automatically enabled.");
@@ -2732,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=aoa and --mouse=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

@@ -52,11 +52,8 @@ is_shortcut_mod(struct sc_input_manager *im, uint16_t sdl_mod) {
void
sc_input_manager_init(struct sc_input_manager *im,
const struct sc_input_manager_params *params) {
// A key/mouse processor may not be present if there is no controller
assert((!params->kp && !params->mp) || params->controller);
// A processor must have ops initialized
assert(!params->kp || params->kp->ops);
assert(!params->mp || params->mp->ops);
assert(!params->controller || (params->kp && params->kp->ops));
assert(!params->controller || (params->mp && params->mp->ops));
im->controller = params->controller;
im->fp = params->fp;
@@ -79,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;
@@ -90,10 +85,8 @@ sc_input_manager_init(struct sc_input_manager *im,
}
static void
send_keycode(struct sc_input_manager *im, enum android_keycode keycode,
send_keycode(struct sc_controller *controller, enum android_keycode keycode,
enum sc_action action, const char *name) {
assert(im->controller && im->kp);
// send DOWN event
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_INJECT_KEYCODE;
@@ -104,109 +97,100 @@ send_keycode(struct sc_input_manager *im, enum android_keycode keycode,
msg.inject_keycode.metastate = 0;
msg.inject_keycode.repeat = 0;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request 'inject %s'", name);
}
}
static inline void
action_home(struct sc_input_manager *im, enum sc_action action) {
send_keycode(im, AKEYCODE_HOME, action, "HOME");
action_home(struct sc_controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_HOME, action, "HOME");
}
static inline void
action_back(struct sc_input_manager *im, enum sc_action action) {
send_keycode(im, AKEYCODE_BACK, action, "BACK");
action_back(struct sc_controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_BACK, action, "BACK");
}
static inline void
action_app_switch(struct sc_input_manager *im, enum sc_action action) {
send_keycode(im, AKEYCODE_APP_SWITCH, action, "APP_SWITCH");
action_app_switch(struct sc_controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_APP_SWITCH, action, "APP_SWITCH");
}
static inline void
action_power(struct sc_input_manager *im, enum sc_action action) {
send_keycode(im, AKEYCODE_POWER, action, "POWER");
action_power(struct sc_controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_POWER, action, "POWER");
}
static inline void
action_volume_up(struct sc_input_manager *im, enum sc_action action) {
send_keycode(im, AKEYCODE_VOLUME_UP, action, "VOLUME_UP");
action_volume_up(struct sc_controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_VOLUME_UP, action, "VOLUME_UP");
}
static inline void
action_volume_down(struct sc_input_manager *im, enum sc_action action) {
send_keycode(im, AKEYCODE_VOLUME_DOWN, action, "VOLUME_DOWN");
action_volume_down(struct sc_controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_VOLUME_DOWN, action, "VOLUME_DOWN");
}
static inline void
action_menu(struct sc_input_manager *im, enum sc_action action) {
send_keycode(im, AKEYCODE_MENU, action, "MENU");
action_menu(struct sc_controller *controller, enum sc_action action) {
send_keycode(controller, AKEYCODE_MENU, action, "MENU");
}
// turn the screen on if it was off, press BACK otherwise
// If the screen is off, it is turned on only on ACTION_DOWN
static void
press_back_or_turn_screen_on(struct sc_input_manager *im,
press_back_or_turn_screen_on(struct sc_controller *controller,
enum sc_action action) {
assert(im->controller && im->kp);
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON;
msg.back_or_screen_on.action = action == SC_ACTION_DOWN
? AKEY_EVENT_ACTION_DOWN
: AKEY_EVENT_ACTION_UP;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request 'press back or turn screen on'");
}
}
static void
expand_notification_panel(struct sc_input_manager *im) {
assert(im->controller);
expand_notification_panel(struct sc_controller *controller) {
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request 'expand notification panel'");
}
}
static void
expand_settings_panel(struct sc_input_manager *im) {
assert(im->controller);
expand_settings_panel(struct sc_controller *controller) {
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request 'expand settings panel'");
}
}
static void
collapse_panels(struct sc_input_manager *im) {
assert(im->controller);
collapse_panels(struct sc_controller *controller) {
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request 'collapse notification panel'");
}
}
static bool
get_device_clipboard(struct sc_input_manager *im, enum sc_copy_key copy_key) {
assert(im->controller && im->kp);
get_device_clipboard(struct sc_controller *controller,
enum sc_copy_key copy_key) {
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_GET_CLIPBOARD;
msg.get_clipboard.copy_key = copy_key;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request 'get device clipboard'");
return false;
}
@@ -215,10 +199,8 @@ get_device_clipboard(struct sc_input_manager *im, enum sc_copy_key copy_key) {
}
static bool
set_device_clipboard(struct sc_input_manager *im, bool paste,
set_device_clipboard(struct sc_controller *controller, bool paste,
uint64_t sequence) {
assert(im->controller && im->kp);
char *text = SDL_GetClipboardText();
if (!text) {
LOGW("Could not get clipboard text: %s", SDL_GetError());
@@ -238,7 +220,7 @@ set_device_clipboard(struct sc_input_manager *im, bool paste,
msg.set_clipboard.text = text_dup;
msg.set_clipboard.paste = paste;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
free(text_dup);
LOGW("Could not request 'set device clipboard'");
return false;
@@ -248,23 +230,19 @@ set_device_clipboard(struct sc_input_manager *im, bool paste,
}
static void
set_screen_power_mode(struct sc_input_manager *im,
set_screen_power_mode(struct sc_controller *controller,
enum sc_screen_power_mode mode) {
assert(im->controller);
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE;
msg.set_screen_power_mode.mode = mode;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request 'set screen power mode'");
}
}
static void
switch_fps_counter_state(struct sc_input_manager *im) {
struct sc_fps_counter *fps_counter = &im->screen->fps_counter;
switch_fps_counter_state(struct sc_fps_counter *fps_counter) {
// the started state can only be written from the current thread, so there
// is no ToCToU issue
if (sc_fps_counter_is_started(fps_counter)) {
@@ -276,9 +254,7 @@ switch_fps_counter_state(struct sc_input_manager *im) {
}
static void
clipboard_paste(struct sc_input_manager *im) {
assert(im->controller && im->kp);
clipboard_paste(struct sc_controller *controller) {
char *text = SDL_GetClipboardText();
if (!text) {
LOGW("Could not get clipboard text: %s", SDL_GetError());
@@ -300,28 +276,25 @@ clipboard_paste(struct sc_input_manager *im) {
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_INJECT_TEXT;
msg.inject_text.text = text_dup;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
free(text_dup);
LOGW("Could not request 'paste clipboard'");
}
}
static void
rotate_device(struct sc_input_manager *im) {
assert(im->controller);
rotate_device(struct sc_controller *controller) {
struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_ROTATE_DEVICE;
if (!sc_controller_push_msg(im->controller, &msg)) {
if (!sc_controller_push_msg(controller, &msg)) {
LOGW("Could not request device rotation");
}
}
static void
apply_orientation_transform(struct sc_input_manager *im,
apply_orientation_transform(struct sc_screen *screen,
enum sc_orientation transform) {
struct sc_screen *screen = im->screen;
enum sc_orientation new_orientation =
sc_orientation_apply(screen->orientation, transform);
sc_screen_set_orientation(screen, new_orientation);
@@ -374,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) {
point.x = size.width - point.x;
}
if (invert_y) {
point.y = size.height - point.y;
}
inverse_point(struct sc_point point, struct sc_size size) {
point.x = size.width - point.x;
point.y = size.height - point.y;
return point;
}
@@ -389,7 +357,7 @@ static void
sc_input_manager_process_key(struct sc_input_manager *im,
const SDL_KeyboardEvent *event) {
// controller is NULL if --no-control is requested
bool control = im->controller;
struct sc_controller *controller = im->controller;
SDL_Keycode keycode = event->keysym.sym;
uint16_t mod = event->keysym.mod;
@@ -415,68 +383,68 @@ sc_input_manager_process_key(struct sc_input_manager *im,
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
switch (keycode) {
case SDLK_h:
if (im->kp && !shift && !repeat) {
action_home(im, action);
if (controller && !shift && !repeat) {
action_home(controller, action);
}
return;
case SDLK_b: // fall-through
case SDLK_BACKSPACE:
if (im->kp && !shift && !repeat) {
action_back(im, action);
if (controller && !shift && !repeat) {
action_back(controller, action);
}
return;
case SDLK_s:
if (im->kp && !shift && !repeat) {
action_app_switch(im, action);
if (controller && !shift && !repeat) {
action_app_switch(controller, action);
}
return;
case SDLK_m:
if (im->kp && !shift && !repeat) {
action_menu(im, action);
if (controller && !shift && !repeat) {
action_menu(controller, action);
}
return;
case SDLK_p:
if (im->kp && !shift && !repeat) {
action_power(im, action);
if (controller && !shift && !repeat) {
action_power(controller, action);
}
return;
case SDLK_o:
if (control && !repeat && down) {
if (controller && !repeat && down) {
enum sc_screen_power_mode mode = shift
? SC_SCREEN_POWER_MODE_NORMAL
: SC_SCREEN_POWER_MODE_OFF;
set_screen_power_mode(im, mode);
set_screen_power_mode(controller, mode);
}
return;
case SDLK_DOWN:
if (shift) {
if (!repeat & down) {
apply_orientation_transform(im,
apply_orientation_transform(im->screen,
SC_ORIENTATION_FLIP_180);
}
} else if (im->kp) {
} else if (controller) {
// forward repeated events
action_volume_down(im, action);
action_volume_down(controller, action);
}
return;
case SDLK_UP:
if (shift) {
if (!repeat & down) {
apply_orientation_transform(im,
apply_orientation_transform(im->screen,
SC_ORIENTATION_FLIP_180);
}
} else if (im->kp) {
} else if (controller) {
// forward repeated events
action_volume_up(im, action);
action_volume_up(controller, action);
}
return;
case SDLK_LEFT:
if (!repeat && down) {
if (shift) {
apply_orientation_transform(im,
apply_orientation_transform(im->screen,
SC_ORIENTATION_FLIP_0);
} else {
apply_orientation_transform(im,
apply_orientation_transform(im->screen,
SC_ORIENTATION_270);
}
}
@@ -484,33 +452,34 @@ sc_input_manager_process_key(struct sc_input_manager *im,
case SDLK_RIGHT:
if (!repeat && down) {
if (shift) {
apply_orientation_transform(im,
apply_orientation_transform(im->screen,
SC_ORIENTATION_FLIP_0);
} else {
apply_orientation_transform(im,
apply_orientation_transform(im->screen,
SC_ORIENTATION_90);
}
}
return;
case SDLK_c:
if (im->kp && !shift && !repeat && down) {
get_device_clipboard(im, SC_COPY_KEY_COPY);
if (controller && !shift && !repeat && down) {
get_device_clipboard(controller, SC_COPY_KEY_COPY);
}
return;
case SDLK_x:
if (im->kp && !shift && !repeat && down) {
get_device_clipboard(im, SC_COPY_KEY_CUT);
if (controller && !shift && !repeat && down) {
get_device_clipboard(controller, SC_COPY_KEY_CUT);
}
return;
case SDLK_v:
if (im->kp && !repeat && down) {
if (controller && !repeat && down) {
if (shift || im->legacy_paste) {
// inject the text as input events
clipboard_paste(im);
clipboard_paste(controller);
} else {
// store the text in the device clipboard and paste,
// without requesting an acknowledgment
set_device_clipboard(im, true, SC_SEQUENCE_INVALID);
set_device_clipboard(controller, true,
SC_SEQUENCE_INVALID);
}
}
return;
@@ -531,23 +500,23 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return;
case SDLK_i:
if (!shift && !repeat && down) {
switch_fps_counter_state(im);
switch_fps_counter_state(&im->screen->fps_counter);
}
return;
case SDLK_n:
if (control && !repeat && down) {
if (controller && !repeat && down) {
if (shift) {
collapse_panels(im);
collapse_panels(controller);
} else if (im->key_repeat == 0) {
expand_notification_panel(im);
expand_notification_panel(controller);
} else {
expand_settings_panel(im);
expand_settings_panel(controller);
}
}
return;
case SDLK_r:
if (control && !shift && !repeat && down) {
rotate_device(im);
if (controller && !shift && !repeat && down) {
rotate_device(controller);
}
return;
}
@@ -555,7 +524,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return;
}
if (!im->kp) {
if (!controller) {
return;
}
@@ -564,7 +533,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
if (im->clipboard_autosync && is_ctrl_v) {
if (im->legacy_paste) {
// inject the text as input events
clipboard_paste(im);
clipboard_paste(controller);
return;
}
@@ -574,7 +543,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
// Synchronize the computer clipboard to the device clipboard before
// sending Ctrl+v, to allow seamless copy-paste.
bool ok = set_device_clipboard(im, false, sequence);
bool ok = set_device_clipboard(controller, false, sequence);
if (!ok) {
LOGW("Clipboard could not be synchronized, Ctrl+v not injected");
return;
@@ -636,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);
}
}
@@ -676,7 +643,7 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
static void
sc_input_manager_process_mouse_button(struct sc_input_manager *im,
const SDL_MouseButtonEvent *event) {
bool control = im->controller;
struct sc_controller *controller = im->controller;
if (event->which == SDL_TOUCH_MOUSEID) {
// simulated from touch events, so it's a duplicate
@@ -685,27 +652,27 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (!im->forward_all_clicks) {
if (control) {
if (controller) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
if (im->kp && event->button == SDL_BUTTON_X1) {
action_app_switch(im, action);
if (event->button == SDL_BUTTON_X1) {
action_app_switch(controller, action);
return;
}
if (event->button == SDL_BUTTON_X2 && down) {
if (event->clicks < 2) {
expand_notification_panel(im);
expand_notification_panel(controller);
} else {
expand_settings_panel(im);
expand_settings_panel(controller);
}
return;
}
if (im->kp && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im, action);
if (event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(controller, action);
return;
}
if (im->kp && event->button == SDL_BUTTON_MIDDLE) {
action_home(im, action);
if (event->button == SDL_BUTTON_MIDDLE) {
action_home(controller, action);
return;
}
}
@@ -728,7 +695,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
// otherwise, send the click event to the device
}
if (!im->mp) {
if (!controller) {
return;
}
@@ -759,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
@@ -768,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;
@@ -868,7 +820,7 @@ sc_input_manager_handle_event(struct sc_input_manager *im,
bool control = im->controller;
switch (event->type) {
case SDL_TEXTINPUT:
if (!im->kp) {
if (!control) {
break;
}
sc_input_manager_process_text_input(im, &event->text);
@@ -880,13 +832,13 @@ sc_input_manager_handle_event(struct sc_input_manager *im,
sc_input_manager_process_key(im, &event->key);
break;
case SDL_MOUSEMOTION:
if (!im->mp) {
if (!control) {
break;
}
sc_input_manager_process_mouse_motion(im, &event->motion);
break;
case SDL_MOUSEWHEEL:
if (!im->mp) {
if (!control) {
break;
}
sc_input_manager_process_mouse_wheel(im, &event->wheel);
@@ -900,7 +852,7 @@ sc_input_manager_handle_event(struct sc_input_manager *im,
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
if (!im->mp) {
if (!control) {
break;
}
sc_input_manager_process_touch(im, &event->tfinger);

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_SDK,
SC_KEYBOARD_INPUT_MODE_AOA,
SC_KEYBOARD_INPUT_MODE_INJECT,
SC_KEYBOARD_INPUT_MODE_HID,
};
enum sc_mouse_input_mode {
SC_MOUSE_INPUT_MODE_AUTO,
SC_MOUSE_INPUT_MODE_DISABLED,
SC_MOUSE_INPUT_MODE_SDK,
SC_MOUSE_INPUT_MODE_AOA,
SC_MOUSE_INPUT_MODE_INJECT,
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;
@@ -634,23 +634,25 @@ aoa_hid_end:
}
}
if (use_aoa_keyboard && !hid_keyboard_initialized) {
LOGE("Fallback to --keyboard=sdk (--keyboard=aoa ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_SDK;
if (use_hid_keyboard && !hid_keyboard_initialized) {
LOGE("Fallback to default keyboard injection method "
"(-K/--hid-keyboard ignored)");
options->keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT;
}
if (use_aoa_mouse && !hid_mouse_initialized) {
LOGE("Fallback to --keyboard=sdk (--keyboard=aoa ignored)");
options->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
if (use_hid_mouse && !hid_mouse_initialized) {
LOGE("Fallback to default mouse injection method "
"(-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
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_SDK) {
if (options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_INJECT) {
sc_keyboard_inject_init(&s->keyboard_inject, &s->controller,
options->key_inject_mode,
options->forward_key_repeat);
@@ -658,7 +660,7 @@ aoa_hid_end:
}
// mouse_input_mode may have been reset if HID mode failed
if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK) {
if (options->mouse_input_mode == SC_MOUSE_INPUT_MODE_INJECT) {
sc_mouse_inject_init(&s->mouse_inject, &s->controller);
mp = &s->mouse_inject.mouse_processor;
}

View File

@@ -62,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);
@@ -117,15 +117,10 @@ scrcpy_otg(struct scrcpy_options *options) {
}
aoa_initialized = true;
assert(options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AOA
|| options->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_DISABLED);
assert(options->mouse_input_mode == SC_MOUSE_INPUT_MODE_AOA
|| options->mouse_input_mode == SC_MOUSE_INPUT_MODE_DISABLED);
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");
}
}
display = createDisplay();
setDisplaySurface(display, surface, videoRotation, contentRect, unlockedVideoRect, layerStack);
}
@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);
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);
}
// 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);
nativeSetupMethod.setAccessible(true);
initResult = (int) nativeSetupMethod.invoke(audioRecord, new WeakReference<AudioRecord>(audioRecord), attributes, sampleRateArray,
channelMask, channelIndexMask, audioRecord.getAudioFormat(), bufferSizeInBytes, session, attributionSourceParcel, 0L, 0);
}
}

View File

@@ -41,21 +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) {
try {
getPrimaryClipMethod = manager.getClass()
.getMethod("getPrimaryClip", String.class, String.class, int.class, int.class, boolean.class);
getMethodVersion = 4;
} catch (NoSuchMethodException e5) {
getPrimaryClipMethod = manager.getClass()
.getMethod("getPrimaryClip", String.class, String.class, String.class, String.class, int.class, int.class,
boolean.class);
getMethodVersion = 5;
}
}
getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class, int.class, String.class);
getMethodVersion = 3;
}
}
}
@@ -100,13 +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);
case 4:
// The last boolean parameter is "userOperate"
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, FakeContext.ROOT_UID, 0, true);
default:
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, null, 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 {
return (IBinder) CLASS.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure);
} catch (Exception e) {
throw new AssertionError(e);
}
public static IBinder createDisplay(String name, boolean secure) throws Exception {
return (IBinder) CLASS.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure);
}
private static Method getGetBuiltInDisplayMethod() throws NoSuchMethodException {