Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
315f467cc7 | |||
e98122c110 | |||
0c5d5b6339 |
551
app/src/cli.c
551
app/src/cli.c
@ -11,22 +11,69 @@
|
|||||||
#include "util/strbuf.h"
|
#include "util/strbuf.h"
|
||||||
#include "util/str_util.h"
|
#include "util/str_util.h"
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#define STR_IMPL_(x) #x
|
#define STR_IMPL_(x) #x
|
||||||
#define STR(x) STR_IMPL_(x)
|
#define STR(x) STR_IMPL_(x)
|
||||||
|
|
||||||
|
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
||||||
|
#define OPT_WINDOW_TITLE 1001
|
||||||
|
#define OPT_PUSH_TARGET 1002
|
||||||
|
#define OPT_ALWAYS_ON_TOP 1003
|
||||||
|
#define OPT_CROP 1004
|
||||||
|
#define OPT_RECORD_FORMAT 1005
|
||||||
|
#define OPT_PREFER_TEXT 1006
|
||||||
|
#define OPT_WINDOW_X 1007
|
||||||
|
#define OPT_WINDOW_Y 1008
|
||||||
|
#define OPT_WINDOW_WIDTH 1009
|
||||||
|
#define OPT_WINDOW_HEIGHT 1010
|
||||||
|
#define OPT_WINDOW_BORDERLESS 1011
|
||||||
|
#define OPT_MAX_FPS 1012
|
||||||
|
#define OPT_LOCK_VIDEO_ORIENTATION 1013
|
||||||
|
#define OPT_DISPLAY_ID 1014
|
||||||
|
#define OPT_ROTATION 1015
|
||||||
|
#define OPT_RENDER_DRIVER 1016
|
||||||
|
#define OPT_NO_MIPMAPS 1017
|
||||||
|
#define OPT_CODEC_OPTIONS 1018
|
||||||
|
#define OPT_FORCE_ADB_FORWARD 1019
|
||||||
|
#define OPT_DISABLE_SCREENSAVER 1020
|
||||||
|
#define OPT_SHORTCUT_MOD 1021
|
||||||
|
#define OPT_NO_KEY_REPEAT 1022
|
||||||
|
#define OPT_FORWARD_ALL_CLICKS 1023
|
||||||
|
#define OPT_LEGACY_PASTE 1024
|
||||||
|
#define OPT_ENCODER_NAME 1025
|
||||||
|
#define OPT_POWER_OFF_ON_CLOSE 1026
|
||||||
|
#define OPT_V4L2_SINK 1027
|
||||||
|
#define OPT_DISPLAY_BUFFER 1028
|
||||||
|
#define OPT_V4L2_BUFFER 1029
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
char shortopt;
|
char shortopt;
|
||||||
|
int longopt_id; // either shortopt or longopt_id is non-zero
|
||||||
const char *longopt;
|
const char *longopt;
|
||||||
// no argument: argdesc == NULL && !optional_arg
|
// no argument: argdesc == NULL && !optional_arg
|
||||||
// optional argument: argdesc != NULL && optional_arg
|
// optional argument: argdesc != NULL && optional_arg
|
||||||
// required argument: argdesc != NULL && !optional_arg
|
// required argument: argdesc != NULL && !optional_arg
|
||||||
const char *argdesc;
|
const char *argdesc;
|
||||||
bool optional_arg;
|
bool optional_arg;
|
||||||
|
const char *text; // if NULL, the option does not appear in the help
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_EQUIVALENT_SHORTCUTS 3
|
||||||
|
struct sc_shortcut {
|
||||||
|
const char *shortcuts[MAX_EQUIVALENT_SHORTCUTS + 1];
|
||||||
const char *text;
|
const char *text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sc_getopt_adapter {
|
||||||
|
char *optstring;
|
||||||
|
struct option *longopts;
|
||||||
|
};
|
||||||
|
|
||||||
static const struct sc_option options[] = {
|
static const struct sc_option options[] = {
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_ALWAYS_ON_TOP,
|
||||||
.longopt = "always-on-top",
|
.longopt = "always-on-top",
|
||||||
.text = "Make scrcpy window always on top (above other windows).",
|
.text = "Make scrcpy window always on top (above other windows).",
|
||||||
},
|
},
|
||||||
@ -39,6 +86,7 @@ static const struct sc_option options[] = {
|
|||||||
"Default is " STR(DEFAULT_BIT_RATE) ".",
|
"Default is " STR(DEFAULT_BIT_RATE) ".",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_CODEC_OPTIONS,
|
||||||
.longopt = "codec-options",
|
.longopt = "codec-options",
|
||||||
.argdesc = "key[:type]=value[,...]",
|
.argdesc = "key[:type]=value[,...]",
|
||||||
.text = "Set a list of comma-separated key:type=value options for the "
|
.text = "Set a list of comma-separated key:type=value options for the "
|
||||||
@ -50,6 +98,7 @@ static const struct sc_option options[] = {
|
|||||||
"<https://d.android.com/reference/android/media/MediaFormat>",
|
"<https://d.android.com/reference/android/media/MediaFormat>",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_CROP,
|
||||||
.longopt = "crop",
|
.longopt = "crop",
|
||||||
.argdesc = "width:height:x:y",
|
.argdesc = "width:height:x:y",
|
||||||
.text = "Crop the device screen on the server.\n"
|
.text = "Crop the device screen on the server.\n"
|
||||||
@ -58,10 +107,12 @@ static const struct sc_option options[] = {
|
|||||||
"Any --max-size value is cmoputed on the cropped size.",
|
"Any --max-size value is cmoputed on the cropped size.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_DISABLE_SCREENSAVER,
|
||||||
.longopt = "disable-screensaver",
|
.longopt = "disable-screensaver",
|
||||||
.text = "Disable screensaver while scrcpy is running.",
|
.text = "Disable screensaver while scrcpy is running.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_DISPLAY_ID,
|
||||||
.longopt = "display",
|
.longopt = "display",
|
||||||
.argdesc = "id",
|
.argdesc = "id",
|
||||||
.text = "Specify the display id to mirror.\n"
|
.text = "Specify the display id to mirror.\n"
|
||||||
@ -71,6 +122,7 @@ static const struct sc_option options[] = {
|
|||||||
"Default is 0.",
|
"Default is 0.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_DISPLAY_BUFFER,
|
||||||
.longopt = "display-buffer",
|
.longopt = "display-buffer",
|
||||||
.argdesc = "ms",
|
.argdesc = "ms",
|
||||||
.text = "Add a buffering delay (in milliseconds) before displaying. "
|
.text = "Add a buffering delay (in milliseconds) before displaying. "
|
||||||
@ -78,16 +130,19 @@ static const struct sc_option options[] = {
|
|||||||
"Default is 0 (no buffering).",
|
"Default is 0 (no buffering).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_ENCODER_NAME,
|
||||||
.longopt = "encoder",
|
.longopt = "encoder",
|
||||||
.argdesc = "name",
|
.argdesc = "name",
|
||||||
.text = "Use a specific MediaCodec encoder (must be a H.264 encoder).",
|
.text = "Use a specific MediaCodec encoder (must be a H.264 encoder).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_FORCE_ADB_FORWARD,
|
||||||
.longopt = "force-adb-forward",
|
.longopt = "force-adb-forward",
|
||||||
.text = "Do not attempt to use \"adb reverse\" to connect to the "
|
.text = "Do not attempt to use \"adb reverse\" to connect to the "
|
||||||
"device.",
|
"device.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_FORWARD_ALL_CLICKS,
|
||||||
.longopt = "forward-all-clicks",
|
.longopt = "forward-all-clicks",
|
||||||
.text = "By default, right-click triggers BACK (or POWER on) and "
|
.text = "By default, right-click triggers BACK (or POWER on) and "
|
||||||
"middle-click triggers HOME. This option disables these "
|
"middle-click triggers HOME. This option disables these "
|
||||||
@ -114,6 +169,7 @@ static const struct sc_option options[] = {
|
|||||||
.text = "Print this help.",
|
.text = "Print this help.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_LEGACY_PASTE,
|
||||||
.longopt = "legacy-paste",
|
.longopt = "legacy-paste",
|
||||||
.text = "Inject computer clipboard text as a sequence of key events "
|
.text = "Inject computer clipboard text as a sequence of key events "
|
||||||
"on Ctrl+v (like MOD+Shift+v).\n"
|
"on Ctrl+v (like MOD+Shift+v).\n"
|
||||||
@ -121,6 +177,7 @@ static const struct sc_option options[] = {
|
|||||||
"expected when setting the device clipboard programmatically.",
|
"expected when setting the device clipboard programmatically.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_LOCK_VIDEO_ORIENTATION,
|
||||||
.longopt = "lock-video-orientation",
|
.longopt = "lock-video-orientation",
|
||||||
.argdesc = "value",
|
.argdesc = "value",
|
||||||
.optional_arg = true,
|
.optional_arg = true,
|
||||||
@ -134,6 +191,7 @@ static const struct sc_option options[] = {
|
|||||||
"\"initial\".",
|
"\"initial\".",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_MAX_FPS,
|
||||||
.longopt = "max-fps",
|
.longopt = "max-fps",
|
||||||
.argdesc = "value",
|
.argdesc = "value",
|
||||||
.text = "Limit the frame rate of screen capture (officially supported "
|
.text = "Limit the frame rate of screen capture (officially supported "
|
||||||
@ -163,10 +221,12 @@ static const struct sc_option options[] = {
|
|||||||
"is enabled).",
|
"is enabled).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_NO_KEY_REPEAT,
|
||||||
.longopt = "no-key-repeat",
|
.longopt = "no-key-repeat",
|
||||||
.text = "Do not forward repeated key events when a key is held down.",
|
.text = "Do not forward repeated key events when a key is held down.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_NO_MIPMAPS,
|
||||||
.longopt = "no-mipmaps",
|
.longopt = "no-mipmaps",
|
||||||
.text = "If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then "
|
.text = "If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then "
|
||||||
"mipmaps are automatically generated to improve downscaling "
|
"mipmaps are automatically generated to improve downscaling "
|
||||||
@ -181,10 +241,12 @@ static const struct sc_option options[] = {
|
|||||||
STR(DEFAULT_LOCAL_PORT_RANGE_LAST) ".",
|
STR(DEFAULT_LOCAL_PORT_RANGE_LAST) ".",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_POWER_OFF_ON_CLOSE,
|
||||||
.longopt = "power-off-on-close",
|
.longopt = "power-off-on-close",
|
||||||
.text = "Turn the device screen off when closing scrcpy.",
|
.text = "Turn the device screen off when closing scrcpy.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_PREFER_TEXT,
|
||||||
.longopt = "prefer-text",
|
.longopt = "prefer-text",
|
||||||
.text = "Inject alpha characters and space as text events instead of"
|
.text = "Inject alpha characters and space as text events instead of"
|
||||||
"key events.\n"
|
"key events.\n"
|
||||||
@ -193,6 +255,7 @@ static const struct sc_option options[] = {
|
|||||||
"keys in games (typically WASD).",
|
"keys in games (typically WASD).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_PUSH_TARGET,
|
||||||
.longopt = "push-target",
|
.longopt = "push-target",
|
||||||
.argdesc = "path",
|
.argdesc = "path",
|
||||||
.text = "Set the target directory for pushing files to the device by "
|
.text = "Set the target directory for pushing files to the device by "
|
||||||
@ -208,11 +271,13 @@ static const struct sc_option options[] = {
|
|||||||
"set, or by the file extension (.mp4 or .mkv).",
|
"set, or by the file extension (.mp4 or .mkv).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_RECORD_FORMAT,
|
||||||
.longopt = "record-format",
|
.longopt = "record-format",
|
||||||
.argdesc = "format",
|
.argdesc = "format",
|
||||||
.text = "Force recording format (either mp4 or mkv).",
|
.text = "Force recording format (either mp4 or mkv).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_RENDER_DRIVER,
|
||||||
.longopt = "render-driver",
|
.longopt = "render-driver",
|
||||||
.argdesc = "name",
|
.argdesc = "name",
|
||||||
.text = "Request SDL to use the given render driver (this is just a "
|
.text = "Request SDL to use the given render driver (this is just a "
|
||||||
@ -222,6 +287,12 @@ static const struct sc_option options[] = {
|
|||||||
"<https://wiki.libsdl.org/SDL_HINT_RENDER_DRIVER>",
|
"<https://wiki.libsdl.org/SDL_HINT_RENDER_DRIVER>",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// deprecated
|
||||||
|
.longopt_id = OPT_RENDER_EXPIRED_FRAMES,
|
||||||
|
.longopt = "render-expired-frames",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_ROTATION,
|
||||||
.longopt = "rotation",
|
.longopt = "rotation",
|
||||||
.argdesc = "value",
|
.argdesc = "value",
|
||||||
.text = "Set the initial display rotation.\n"
|
.text = "Set the initial display rotation.\n"
|
||||||
@ -236,6 +307,7 @@ static const struct sc_option options[] = {
|
|||||||
"are connected to adb.",
|
"are connected to adb.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_SHORTCUT_MOD,
|
||||||
.longopt = "shortcut-mod",
|
.longopt = "shortcut-mod",
|
||||||
.argdesc = "key[+...][,...]",
|
.argdesc = "key[+...][,...]",
|
||||||
.text = "Specify the modifiers to use for scrcpy shortcuts.\n"
|
.text = "Specify the modifiers to use for scrcpy shortcuts.\n"
|
||||||
@ -261,6 +333,7 @@ static const struct sc_option options[] = {
|
|||||||
},
|
},
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_V4L2_SINK,
|
||||||
.longopt = "v4l2-sink",
|
.longopt = "v4l2-sink",
|
||||||
.argdesc = "/dev/videoN",
|
.argdesc = "/dev/videoN",
|
||||||
.text = "Output to v4l2loopback device.\n"
|
.text = "Output to v4l2loopback device.\n"
|
||||||
@ -268,6 +341,7 @@ static const struct sc_option options[] = {
|
|||||||
"--lock-video-orientation).",
|
"--lock-video-orientation).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_V4L2_BUFFER,
|
||||||
.longopt = "v4l2-buffer",
|
.longopt = "v4l2-buffer",
|
||||||
.argdesc = "ms",
|
.argdesc = "ms",
|
||||||
.text = "Add a buffering delay (in milliseconds) before pushing "
|
.text = "Add a buffering delay (in milliseconds) before pushing "
|
||||||
@ -300,33 +374,39 @@ static const struct sc_option options[] = {
|
|||||||
"is plugged in.",
|
"is plugged in.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_WINDOW_BORDERLESS,
|
||||||
.longopt = "window-borderless",
|
.longopt = "window-borderless",
|
||||||
.text = "Disable window decorations (display borderless window)."
|
.text = "Disable window decorations (display borderless window)."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_WINDOW_TITLE,
|
||||||
.longopt = "window-title",
|
.longopt = "window-title",
|
||||||
.argdesc = "text",
|
.argdesc = "text",
|
||||||
.text = "Set a custom window title.",
|
.text = "Set a custom window title.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_WINDOW_X,
|
||||||
.longopt = "window-x",
|
.longopt = "window-x",
|
||||||
.argdesc = "value",
|
.argdesc = "value",
|
||||||
.text = "Set the initial window horizontal position.\n"
|
.text = "Set the initial window horizontal position.\n"
|
||||||
"Default is \"auto\".",
|
"Default is \"auto\".",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_WINDOW_Y,
|
||||||
.longopt = "window-y",
|
.longopt = "window-y",
|
||||||
.argdesc = "value",
|
.argdesc = "value",
|
||||||
.text = "Set the initial window vertical position.\n"
|
.text = "Set the initial window vertical position.\n"
|
||||||
"Default is \"auto\".",
|
"Default is \"auto\".",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_WINDOW_WIDTH,
|
||||||
.longopt = "window-width",
|
.longopt = "window-width",
|
||||||
.argdesc = "value",
|
.argdesc = "value",
|
||||||
.text = "Set the initial window width.\n"
|
.text = "Set the initial window width.\n"
|
||||||
"Default is 0 (automatic).",
|
"Default is 0 (automatic).",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
.longopt_id = OPT_WINDOW_HEIGHT,
|
||||||
.longopt = "window-height",
|
.longopt = "window-height",
|
||||||
.argdesc = "value",
|
.argdesc = "value",
|
||||||
.text = "Set the initial window height.\n"
|
.text = "Set the initial window height.\n"
|
||||||
@ -334,6 +414,214 @@ static const struct sc_option options[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sc_shortcut shortcuts[] = {
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+f" },
|
||||||
|
.text = "Switch fullscreen mode",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+Left" },
|
||||||
|
.text = "Rotate display left",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+Right" },
|
||||||
|
.text = "Rotate display right",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+g" },
|
||||||
|
.text = "Resize window to 1:1 (pixel-perfect)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+w", "Double-click on black borders" },
|
||||||
|
.text = "Resize window to remove black borders",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+h", "Middle-click" },
|
||||||
|
.text = "Click on HOME",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = {
|
||||||
|
"MOD+b",
|
||||||
|
"MOD+Backspace",
|
||||||
|
"Right-click (when screen is on)",
|
||||||
|
},
|
||||||
|
.text = "Click on BACK",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+s" },
|
||||||
|
.text = "Click on APP_SWITCH",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+m" },
|
||||||
|
.text = "Click on MENU",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+Up" },
|
||||||
|
.text = "Click on VOLUME_UP",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+Down" },
|
||||||
|
.text = "Click on VOLUME_DOWN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+p" },
|
||||||
|
.text = "Click on POWER (turn screen on/off)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "Right-click (when screen is off)" },
|
||||||
|
.text = "Power on",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+o" },
|
||||||
|
.text = "Turn device screen off (keep mirroring)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+Shift+o" },
|
||||||
|
.text = "Turn device screen on",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+r" },
|
||||||
|
.text = "Rotate device screen",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+n" },
|
||||||
|
.text = "Expand notification panel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+Shift+n" },
|
||||||
|
.text = "Collapse notification panel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+c" },
|
||||||
|
.text = "Copy to clipboard (inject COPY keycode, Android >= 7 only)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+x" },
|
||||||
|
.text = "Cut to clipboard (inject CUT keycode, Android >= 7 only)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+v" },
|
||||||
|
.text = "Copy computer clipboard to device, then paste (inject PASTE "
|
||||||
|
"keycode, Android >= 7 only)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+Shift+v" },
|
||||||
|
.text = "Inject computer clipboard text as a sequence of key events",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+i" },
|
||||||
|
.text = "Enable/disable FPS counter (print frames/second in logs)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "Ctrl+click-and-move" },
|
||||||
|
.text = "Pinch-to-zoom from the center of the screen",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "Drag & drop APK file" },
|
||||||
|
.text = "Install APK from computer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "Drag & drop non-APK file" },
|
||||||
|
.text = "Push file to device (see --push-target)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *
|
||||||
|
sc_getopt_adapter_create_optstring(void) {
|
||||||
|
struct sc_strbuf buf;
|
||||||
|
if (!sc_strbuf_init(&buf, 64)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
|
||||||
|
const struct sc_option *opt = &options[i];
|
||||||
|
if (opt->shortopt) {
|
||||||
|
if (!sc_strbuf_append_char(&buf, opt->shortopt)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
// If there is an argument, add ':'
|
||||||
|
if (opt->argdesc) {
|
||||||
|
if (!sc_strbuf_append_char(&buf, ':')) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
// If the argument is optional, add another ':'
|
||||||
|
if (opt->optional_arg && !sc_strbuf_append_char(&buf, ':')) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.s;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(buf.s);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct option *
|
||||||
|
sc_getopt_adapter_create_longopts(void) {
|
||||||
|
struct option *longopts =
|
||||||
|
malloc((ARRAY_LEN(options) + 1) * sizeof(*longopts));
|
||||||
|
if (!longopts) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t out_idx = 0;
|
||||||
|
for (size_t i = 0; i < ARRAY_LEN(options); ++i) {
|
||||||
|
const struct sc_option *in = &options[i];
|
||||||
|
if (!in->longopt) {
|
||||||
|
// The longopts array must only contain long options
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct option *out = &longopts[out_idx++];
|
||||||
|
|
||||||
|
out->name = in->longopt;
|
||||||
|
|
||||||
|
if (!in->argdesc) {
|
||||||
|
assert(!in->optional_arg);
|
||||||
|
out->has_arg = no_argument;
|
||||||
|
} else if (in->optional_arg) {
|
||||||
|
out->has_arg = optional_argument;
|
||||||
|
} else {
|
||||||
|
out->has_arg = required_argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->flag = NULL;
|
||||||
|
|
||||||
|
// Either shortopt or longopt_id is set, but not both
|
||||||
|
assert(!!in->shortopt ^ !!in->longopt_id);
|
||||||
|
out->val = in->shortopt ? in->shortopt : in->longopt_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The array must be terminated by a NULL item
|
||||||
|
longopts[out_idx] = (struct option) {0};
|
||||||
|
|
||||||
|
return longopts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_getopt_adapter_init(struct sc_getopt_adapter *adapter) {
|
||||||
|
adapter->optstring = sc_getopt_adapter_create_optstring();
|
||||||
|
if (!adapter->optstring) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter->longopts = sc_getopt_adapter_create_longopts();
|
||||||
|
if (!adapter->longopts) {
|
||||||
|
free(adapter->optstring);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_getopt_adapter_destroy(struct sc_getopt_adapter *adapter) {
|
||||||
|
free(adapter->optstring);
|
||||||
|
free(adapter->longopts);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_option_usage_header(const struct sc_option *opt) {
|
print_option_usage_header(const struct sc_option *opt) {
|
||||||
struct sc_strbuf buf;
|
struct sc_strbuf buf;
|
||||||
@ -396,7 +684,11 @@ error:
|
|||||||
static void
|
static void
|
||||||
print_option_usage(const struct sc_option *opt, unsigned cols) {
|
print_option_usage(const struct sc_option *opt, unsigned cols) {
|
||||||
assert(cols > 8); // wrap_lines() requires indent < columns
|
assert(cols > 8); // wrap_lines() requires indent < columns
|
||||||
assert(opt->text);
|
|
||||||
|
if (!opt->text) {
|
||||||
|
// Option not documented in help (for example because it is deprecated)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
print_option_usage_header(opt);
|
print_option_usage_header(opt);
|
||||||
|
|
||||||
@ -410,9 +702,56 @@ print_option_usage(const struct sc_option *opt, unsigned cols) {
|
|||||||
free(text);
|
free(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_shortcuts_intro(unsigned cols) {
|
||||||
|
char *intro = wrap_lines(
|
||||||
|
"In the following list, MOD is the shortcut modifier. By default, it's "
|
||||||
|
"(left) Alt or (left) Super, but it can be configured by "
|
||||||
|
"--shortcut-mod (see above).", cols, 4);
|
||||||
|
if (!intro) {
|
||||||
|
fprintf(stderr, "<ERROR>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "%s\n", intro);
|
||||||
|
free(intro);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_shortcut(const struct sc_shortcut *shortcut, unsigned cols) {
|
||||||
|
assert(cols > 8); // wrap_lines() requires indent < columns
|
||||||
|
assert(shortcut->shortcuts[0]); // At least one shortcut
|
||||||
|
assert(shortcut->text);
|
||||||
|
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
unsigned i = 0;
|
||||||
|
while (shortcut->shortcuts[i]) {
|
||||||
|
fprintf(stderr, " %s\n", shortcut->shortcuts[i]);
|
||||||
|
++i;
|
||||||
|
};
|
||||||
|
|
||||||
|
char *text = wrap_lines(shortcut->text, cols, 8);
|
||||||
|
if (!text) {
|
||||||
|
fprintf(stderr, "<ERROR>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "%s\n", text);
|
||||||
|
free(text);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
scrcpy_print_usage(const char *arg0) {
|
scrcpy_print_usage(const char *arg0) {
|
||||||
const unsigned cols = 80; // For now, use an hardcoded value
|
unsigned cols = 80;
|
||||||
|
|
||||||
|
struct winsize ws;
|
||||||
|
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1) {
|
||||||
|
cols = ws.ws_col;
|
||||||
|
if (cols < 20) {
|
||||||
|
cols = 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Usage: %s [options]\n\n"
|
fprintf(stderr, "Usage: %s [options]\n\n"
|
||||||
"Options:\n", arg0);
|
"Options:\n", arg0);
|
||||||
@ -421,96 +760,11 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print shortcuts section
|
// Print shortcuts section
|
||||||
fprintf(stderr, "\n"
|
fprintf(stderr, "\nShortcuts:\n\n");
|
||||||
"Shortcuts:\n"
|
print_shortcuts_intro(cols);
|
||||||
"\n"
|
for (size_t i = 0; i < ARRAY_LEN(shortcuts); ++i) {
|
||||||
" In the following list, MOD is the shortcut modifier. By default,\n"
|
print_shortcut(&shortcuts[i], cols);
|
||||||
" it's (left) Alt or (left) Super, but it can be configured by\n"
|
}
|
||||||
" --shortcut-mod (see above).\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+f\n"
|
|
||||||
" Switch fullscreen mode\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+Left\n"
|
|
||||||
" Rotate display left\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+Right\n"
|
|
||||||
" Rotate display right\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+g\n"
|
|
||||||
" Resize window to 1:1 (pixel-perfect)\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+w\n"
|
|
||||||
" Double-click on black borders\n"
|
|
||||||
" Resize window to remove black borders\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+h\n"
|
|
||||||
" Middle-click\n"
|
|
||||||
" Click on HOME\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+b\n"
|
|
||||||
" MOD+Backspace\n"
|
|
||||||
" Right-click (when screen is on)\n"
|
|
||||||
" Click on BACK\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+s\n"
|
|
||||||
" Click on APP_SWITCH\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+m\n"
|
|
||||||
" Click on MENU\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+Up\n"
|
|
||||||
" Click on VOLUME_UP\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+Down\n"
|
|
||||||
" Click on VOLUME_DOWN\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+p\n"
|
|
||||||
" Click on POWER (turn screen on/off)\n"
|
|
||||||
"\n"
|
|
||||||
" Right-click (when screen is off)\n"
|
|
||||||
" Power on\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+o\n"
|
|
||||||
" Turn device screen off (keep mirroring)\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+Shift+o\n"
|
|
||||||
" Turn device screen on\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+r\n"
|
|
||||||
" Rotate device screen\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+n\n"
|
|
||||||
" Expand notification panel\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+Shift+n\n"
|
|
||||||
" Collapse notification panel\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+c\n"
|
|
||||||
" Copy to clipboard (inject COPY keycode, Android >= 7 only)\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+x\n"
|
|
||||||
" Cut to clipboard (inject CUT keycode, Android >= 7 only)\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+v\n"
|
|
||||||
" Copy computer clipboard to device, then paste (inject PASTE\n"
|
|
||||||
" keycode, Android >= 7 only)\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+Shift+v\n"
|
|
||||||
" Inject computer clipboard text as a sequence of key events\n"
|
|
||||||
"\n"
|
|
||||||
" MOD+i\n"
|
|
||||||
" Enable/disable FPS counter (print frames/second in logs)\n"
|
|
||||||
"\n"
|
|
||||||
" Ctrl+click-and-move\n"
|
|
||||||
" Pinch-to-zoom from the center of the screen\n"
|
|
||||||
"\n"
|
|
||||||
" Drag & drop APK file\n"
|
|
||||||
" Install APK from computer\n"
|
|
||||||
"\n"
|
|
||||||
" Drag & drop non-APK file\n"
|
|
||||||
" Push file to device (see --push-target)\n"
|
|
||||||
"\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -879,104 +1133,15 @@ guess_record_format(const char *filename) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
static bool
|
||||||
#define OPT_WINDOW_TITLE 1001
|
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
#define OPT_PUSH_TARGET 1002
|
const char *optstring, const struct option *longopts) {
|
||||||
#define OPT_ALWAYS_ON_TOP 1003
|
|
||||||
#define OPT_CROP 1004
|
|
||||||
#define OPT_RECORD_FORMAT 1005
|
|
||||||
#define OPT_PREFER_TEXT 1006
|
|
||||||
#define OPT_WINDOW_X 1007
|
|
||||||
#define OPT_WINDOW_Y 1008
|
|
||||||
#define OPT_WINDOW_WIDTH 1009
|
|
||||||
#define OPT_WINDOW_HEIGHT 1010
|
|
||||||
#define OPT_WINDOW_BORDERLESS 1011
|
|
||||||
#define OPT_MAX_FPS 1012
|
|
||||||
#define OPT_LOCK_VIDEO_ORIENTATION 1013
|
|
||||||
#define OPT_DISPLAY_ID 1014
|
|
||||||
#define OPT_ROTATION 1015
|
|
||||||
#define OPT_RENDER_DRIVER 1016
|
|
||||||
#define OPT_NO_MIPMAPS 1017
|
|
||||||
#define OPT_CODEC_OPTIONS 1018
|
|
||||||
#define OPT_FORCE_ADB_FORWARD 1019
|
|
||||||
#define OPT_DISABLE_SCREENSAVER 1020
|
|
||||||
#define OPT_SHORTCUT_MOD 1021
|
|
||||||
#define OPT_NO_KEY_REPEAT 1022
|
|
||||||
#define OPT_FORWARD_ALL_CLICKS 1023
|
|
||||||
#define OPT_LEGACY_PASTE 1024
|
|
||||||
#define OPT_ENCODER_NAME 1025
|
|
||||||
#define OPT_POWER_OFF_ON_CLOSE 1026
|
|
||||||
#define OPT_V4L2_SINK 1027
|
|
||||||
#define OPT_DISPLAY_BUFFER 1028
|
|
||||||
#define OPT_V4L2_BUFFER 1029
|
|
||||||
|
|
||||||
bool
|
|
||||||
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|
||||||
static const struct option long_options[] = {
|
|
||||||
{"always-on-top", no_argument, NULL, OPT_ALWAYS_ON_TOP},
|
|
||||||
{"bit-rate", required_argument, NULL, 'b'},
|
|
||||||
{"codec-options", required_argument, NULL, OPT_CODEC_OPTIONS},
|
|
||||||
{"crop", required_argument, NULL, OPT_CROP},
|
|
||||||
{"disable-screensaver", no_argument, NULL,
|
|
||||||
OPT_DISABLE_SCREENSAVER},
|
|
||||||
{"display", required_argument, NULL, OPT_DISPLAY_ID},
|
|
||||||
{"display-buffer", required_argument, NULL, OPT_DISPLAY_BUFFER},
|
|
||||||
{"encoder", required_argument, NULL, OPT_ENCODER_NAME},
|
|
||||||
{"force-adb-forward", no_argument, NULL,
|
|
||||||
OPT_FORCE_ADB_FORWARD},
|
|
||||||
{"forward-all-clicks", no_argument, NULL,
|
|
||||||
OPT_FORWARD_ALL_CLICKS},
|
|
||||||
{"fullscreen", no_argument, NULL, 'f'},
|
|
||||||
{"help", no_argument, NULL, 'h'},
|
|
||||||
{"hid-keyboard", no_argument, NULL, 'K'},
|
|
||||||
{"legacy-paste", no_argument, NULL, OPT_LEGACY_PASTE},
|
|
||||||
{"lock-video-orientation", optional_argument, NULL,
|
|
||||||
OPT_LOCK_VIDEO_ORIENTATION},
|
|
||||||
{"max-fps", required_argument, NULL, OPT_MAX_FPS},
|
|
||||||
{"max-size", required_argument, NULL, 'm'},
|
|
||||||
{"no-control", no_argument, NULL, 'n'},
|
|
||||||
{"no-display", no_argument, NULL, 'N'},
|
|
||||||
{"no-key-repeat", no_argument, NULL, OPT_NO_KEY_REPEAT},
|
|
||||||
{"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS},
|
|
||||||
{"port", required_argument, NULL, 'p'},
|
|
||||||
{"prefer-text", no_argument, NULL, OPT_PREFER_TEXT},
|
|
||||||
{"push-target", required_argument, NULL, OPT_PUSH_TARGET},
|
|
||||||
{"record", required_argument, NULL, 'r'},
|
|
||||||
{"record-format", required_argument, NULL, OPT_RECORD_FORMAT},
|
|
||||||
{"render-driver", required_argument, NULL, OPT_RENDER_DRIVER},
|
|
||||||
{"render-expired-frames", no_argument, NULL,
|
|
||||||
OPT_RENDER_EXPIRED_FRAMES},
|
|
||||||
{"rotation", required_argument, NULL, OPT_ROTATION},
|
|
||||||
{"serial", required_argument, NULL, 's'},
|
|
||||||
{"shortcut-mod", required_argument, NULL, OPT_SHORTCUT_MOD},
|
|
||||||
{"show-touches", no_argument, NULL, 't'},
|
|
||||||
{"stay-awake", no_argument, NULL, 'w'},
|
|
||||||
{"turn-screen-off", no_argument, NULL, 'S'},
|
|
||||||
#ifdef HAVE_V4L2
|
|
||||||
{"v4l2-sink", required_argument, NULL, OPT_V4L2_SINK},
|
|
||||||
{"v4l2-buffer", required_argument, NULL, OPT_V4L2_BUFFER},
|
|
||||||
#endif
|
|
||||||
{"verbosity", required_argument, NULL, 'V'},
|
|
||||||
{"version", no_argument, NULL, 'v'},
|
|
||||||
{"window-title", required_argument, NULL, OPT_WINDOW_TITLE},
|
|
||||||
{"window-x", required_argument, NULL, OPT_WINDOW_X},
|
|
||||||
{"window-y", required_argument, NULL, OPT_WINDOW_Y},
|
|
||||||
{"window-width", required_argument, NULL, OPT_WINDOW_WIDTH},
|
|
||||||
{"window-height", required_argument, NULL, OPT_WINDOW_HEIGHT},
|
|
||||||
{"window-borderless", no_argument, NULL,
|
|
||||||
OPT_WINDOW_BORDERLESS},
|
|
||||||
{"power-off-on-close", no_argument, NULL,
|
|
||||||
OPT_POWER_OFF_ON_CLOSE},
|
|
||||||
{NULL, 0, NULL, 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
struct scrcpy_options *opts = &args->opts;
|
struct scrcpy_options *opts = &args->opts;
|
||||||
|
|
||||||
optind = 0; // reset to start from the first argument in tests
|
optind = 0; // reset to start from the first argument in tests
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "b:fF:hKm:nNp:r:s:StvV:w",
|
while ((c = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) {
|
||||||
long_options, NULL)) != -1) {
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (!parse_bit_rate(optarg, &opts->bit_rate)) {
|
if (!parse_bit_rate(optarg, &opts->bit_rate)) {
|
||||||
@ -1216,3 +1381,19 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||||
|
struct sc_getopt_adapter adapter;
|
||||||
|
if (!sc_getopt_adapter_init(&adapter)) {
|
||||||
|
LOGW("Could not create getopt adapter");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = parse_args_with_getopt(args, argc, argv, adapter.optstring,
|
||||||
|
adapter.longopts);
|
||||||
|
|
||||||
|
sc_getopt_adapter_destroy(&adapter);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user