Compare commits

..

1 Commits

Author SHA1 Message Date
Romain Vimont
d25f38266d Extract device connection to a separate doc page
Create a new "Connection" documentation page.
2023-07-14 23:21:14 +02:00
19 changed files with 58 additions and 175 deletions

View File

@@ -5,7 +5,7 @@
_pronounced "**scr**een **c**o**py**"_ _pronounced "**scr**een **c**o**py**"_
This application mirrors Android devices (video and audio) connected via This application mirrors Android devices (video and audio) connected via
USB or [over TCP/IP](doc/connection.md#tcpip-wireless), and allows to control the USB or [over TCP/IP](doc/device.md#tcpip-wireless), and allows to control the
device with the keyboard and the mouse of the computer. It does not require any device with the keyboard and the mouse of the computer. It does not require any
_root_ access. It works on _Linux_, _Windows_ and _macOS_. _root_ access. It works on _Linux_, _Windows_ and _macOS_.

View File

@@ -44,8 +44,6 @@ _scrcpy() {
--no-video-playback --no-video-playback
--otg --otg
-p --port= -p --port=
--pause-on-exit
--pause-on-exit=
--power-off-on-close --power-off-on-close
--prefer-text --prefer-text
--print-fps --print-fps
@@ -99,10 +97,6 @@ _scrcpy() {
COMPREPLY=($(compgen -W 'unlocked initial 0 1 2 3' -- "$cur")) COMPREPLY=($(compgen -W 'unlocked initial 0 1 2 3' -- "$cur"))
return return
;; ;;
--pause-on-exit)
COMPREPLY=($(compgen -W 'true false if-error' -- "$cur"))
return
;;
-r|--record) -r|--record)
COMPREPLY=($(compgen -f -- "$cur")) COMPREPLY=($(compgen -f -- "$cur"))
return return

View File

@@ -1,2 +1,4 @@
@echo off @echo off
scrcpy.exe --pause-on-exit=if-error %* scrcpy.exe %*
:: if the exit code is >= 1, then pause
if errorlevel 1 pause

View File

@@ -5,7 +5,7 @@ Comment=Display and control your Android device
# For some users, the PATH or ADB environment variables are set from the shell # For some users, the PATH or ADB environment variables are set from the shell
# startup file, like .bashrc or .zshrc… Run an interactive shell to get # startup file, like .bashrc or .zshrc… Run an interactive shell to get
# environment correctly initialized. # environment correctly initialized.
Exec=/bin/sh -c "\"\\$SHELL\" -i -c scrcpy --pause-on-exit=if-error" Exec=/bin/bash --norc --noprofile -i -c "\"\\$SHELL\" -i -c scrcpy || read -p 'Press Enter to quit...'"
Icon=scrcpy Icon=scrcpy
Terminal=true Terminal=true
Type=Application Type=Application

View File

@@ -50,7 +50,6 @@ arguments=(
'--no-video-playback[Disable video playback]' '--no-video-playback[Disable video playback]'
'--otg[Run in OTG mode \(simulating physical keyboard and mouse\)]' '--otg[Run in OTG mode \(simulating physical keyboard and mouse\)]'
{-p,--port=}'[\[port\[\:port\]\] Set the TCP port \(range\) used by the client to listen]' {-p,--port=}'[\[port\[\:port\]\] Set the TCP port \(range\) used by the client to listen]'
'--pause-on-exit=[Make scrcpy pause before exiting]:mode:(true false if-error)'
'--power-off-on-close[Turn the device screen off when closing scrcpy]' '--power-off-on-close[Turn the device screen off when closing scrcpy]'
'--prefer-text[Inject alpha characters and space as text events instead of key events]' '--prefer-text[Inject alpha characters and space as text events instead of key events]'
'--print-fps[Start FPS counter, to print frame logs to the console]' '--print-fps[Start FPS counter, to print frame logs to the console]'

View File

@@ -267,16 +267,6 @@ Set the TCP port (range) used by the client to listen.
Default is 27183:27199. Default is 27183:27199.
.TP
\fB\-\-pause\-on\-exit\fR[=\fImode\fR]
Configure pause on exit. Possible values are "true" (always pause on exit), "false" (never pause on exit) and "if-error" (pause only if an error occured).
This is useful to prevent the terminal window from automatically closing, so that error messages can be read.
Default is "false".
Passing the option without argument is equivalent to passing "true".
.TP .TP
.B \-\-power\-off\-on\-close .B \-\-power\-off\-on\-close
Turn the device screen off when closing scrcpy. Turn the device screen off when closing scrcpy.

View File

@@ -79,7 +79,6 @@ enum {
OPT_AUDIO_SOURCE, OPT_AUDIO_SOURCE,
OPT_KILL_ADB_ON_CLOSE, OPT_KILL_ADB_ON_CLOSE,
OPT_TIME_LIMIT, OPT_TIME_LIMIT,
OPT_PAUSE_ON_EXIT,
}; };
struct sc_option { struct sc_option {
@@ -464,20 +463,6 @@ static const struct sc_option options[] = {
"Default is " STR(DEFAULT_LOCAL_PORT_RANGE_FIRST) ":" "Default is " STR(DEFAULT_LOCAL_PORT_RANGE_FIRST) ":"
STR(DEFAULT_LOCAL_PORT_RANGE_LAST) ".", STR(DEFAULT_LOCAL_PORT_RANGE_LAST) ".",
}, },
{
.longopt_id = OPT_PAUSE_ON_EXIT,
.longopt = "pause-on-exit",
.argdesc = "mode",
.optional_arg = true,
.text = "Configure pause on exit. Possible values are \"true\" (always "
"pause on exit), \"false\" (never pause on exit) and "
"\"if-error\" (pause only if an error occured).\n"
"This is useful to prevent the terminal window from "
"automatically closing, so that error messages can be read.\n"
"Default is \"false\".\n"
"Passing the option without argument is equivalent to passing "
"\"true\".",
},
{ {
.longopt_id = OPT_POWER_OFF_ON_CLOSE, .longopt_id = OPT_POWER_OFF_ON_CLOSE,
.longopt = "power-off-on-close", .longopt = "power-off-on-close",
@@ -1652,29 +1637,6 @@ parse_time_limit(const char *s, sc_tick *tick) {
return true; return true;
} }
static bool
parse_pause_on_exit(const char *s, enum sc_pause_on_exit *pause_on_exit) {
if (!s || !strcmp(s, "true")) {
*pause_on_exit = SC_PAUSE_ON_EXIT_TRUE;
return true;
}
if (!strcmp(s, "false")) {
*pause_on_exit = SC_PAUSE_ON_EXIT_FALSE;
return true;
}
if (!strcmp(s, "if-error")) {
*pause_on_exit = SC_PAUSE_ON_EXIT_IF_ERROR;
return true;
}
LOGE("Unsupported pause on exit mode: %s "
"(expected true, false or if-error)", optarg);
return false;
}
static bool static bool
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
const char *optstring, const struct option *longopts) { const char *optstring, const struct option *longopts) {
@@ -2015,11 +1977,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false; return false;
} }
break; break;
case OPT_PAUSE_ON_EXIT:
if (!parse_pause_on_exit(optarg, &args->pause_on_exit)) {
return false;
}
break;
default: default:
// getopt prints the error message on stderr // getopt prints the error message on stderr
return false; return false;
@@ -2065,6 +2022,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
opts->audio_playback = false; opts->audio_playback = false;
} }
if (!opts->video_playback && !otg) {
// If video playback is disabled and OTG are disabled, then there is
// no way to control the device.
opts->control = false;
}
if (opts->video && !opts->video_playback && !opts->record_filename if (opts->video && !opts->video_playback && !opts->record_filename
&& !v4l2) { && !v4l2) {
LOGI("No video playback, no recording, no V4L2 sink: video disabled"); LOGI("No video playback, no recording, no V4L2 sink: video disabled");
@@ -2233,37 +2196,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return true; return true;
} }
static enum sc_pause_on_exit
sc_get_pause_on_exit(int argc, char *argv[]) {
// Read arguments backwards so that the last --pause-on-exit is considered
// (same behavior as getopt())
for (int i = argc - 1; i >= 1; --i) {
const char *arg = argv[i];
// Starts with "--pause-on-exit"
if (!strncmp("--pause-on-exit", arg, 15)) {
if (arg[15] == '\0') {
// No argument
return SC_PAUSE_ON_EXIT_TRUE;
}
if (arg[15] != '=') {
// Invalid parameter, ignore
return SC_PAUSE_ON_EXIT_FALSE;
}
const char *value = &arg[16];
if (!strcmp(value, "true")) {
return SC_PAUSE_ON_EXIT_TRUE;
}
if (!strcmp(value, "if-error")) {
return SC_PAUSE_ON_EXIT_IF_ERROR;
}
// Set to false, inclusing when the value is invalid
return SC_PAUSE_ON_EXIT_FALSE;
}
}
return false;
}
bool bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) { scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
struct sc_getopt_adapter adapter; struct sc_getopt_adapter adapter;
@@ -2277,11 +2209,5 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
sc_getopt_adapter_destroy(&adapter); sc_getopt_adapter_destroy(&adapter);
if (!ret && args->pause_on_exit == SC_PAUSE_ON_EXIT_FALSE) {
// Check if "--pause-on-exit" is present in the arguments list, because
// it must be taken into account even if command line parsing failed
args->pause_on_exit = sc_get_pause_on_exit(argc, argv);
}
return ret; return ret;
} }

View File

@@ -7,17 +7,10 @@
#include "options.h" #include "options.h"
enum sc_pause_on_exit {
SC_PAUSE_ON_EXIT_TRUE,
SC_PAUSE_ON_EXIT_FALSE,
SC_PAUSE_ON_EXIT_IF_ERROR,
};
struct scrcpy_cli_args { struct scrcpy_cli_args {
struct scrcpy_options opts; struct scrcpy_options opts;
bool help; bool help;
bool version; bool version;
enum sc_pause_on_exit pause_on_exit;
}; };
void void

View File

@@ -53,7 +53,7 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
display->mipmaps = true; display->mipmaps = true;
} else { } else {
LOGW("Trilinear filtering disabled " LOGW("Trilinear filtering disabled "
"(OpenGL 3.0+ or ES 2.0+ required)"); "(OpenGL 3.0+ or ES 2.0+ required");
} }
} else { } else {
LOGI("Trilinear filtering disabled"); LOGI("Trilinear filtering disabled");

View File

@@ -39,32 +39,26 @@ main_scrcpy(int argc, char *argv[]) {
.opts = scrcpy_options_default, .opts = scrcpy_options_default,
.help = false, .help = false,
.version = false, .version = false,
.pause_on_exit = SC_PAUSE_ON_EXIT_FALSE,
}; };
#ifndef NDEBUG #ifndef NDEBUG
args.opts.log_level = SC_LOG_LEVEL_DEBUG; args.opts.log_level = SC_LOG_LEVEL_DEBUG;
#endif #endif
enum scrcpy_exit_code ret;
if (!scrcpy_parse_args(&args, argc, argv)) { if (!scrcpy_parse_args(&args, argc, argv)) {
ret = SCRCPY_EXIT_FAILURE; return SCRCPY_EXIT_FAILURE;
goto end;
} }
sc_set_log_level(args.opts.log_level); sc_set_log_level(args.opts.log_level);
if (args.help) { if (args.help) {
scrcpy_print_usage(argv[0]); scrcpy_print_usage(argv[0]);
ret = SCRCPY_EXIT_SUCCESS; return SCRCPY_EXIT_SUCCESS;
goto end;
} }
if (args.version) { if (args.version) {
scrcpy_print_version(); scrcpy_print_version();
ret = SCRCPY_EXIT_SUCCESS; return SCRCPY_EXIT_SUCCESS;
goto end;
} }
#ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL #ifdef SCRCPY_LAVF_REQUIRES_REGISTER_ALL
@@ -78,26 +72,18 @@ main_scrcpy(int argc, char *argv[]) {
#endif #endif
if (!net_init()) { if (!net_init()) {
ret = SCRCPY_EXIT_FAILURE; return SCRCPY_EXIT_FAILURE;
goto end;
} }
sc_log_configure(); sc_log_configure();
#ifdef HAVE_USB #ifdef HAVE_USB
ret = args.opts.otg ? scrcpy_otg(&args.opts) : scrcpy(&args.opts); enum scrcpy_exit_code ret = args.opts.otg ? scrcpy_otg(&args.opts)
: scrcpy(&args.opts);
#else #else
ret = scrcpy(&args.opts); enum scrcpy_exit_code ret = scrcpy(&args.opts);
#endif #endif
end:
if (args.pause_on_exit == SC_PAUSE_ON_EXIT_TRUE ||
(args.pause_on_exit == SC_PAUSE_ON_EXIT_IF_ERROR &&
ret != SCRCPY_EXIT_SUCCESS)) {
printf("Press Enter to continue...\n");
getchar();
}
return ret; return ret;
} }

View File

@@ -252,9 +252,7 @@ sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer,
// Contrary to the video demuxer, keep mirroring if only the audio fails // Contrary to the video demuxer, keep mirroring if only the audio fails
// (unless --require-audio is set). // (unless --require-audio is set).
if (status == SC_DEMUXER_STATUS_EOS) { if (status == SC_DEMUXER_STATUS_ERROR
PUSH_EVENT(SC_EVENT_DEVICE_DISCONNECTED);
} else if (status == SC_DEMUXER_STATUS_ERROR
|| (status == SC_DEMUXER_STATUS_DISABLED || (status == SC_DEMUXER_STATUS_DISABLED
&& options->require_audio)) { && options->require_audio)) {
PUSH_EVENT(SC_EVENT_DEMUXER_ERROR); PUSH_EVENT(SC_EVENT_DEMUXER_ERROR);
@@ -450,7 +448,9 @@ scrcpy(struct scrcpy_options *options) {
struct sc_file_pusher *fp = NULL; struct sc_file_pusher *fp = NULL;
if (options->video_playback && options->control) { // control implies video playback
assert(!options->control || options->video_playback);
if (options->control) {
if (!sc_file_pusher_init(&s->file_pusher, serial, if (!sc_file_pusher_init(&s->file_pusher, serial,
options->push_target)) { options->push_target)) {
goto end; goto end;

View File

@@ -77,13 +77,6 @@ In particular, if you get the following error:
then your device has no Opus encoder: try `scrcpy --audio-codec=aac`. then your device has no Opus encoder: try `scrcpy --audio-codec=aac`.
For advanced usage, to pass arbitrary parameters to the [`MediaFormat`],
check `--audio-codec-options` in the manpage or in `scrcpy --help`.
[`MediaFormat`]: https://developer.android.com/reference/android/media/MediaFormat
## Encoder
Several encoders may be available on the device. They can be listed by: Several encoders may be available on the device. They can be listed by:
@@ -93,10 +86,15 @@ scrcpy --list-encoders
To select a specific encoder: To select a specific encoder:
```bash ```
scrcpy --audio-codec=opus --audio-encoder='c2.android.opus.encoder' scrcpy --audio-codec=opus --audio-encoder='c2.android.opus.encoder'
``` ```
For advanced usage, to pass arbitrary parameters to the [`MediaFormat`],
check `--audio-codec-options` in the manpage or in `scrcpy --help`.
[`MediaFormat`]: https://developer.android.com/reference/android/media/MediaFormat
## Bit rate ## Bit rate

View File

@@ -1,7 +1,23 @@
# Device # Device
Some command line arguments perform actions on the device itself while scrcpy is ## Display
running.
If several displays are available on the Android device, it is possible to
select the display to mirror:
```bash
scrcpy --display=1
```
The list of display ids can be retrieved by:
```bash
scrcpy --list-displays
```
A secondary display may only be controlled if the device runs at least Android
10 (otherwise it is mirrored as read-only).
## Stay awake ## Stay awake
@@ -78,3 +94,4 @@ By default, on start, the device is powered on. To prevent this behavior:
```bash ```bash
scrcpy --no-power-on scrcpy --no-power-on
``` ```

View File

@@ -66,14 +66,6 @@ scrcpy --video-codec=av1
H265 may provide better quality, but H264 should provide lower latency. H265 may provide better quality, but H264 should provide lower latency.
AV1 encoders are not common on current Android devices. AV1 encoders are not common on current Android devices.
For advanced usage, to pass arbitrary parameters to the [`MediaFormat`],
check `--video-codec-options` in the manpage or in `scrcpy --help`.
[`MediaFormat`]: https://developer.android.com/reference/android/media/MediaFormat
## Encoder
Several encoders may be available on the device. They can be listed by: Several encoders may be available on the device. They can be listed by:
```bash ```bash
@@ -87,6 +79,11 @@ try another one:
scrcpy --video-codec=h264 --video-encoder='OMX.qcom.video.encoder.avc' scrcpy --video-codec=h264 --video-encoder='OMX.qcom.video.encoder.avc'
``` ```
For advanced usage, to pass arbitrary parameters to the [`MediaFormat`],
check `--video-codec-options` in the manpage or in `scrcpy --help`.
[`MediaFormat`]: https://developer.android.com/reference/android/media/MediaFormat
## Rotation ## Rotation
@@ -137,25 +134,6 @@ phone, landscape for a tablet).
If `--max-size` is also specified, resizing is applied after cropping. If `--max-size` is also specified, resizing is applied after cropping.
## Display
If several displays are available on the Android device, it is possible to
select the display to mirror:
```bash
scrcpy --display=1
```
The list of display ids can be retrieved by:
```bash
scrcpy --list-displays
```
A secondary display may only be controlled if the device runs at least Android
10 (otherwise it is mirrored as read-only).
## Buffering ## Buffering
By default, there is no video buffering, to get the lowest possible latency. By default, there is no video buffering, to get the lowest possible latency.

View File

@@ -154,7 +154,7 @@ public final class Workarounds {
} }
} }
private static void fillBaseContext() { public static void fillBaseContext() {
try { try {
fillActivityThread(); fillActivityThread();

View File

@@ -17,7 +17,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@SuppressLint("PrivateApi,DiscouragedPrivateApi") @SuppressLint("PrivateApi,DiscouragedPrivateApi")
public final class ActivityManager { public class ActivityManager {
private final IInterface manager; private final IInterface manager;
private Method getContentProviderExternalMethod; private Method getContentProviderExternalMethod;

View File

@@ -11,7 +11,7 @@ import android.os.IInterface;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
public final class ClipboardManager { public class ClipboardManager {
private final IInterface manager; private final IInterface manager;
private Method getPrimaryClipMethod; private Method getPrimaryClipMethod;
private Method setPrimaryClipMethod; private Method setPrimaryClipMethod;

View File

@@ -14,7 +14,7 @@ import java.io.Closeable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
public final class ContentProvider implements Closeable { public class ContentProvider implements Closeable {
public static final String TABLE_SYSTEM = "system"; public static final String TABLE_SYSTEM = "system";
public static final String TABLE_SECURE = "secure"; public static final String TABLE_SECURE = "secure";

View File

@@ -7,7 +7,7 @@ import android.os.IInterface;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
public final class StatusBarManager { public class StatusBarManager {
private final IInterface manager; private final IInterface manager;
private Method expandNotificationsPanelMethod; private Method expandNotificationsPanelMethod;