Compare commits

...

22 Commits

Author SHA1 Message Date
Romain Vimont
8da10eb4af WIP remove Meta+Shift+v 2020-05-30 01:48:58 +02:00
Romain Vimont
b330ba448b WIP Synchronize keyboard on Ctrl+v 2020-05-30 01:45:59 +02:00
Romain Vimont
a98b74df3d Add Meta+x shortcut for CUT
Add a scrcpy shortcut to send the CUT keycode.
2020-05-29 23:48:14 +02:00
Romain Vimont
567c5a7da7 Change "resize to fit" shortcut to Meta+w
Meta+x will be used for CUT (cut text to clipboard).
2020-05-29 23:47:29 +02:00
Romain Vimont
9badd2bdf0 Press COPY on "get clipboard" request if possible
If the device runs at least Android 7, just press COPY on the device
(the clipboard content will be sent via the clipboard listener).

<https://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_COPY>
2020-05-29 23:46:54 +02:00
Romain Vimont
31cee2c49f Reformulate seamless paste documentation 2020-05-29 23:46:04 +02:00
Romain Vimont
51d969b20c Simplify PASTE option for "set clipboard"
When the client requests to set the clipboard, it may request to press
the PASTE key in addition. To be a bit generic, it was stored as a flag
in ControlMessage.java.

But flags suggest that it represents a bitwise union, which could become
confusing when we add a COPY option for getting the clipboard.
2020-05-29 23:44:16 +02:00
Romain Vimont
149de2f788 Forward Shift to the device
This allows to select text using Shift+(arrow keys).

Fixes #942 <https://github.com/Genymobile/scrcpy/issues/942>
2020-05-29 23:44:16 +02:00
Romain Vimont
26cadf1022 Forward Ctrl to the device
Now that Meta is used for scrcpy shortcuts, Ctrl can be forwarded to the
device.

This allows to trigger Android shortcuts.

Fixes #555 <https://github.com/Genymobile/scrcpy/issues/555>
2020-05-29 23:44:16 +02:00
Romain Vimont
f650b0195f Replace Ctrl by Meta as shortcut modifier
This paves the way to forward Ctrl to the device.
2020-05-29 23:44:09 +02:00
Romain Vimont
e4efd75766 Avoid repetition for some shortcuts
Keeping the key pressed generate "repeat" events. It does not make sense
to repeat the event for rotation or turn screen off.
2020-05-29 22:02:41 +02:00
Romain Vimont
0e4a6f462b Mention stay awake limitation
The "stay awake" feature only works when the device is plugged in.

Refs #1445 <https://github.com/Genymobile/scrcpy/issues/1445>
2020-05-28 23:07:28 +02:00
Romain Vimont
8b73c90427 Mention how to turn the screen on in README
Now that Ctrl+Shift+o has been reactivated, mention it in the "turn
screen off" section.
2020-05-27 19:32:02 +02:00
Romain Vimont
ef91ab2841 Update links to v1.14 in README and BUILD 2020-05-27 19:31:12 +02:00
Romain Vimont
44fa4a090e Bump version to 1.14 2020-05-27 18:26:46 +02:00
Romain Vimont
dcde578a50 Reactivate "turn device screen on" feature
This reverts commit 8c8649cfcd.

I cannot reproduce the issue with Ctrl+Shift+o on any device, so in
practice it works, it's too bad to remove the feature for a random bug
on some Android versions on some devices.
2020-05-27 18:26:46 +02:00
Romain Vimont
93a5c5149d Push clipboard text only if not null
In practice, it does not change anything (it just avoids a spurious
wake-up), but semantically, it makes no sense to call
pushClipboardText() with a null value.
2020-05-27 18:26:39 +02:00
Romain Vimont
2ca8318b9d Improve manpage formatting
Add a new line to avoid unwanted text justification
2020-05-27 12:39:42 +02:00
Romain Vimont
d499ee53c9 Initialize a default log level
Clean up has been broken by 3df63c579d.

The verbosity was correctly initialized for the Server process, but not
for the CleanUp process.

To avoid the problem, initialize a default log level.
2020-05-27 12:05:29 +02:00
Romain Vimont
8f619f337b Upgrade platform-tools (30.0.0) for Windows
Include the latest version of adb in Windows releases.
2020-05-26 19:21:05 +02:00
Romain Vimont
fc1dec0270 Paste on "set clipboard" if possible
Ctrl+Shift+v synchronizes the computer clipboard to the Android device
clipboard. This feature had been added to provide a way to copy UTF-8
text from the computer to the device.

To make such a paste more straightforward, if the device runs at least
Android 7, also send a PASTE keycode to paste immediately.

<https://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_PASTE>

Fixes #786 <https://github.com/Genymobile/scrcpy/issues/786>
2020-05-25 20:59:21 +02:00
Romain Vimont
274b591d18 Fix union typo
The "set clipboard" event used the wrong union type to store its text.

In practice, it worked because both are at the same offset.
2020-05-25 18:41:05 +02:00
18 changed files with 276 additions and 168 deletions

View File

@@ -249,10 +249,10 @@ You can then [run](README.md#run) _scrcpy_.
## Prebuilt server
- [`scrcpy-server-v1.13`][direct-scrcpy-server]
_(SHA-256: 5fee64ca1ccdc2f38550f31f5353c66de3de30c2e929a964e30fa2d005d5f885)_
- [`scrcpy-server-v1.14`][direct-scrcpy-server]
_(SHA-256: 1d1b18a2b80e956771fd63b99b414d2d028713a8f12ddfa5a369709ad4295620)_
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.13/scrcpy-server-v1.13
[direct-scrcpy-server]: https://github.com/Genymobile/scrcpy/releases/download/v1.14/scrcpy-server-v1.14
Download the prebuilt server somewhere, and specify its path during the Meson
configuration:

View File

@@ -1,4 +1,4 @@
# scrcpy (v1.13)
# scrcpy (v1.14)
This application provides display and control of Android devices connected on
USB (or [over TCP/IP][article-tcpip]). It does not require any _root_ access.
@@ -69,10 +69,10 @@ hard).
For Windows, for simplicity, a prebuilt archive with all the dependencies
(including `adb`) is available:
- [`scrcpy-win64-v1.13.zip`][direct-win64]
_(SHA-256: 806aafc00d4db01513193addaa24f47858893ba5efe75770bfef6ae1ea987d27)_
- [`scrcpy-win64-v1.14.zip`][direct-win64]
_(SHA-256: 2be9139e46e29cf2f5f695848bb2b75a543b8f38be1133257dc5068252abc25f)_
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.13/scrcpy-win64-v1.13.zip
[direct-win64]: https://github.com/Genymobile/scrcpy/releases/download/v1.14/scrcpy-win64-v1.14.zip
It is also available in [Chocolatey]:
@@ -417,7 +417,7 @@ The secondary display may only be controlled if the device runs at least Android
#### Stay awake
To prevent the device to sleep after some delay:
To prevent the device to sleep after some delay when the device is plugged in:
```bash
scrcpy --stay-awake
@@ -439,7 +439,7 @@ scrcpy -S
Or by pressing `Ctrl`+`o` at any time.
To turn it back on, press `POWER` (or `Ctrl`+`p`).
To turn it back on, press `Ctrl`+`Shift`+`o` (or `POWER`, `Ctrl`+`p`).
It can be useful to also prevent the device to sleep:
@@ -494,7 +494,8 @@ It is possible to synchronize clipboards between the computer and the device, in
both directions:
- `Ctrl`+`c` copies the device clipboard to the computer clipboard;
- `Ctrl`+`Shift`+`v` copies the computer clipboard to the device clipboard;
- `Ctrl`+`Shift`+`v` copies the computer clipboard to the device clipboard (and
pastes if the device runs Android >= 7);
- `Ctrl`+`v` _pastes_ the computer clipboard as a sequence of text events (but
breaks non-ASCII characters).
@@ -559,32 +560,36 @@ Also see [issue #14].
## Shortcuts
| Action | Shortcut | Shortcut (macOS)
| -------------------------------------- |:----------------------------- |:-----------------------------
| Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f`
| Rotate display left | `Ctrl`+`←` _(left)_ | `Cmd`+`←` _(left)_
| Rotate display right | `Ctrl`+`→` _(right)_ | `Cmd`+`` _(right)_
| Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g`
| Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
| Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
| Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s`
| Click on `MENU` | `Ctrl`+`m` | `Ctrl`+`m`
| Click on `VOLUME_UP` | `Ctrl`+`↑` _(up)_ | `Cmd`+`` _(up)_
| Click on `VOLUME_DOWN` | `Ctrl`+`↓` _(down)_ | `Cmd`+`` _(down)_
| Click on `POWER` | `Ctrl`+`p` | `Cmd`+`p`
| Power on | _Right-click²_ | _Right-click²_
| Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o`
| Rotate device screen | `Ctrl`+`r` | `Cmd`+`r`
| Expand notification panel | `Ctrl`+`n` | `Cmd`+`n`
| Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
| Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c`
| Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v`
| Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
| Enable/disable FPS counter (on stdout) | `Ctrl`+`i` | `Cmd`+`i`
_`Meta` is typically the `Windows` key on the keyboard, or `Cmd` on macOS._
| Action | Shortcut
| ---------------------------------------------------- |:-----------------------------
| Switch fullscreen mode | `Meta`+`f`
| Rotate display left | `Meta`+`` _(left)_
| Rotate display right | `Meta`+`` _(right)_
| Resize window to 1:1 (pixel-perfect) | `Meta`+`g`
| Resize window to remove black borders | `Meta`+`w` \| _Double-click¹_
| Click on `HOME` | `Meta`+`h` \| _Middle-click_
| Click on `BACK` | `Meta`+`b` \| _Right-click²_
| Click on `APP_SWITCH` | `Meta`+`s`
| Click on `MENU` | `Meta`+`m`
| Click on `VOLUME_UP` | `Meta`+`↑` _(up)_
| Click on `VOLUME_DOWN` | `Meta`+`↓` _(down)_
| Click on `POWER` | `Meta`+`p`
| Power on | _Right-click²_
| Turn device screen off (keep mirroring) | `Meta`+`o`
| Turn device screen on | `Meta`+`Shift`+`o`
| Rotate device screen | `Meta`+`r`
| Expand notification panel | `Meta`+`n`
| Collapse notification panel | `Meta`+`Shift`+`n`
| Press COPY³, then Copy device clipboard to computer | `Meta`+`c`
| Press CUT³ | `Meta`+`x`
| Paste computer clipboard to device | `Meta`+`v`
| Enable/disable FPS counter (on stdout) | `Meta`+`i`
_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._
_²Right-click turns the screen on if it was off, presses BACK otherwise._
_³Only if the device runs Android >= 7._
## Custom paths

View File

@@ -129,6 +129,7 @@ Force recording format (either mp4 or mkv).
Request SDL to use the given render driver (this is just a hint).
Supported names are currently "direct3d", "opengl", "opengles2", "opengles", "metal" and "software".
.UR https://wiki.libsdl.org/SDL_HINT_RENDER_DRIVER
.UE
@@ -166,7 +167,7 @@ Default is "info" for release builds, "debug" for debug builds.
.TP
.B \-w, \-\-stay-awake
Keep the device on while scrcpy is running.
Keep the device on while scrcpy is running, when the device is plugged in.
.TP
.B \-\-window\-borderless
@@ -203,51 +204,51 @@ Default is 0 (automatic).\n
.SH SHORTCUTS
.TP
.B Ctrl+f
.B Meta+f
Switch fullscreen mode
.TP
.B Ctrl+Left
.B Meta+Left
Rotate display left
.TP
.B Ctrl+Right
.B Meta+Right
Rotate display right
.TP
.B Ctrl+g
.B Meta+g
Resize window to 1:1 (pixel\-perfect)
.TP
.B Ctrl+x, Double\-click on black borders
.B Meta+w, Double\-click on black borders
Resize window to remove black borders
.TP
.B Ctrl+h, Home, Middle\-click
.B Meta+h, Home, Middle\-click
Click on HOME
.TP
.B Ctrl+b, Ctrl+Backspace, Right\-click (when screen is on)
.B Meta+b, Meta+Backspace, Right\-click (when screen is on)
Click on BACK
.TP
.B Ctrl+s
.B Meta+s
Click on APP_SWITCH
.TP
.B Ctrl+m
.B Meta+m
Click on MENU
.TP
.B Ctrl+Up
.B Meta+Up
Click on VOLUME_UP
.TP
.B Ctrl+Down
.B Meta+Down
Click on VOLUME_DOWN
.TP
.B Ctrl+p
.B Meta+p
Click on POWER (turn screen on/off)
.TP
@@ -255,35 +256,39 @@ Click on POWER (turn screen on/off)
Turn screen on
.TP
.B Ctrl+o
.B Meta+o
Turn device screen off (keep mirroring)
.TP
.B Ctrl+r
.B Meta+Shift+o
Turn device screen on
.TP
.B Meta+r
Rotate device screen
.TP
.B Ctrl+n
.B Meta+n
Expand notification panel
.TP
.B Ctrl+Shift+n
.B Meta+Shift+n
Collapse notification panel
.TP
.B Ctrl+c
Copy device clipboard to computer
.B Meta+c
Press COPY (Android >= 7), then copy device clipboard to computer
.TP
.B Ctrl+v
.B Meta+x
Press CUT (Android >= 7)
.TP
.B Meta+v
Paste computer clipboard to device
.TP
.B Ctrl+Shift+v
Copy computer clipboard to device
.TP
.B Ctrl+i
.B Meta+i
Enable/disable FPS counter (print frames/second in logs)
.TP

View File

@@ -13,9 +13,9 @@
void
scrcpy_print_usage(const char *arg0) {
#ifdef __APPLE__
# define CTRL_OR_CMD "Cmd"
# define MOD "Cmd"
#else
# define CTRL_OR_CMD "Ctrl"
# define MOD "Meta"
#endif
fprintf(stderr,
"Usage: %s [options]\n"
@@ -159,7 +159,8 @@ scrcpy_print_usage(const char *arg0) {
#endif
"\n"
" -w, --stay-awake\n"
" Keep the device on while scrcpy is running.\n"
" Keep the device on while scrcpy is running, when the device\n"
" is plugged in.\n"
"\n"
" --window-borderless\n"
" Disable window decorations (display borderless window).\n"
@@ -185,19 +186,19 @@ scrcpy_print_usage(const char *arg0) {
"\n"
"Shortcuts:\n"
"\n"
" " CTRL_OR_CMD "+f\n"
" " MOD "+f\n"
" Switch fullscreen mode\n"
"\n"
" " CTRL_OR_CMD "+Left\n"
" " MOD "+Left\n"
" Rotate display left\n"
"\n"
" " CTRL_OR_CMD "+Right\n"
" " MOD "+Right\n"
" Rotate display right\n"
"\n"
" " CTRL_OR_CMD "+g\n"
" " MOD "+g\n"
" Resize window to 1:1 (pixel-perfect)\n"
"\n"
" " CTRL_OR_CMD "+x\n"
" " MOD "+w\n"
" Double-click on black borders\n"
" Resize window to remove black borders\n"
"\n"
@@ -205,51 +206,55 @@ scrcpy_print_usage(const char *arg0) {
" Middle-click\n"
" Click on HOME\n"
"\n"
" " CTRL_OR_CMD "+b\n"
" " CTRL_OR_CMD "+Backspace\n"
" " MOD "+b\n"
" " MOD "+Backspace\n"
" Right-click (when screen is on)\n"
" Click on BACK\n"
"\n"
" " CTRL_OR_CMD "+s\n"
" " MOD "+s\n"
" Click on APP_SWITCH\n"
"\n"
" Ctrl+m\n"
" Click on MENU\n"
"\n"
" " CTRL_OR_CMD "+Up\n"
" " MOD "+Up\n"
" Click on VOLUME_UP\n"
"\n"
" " CTRL_OR_CMD "+Down\n"
" " MOD "+Down\n"
" Click on VOLUME_DOWN\n"
"\n"
" " CTRL_OR_CMD "+p\n"
" " MOD "+p\n"
" Click on POWER (turn screen on/off)\n"
"\n"
" Right-click (when screen is off)\n"
" Power on\n"
"\n"
" " CTRL_OR_CMD "+o\n"
" " MOD "+o\n"
" Turn device screen off (keep mirroring)\n"
"\n"
" " CTRL_OR_CMD "+r\n"
" " MOD "+Shift+o\n"
" Turn device screen on\n"
"\n"
" " MOD "+r\n"
" Rotate device screen\n"
"\n"
" " CTRL_OR_CMD "+n\n"
" " MOD "+n\n"
" Expand notification panel\n"
"\n"
" " CTRL_OR_CMD "+Shift+n\n"
" " MOD "+Shift+n\n"
" Collapse notification panel\n"
"\n"
" " CTRL_OR_CMD "+c\n"
" Copy device clipboard to computer\n"
" " MOD "+c\n"
" Press COPY (Android >= 7), then copy device clipboard to\n"
" computer\n"
"\n"
" " CTRL_OR_CMD "+v\n"
" " MOD "+x\n"
" Press CUT (Android >= 7)\n"
"\n"
" " MOD "+v\n"
" Paste computer clipboard to device\n"
"\n"
" " CTRL_OR_CMD "+Shift+v\n"
" Copy computer clipboard to device\n"
"\n"
" " CTRL_OR_CMD "+i\n"
" " MOD "+i\n"
" Enable/disable FPS counter (print frames/second in logs)\n"
"\n"
" Drag & drop APK file\n"
@@ -260,6 +265,7 @@ scrcpy_print_usage(const char *arg0) {
DEFAULT_LOCK_VIDEO_ORIENTATION, DEFAULT_LOCK_VIDEO_ORIENTATION >= 0 ? "" : " (unlocked)",
DEFAULT_MAX_SIZE, DEFAULT_MAX_SIZE ? "" : " (unlimited)",
DEFAULT_LOCAL_PORT_RANGE_FIRST, DEFAULT_LOCAL_PORT_RANGE_LAST);
#undef MOD
}
static bool

View File

@@ -66,11 +66,15 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
buffer_write32be(&buf[17],
(uint32_t) msg->inject_scroll_event.vscroll);
return 21;
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
buf[1] = msg->get_clipboard.copy;
return 2;
case CONTROL_MSG_TYPE_SET_CLIPBOARD: {
size_t len = write_string(msg->inject_text.text,
buf[1] = !!msg->set_clipboard.paste;
size_t len = write_string(msg->set_clipboard.text,
CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH,
&buf[1]);
return 1 + len;
&buf[2]);
return 2 + len;
}
case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE:
buf[1] = msg->set_screen_power_mode.mode;
@@ -78,7 +82,6 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON:
case CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_COLLAPSE_NOTIFICATION_PANEL:
case CONTROL_MSG_TYPE_GET_CLIPBOARD:
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
// no additional data
return 1;

View File

@@ -11,9 +11,9 @@
#include "common.h"
#define CONTROL_MSG_INJECT_TEXT_MAX_LENGTH 300
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH 4093
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH 4092
#define CONTROL_MSG_SERIALIZED_MAX_SIZE \
(3 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
(4 + CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH)
#define POINTER_ID_MOUSE UINT64_C(-1);
@@ -60,8 +60,12 @@ struct control_msg {
int32_t hscroll;
int32_t vscroll;
} inject_scroll_event;
struct {
bool copy;
} get_clipboard;
struct {
char *text; // owned, to be freed by SDL_free()
bool paste;
} set_clipboard;
struct {
enum screen_power_mode mode;

View File

@@ -92,6 +92,10 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT);
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
MAP(SDLK_UP, AKEYCODE_DPAD_UP);
MAP(SDLK_LCTRL, AKEYCODE_CTRL_LEFT);
MAP(SDLK_RCTRL, AKEYCODE_CTRL_RIGHT);
MAP(SDLK_LSHIFT, AKEYCODE_SHIFT_LEFT);
MAP(SDLK_RSHIFT, AKEYCODE_SHIFT_RIGHT);
}
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {

View File

@@ -70,6 +70,11 @@ action_menu(struct controller *controller, int actions) {
send_keycode(controller, AKEYCODE_MENU, actions, "MENU");
}
static inline void
action_cut(struct controller *controller, int actions) {
send_keycode(controller, AKEYCODE_CUT, actions, "CUT");
}
// turn the screen on if it was off, press BACK otherwise
static void
press_back_or_turn_screen_on(struct controller *controller) {
@@ -102,9 +107,10 @@ collapse_notification_panel(struct controller *controller) {
}
static void
request_device_clipboard(struct controller *controller) {
request_device_clipboard(struct controller *controller, bool copy) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
msg.get_clipboard.copy = copy;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request device clipboard");
@@ -112,7 +118,7 @@ request_device_clipboard(struct controller *controller) {
}
static void
set_device_clipboard(struct controller *controller) {
set_device_clipboard(struct controller *controller, bool paste) {
char *text = SDL_GetClipboardText();
if (!text) {
LOGW("Could not get clipboard text: %s", SDL_GetError());
@@ -127,6 +133,7 @@ set_device_clipboard(struct controller *controller) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_SET_CLIPBOARD;
msg.set_clipboard.text = text;
msg.set_clipboard.paste = paste;
if (!controller_push_msg(controller, &msg)) {
SDL_free(text);
@@ -261,131 +268,128 @@ input_manager_process_key(struct input_manager *im,
bool ctrl = event->keysym.mod & (KMOD_LCTRL | KMOD_RCTRL);
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
// use Cmd on macOS, Ctrl on other platforms
#ifdef __APPLE__
bool cmd = !ctrl && meta;
#else
if (meta) {
// no shortcuts involve Meta on platforms other than macOS, and it must
// not be forwarded to the device
return;
}
bool cmd = ctrl; // && !meta, already guaranteed
#endif
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
if (alt) {
// no shortcuts involve Alt, and it must not be forwarded to the device
// No shortcuts involve Alt, and it is not forwarded to the device
return;
}
struct controller *controller = im->controller;
// capture all Ctrl events
if (ctrl || cmd) {
SDL_Keycode keycode = event->keysym.sym;
bool down = event->type == SDL_KEYDOWN;
SDL_Keycode keycode = event->keysym.sym;
bool down = event->type == SDL_KEYDOWN;
// Capture all Meta events
if (meta) {
if (ctrl) {
// No shortcuts involve Ctrl+Meta
return;
}
int action = down ? ACTION_DOWN : ACTION_UP;
bool repeat = event->repeat;
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
switch (keycode) {
case SDLK_h:
// Ctrl+h on all platform, since Cmd+h is already captured by
// the system on macOS to hide the window
if (control && ctrl && !meta && !shift && !repeat) {
if (control && !shift && !repeat) {
action_home(controller, action);
}
return;
case SDLK_b: // fall-through
case SDLK_BACKSPACE:
if (control && cmd && !shift && !repeat) {
if (control && !shift && !repeat) {
action_back(controller, action);
}
return;
case SDLK_s:
if (control && cmd && !shift && !repeat) {
if (control && !shift && !repeat) {
action_app_switch(controller, action);
}
return;
case SDLK_m:
// Ctrl+m on all platform, since Cmd+m is already captured by
// the system on macOS to minimize the window
if (control && ctrl && !meta && !shift && !repeat) {
if (control && !shift && !repeat) {
action_menu(controller, action);
}
return;
case SDLK_p:
if (control && cmd && !shift && !repeat) {
if (control && !shift && !repeat) {
action_power(controller, action);
}
return;
case SDLK_o:
if (control && cmd && !shift && down) {
set_screen_power_mode(controller, SCREEN_POWER_MODE_OFF);
if (control && !repeat && down) {
enum screen_power_mode mode = shift
? SCREEN_POWER_MODE_NORMAL
: SCREEN_POWER_MODE_OFF;
set_screen_power_mode(controller, mode);
}
return;
case SDLK_DOWN:
if (control && cmd && !shift) {
if (control && !shift) {
// forward repeated events
action_volume_down(controller, action);
}
return;
case SDLK_UP:
if (control && cmd && !shift) {
if (control && !shift) {
// forward repeated events
action_volume_up(controller, action);
}
return;
case SDLK_LEFT:
if (cmd && !shift && down) {
if (!shift && !repeat && down) {
rotate_client_left(im->screen);
}
return;
case SDLK_RIGHT:
if (cmd && !shift && down) {
if (!shift && !repeat && down) {
rotate_client_right(im->screen);
}
return;
case SDLK_c:
if (control && cmd && !shift && !repeat && down) {
request_device_clipboard(controller);
}
return;
case SDLK_v:
if (control && cmd && !repeat && down) {
if (shift) {
// store the text in the device clipboard
set_device_clipboard(controller);
} else {
// inject the text as input events
clipboard_paste(controller);
}
}
return;
case SDLK_f:
if (!shift && cmd && !repeat && down) {
screen_switch_fullscreen(im->screen);
if (control && !shift && !repeat && down) {
// Press COPY, then get the clipboard content
request_device_clipboard(controller, true);
}
return;
case SDLK_x:
if (!shift && cmd && !repeat && down) {
if (control && !shift && !repeat && down) {
// For convenience (especially on macOS), bind Meta+x to
// CUT (even if it is already accessible by pressing Ctrl+x
// on the device)
action_cut(controller, action);
}
return;
case SDLK_v:
if (control && !shift && !repeat && down) {
// Inject the text as input events
clipboard_paste(controller);
}
return;
case SDLK_f:
if (!shift && !repeat && down) {
screen_switch_fullscreen(im->screen);
}
return;
case SDLK_w:
if (!shift && !repeat && down) {
screen_resize_to_fit(im->screen);
}
return;
case SDLK_g:
if (!shift && cmd && !repeat && down) {
if (!shift && !repeat && down) {
screen_resize_to_pixel_perfect(im->screen);
}
return;
case SDLK_i:
if (!shift && cmd && !repeat && down) {
if (!shift && !repeat && down) {
struct fps_counter *fps_counter =
im->video_buffer->fps_counter;
switch_fps_counter_state(fps_counter);
}
return;
case SDLK_n:
if (control && cmd && !repeat && down) {
if (control && !repeat && down) {
if (shift) {
collapse_notification_panel(controller);
} else {
@@ -394,7 +398,7 @@ input_manager_process_key(struct input_manager *im,
}
return;
case SDLK_r:
if (control && cmd && !shift && !repeat && down) {
if (control && !shift && !repeat && down) {
rotate_device(controller);
}
return;
@@ -407,6 +411,14 @@ input_manager_process_key(struct input_manager *im,
return;
}
assert(!meta);
if (ctrl && !shift && keycode == SDLK_v && down) {
// Synchronize the computer clipboard to the device clipboard before
// sending Ctrl+v
set_device_clipboard(controller, false);
}
struct control_msg msg;
if (convert_input_key(event, &msg, im->prefer_text)) {
if (!controller_push_msg(controller, &msg)) {

View File

@@ -185,14 +185,18 @@ static void test_serialize_collapse_notification_panel(void) {
static void test_serialize_get_clipboard(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_GET_CLIPBOARD,
.get_clipboard = {
.copy = true,
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 1);
assert(size == 2);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_GET_CLIPBOARD,
1, // copy
};
assert(!memcmp(buf, expected, sizeof(expected)));
}
@@ -200,17 +204,19 @@ static void test_serialize_get_clipboard(void) {
static void test_serialize_set_clipboard(void) {
struct control_msg msg = {
.type = CONTROL_MSG_TYPE_SET_CLIPBOARD,
.inject_text = {
.set_clipboard = {
.paste = true,
.text = "hello, world!",
},
};
unsigned char buf[CONTROL_MSG_SERIALIZED_MAX_SIZE];
int size = control_msg_serialize(&msg, buf);
assert(size == 16);
assert(size == 17);
const unsigned char expected[] = {
CONTROL_MSG_TYPE_SET_CLIPBOARD,
1, // paste
0x00, 0x0d, // text length
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', // text
};

View File

@@ -1,5 +1,5 @@
project('scrcpy', 'c',
version: '1.13',
version: '1.14',
meson_version: '>= 0.48',
default_options: [
'c_std=c11',

View File

@@ -35,6 +35,6 @@ prepare-sdl2:
SDL2-2.0.12
prepare-adb:
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r29.0.5-windows.zip \
2df06160056ec9a84c7334af2a1e42740befbb1a2e34370e7af544a2cc78152c \
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.0-windows.zip \
854305f9a702f5ea2c3de73edde402bd26afa0ee944c9b0c4380420f5a862e0d \
platform-tools

View File

@@ -6,8 +6,8 @@ android {
applicationId "com.genymobile.scrcpy"
minSdkVersion 21
targetSdkVersion 29
versionCode 15
versionName "1.13"
versionCode 16
versionName "1.14"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {

View File

@@ -12,7 +12,7 @@
set -e
SCRCPY_DEBUG=false
SCRCPY_VERSION_NAME=1.13
SCRCPY_VERSION_NAME=1.14
PLATFORM=${ANDROID_PLATFORM:-29}
BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-29.0.2}

View File

@@ -28,6 +28,7 @@ public final class ControlMessage {
private Position position;
private int hScroll;
private int vScroll;
private boolean pressCopyOrPaste;
private ControlMessage() {
}
@@ -68,10 +69,18 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createSetClipboard(String text) {
public static ControlMessage createGetClipboard(boolean copy) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_GET_CLIPBOARD;
msg.pressCopyOrPaste = copy;
return msg;
}
public static ControlMessage createSetClipboard(String text, boolean paste) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_SET_CLIPBOARD;
msg.text = text;
msg.pressCopyOrPaste = paste;
return msg;
}
@@ -134,4 +143,8 @@ public final class ControlMessage {
public int getVScroll() {
return vScroll;
}
public boolean getPressCopyOrPaste() {
return pressCopyOrPaste;
}
}

View File

@@ -12,8 +12,10 @@ public class ControlMessageReader {
static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27;
static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20;
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
static final int GET_CLIPBOARD_PAYLOAD_LENGTH = 1;
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093;
public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4092; // 4096 - 1 (type) - 1 (parse flag) - 2 (length)
public static final int INJECT_TEXT_MAX_LENGTH = 300;
private static final int RAW_BUFFER_SIZE = 4096;
@@ -66,6 +68,9 @@ public class ControlMessageReader {
case ControlMessage.TYPE_INJECT_SCROLL_EVENT:
msg = parseInjectScrollEvent();
break;
case ControlMessage.TYPE_GET_CLIPBOARD:
msg = parseGetClipboard();
break;
case ControlMessage.TYPE_SET_CLIPBOARD:
msg = parseSetClipboard();
break;
@@ -75,7 +80,6 @@ public class ControlMessageReader {
case ControlMessage.TYPE_BACK_OR_SCREEN_ON:
case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL:
case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL:
case ControlMessage.TYPE_GET_CLIPBOARD:
case ControlMessage.TYPE_ROTATE_DEVICE:
msg = ControlMessage.createEmpty(type);
break;
@@ -147,12 +151,24 @@ public class ControlMessageReader {
return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll);
}
private ControlMessage parseGetClipboard() {
if (buffer.remaining() < GET_CLIPBOARD_PAYLOAD_LENGTH) {
return null;
}
boolean copy = buffer.get() != 0;
return ControlMessage.createGetClipboard(copy);
}
private ControlMessage parseSetClipboard() {
if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) {
return null;
}
boolean parse = buffer.get() != 0;
String text = parseString();
if (text == null) {
return null;
}
return ControlMessage.createSetClipboard(text);
return ControlMessage.createSetClipboard(text, parse);
}
private ControlMessage parseSetScreenPowerMode() {

View File

@@ -1,5 +1,6 @@
package com.genymobile.scrcpy;
import android.os.Build;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -103,14 +104,10 @@ public class Controller {
device.collapsePanels();
break;
case ControlMessage.TYPE_GET_CLIPBOARD:
String clipboardText = device.getClipboardText();
sender.pushClipboardText(clipboardText);
getClipboard(msg.getPressCopyOrPaste());
break;
case ControlMessage.TYPE_SET_CLIPBOARD:
boolean setClipboardOk = device.setClipboardText(msg.getText());
if (setClipboardOk) {
Ln.i("Device clipboard set");
}
setClipboard(msg.getText(), msg.getPressCopyOrPaste());
break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
if (device.supportsInputEvents()) {
@@ -227,4 +224,35 @@ public class Controller {
int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER;
return device.injectKeycode(keycode);
}
private boolean getClipboard(boolean copy) {
// On Android >= 7, also press the COPY key if requested
if (copy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
// If there is something to copy, the clipboard will be automatically sent to the computer clipboard via the ClipboardListener
return device.injectKeycode(KeyEvent.KEYCODE_COPY);
}
// We can't press COPY, so only synchronize the current clipboard
String clipboardText = device.getClipboardText();
if (clipboardText != null) {
sender.pushClipboardText(clipboardText);
return true;
}
return false;
}
private boolean setClipboard(String text, boolean paste) {
boolean ok = device.setClipboardText(text);
if (ok) {
Ln.i("Device clipboard set");
}
// On Android >= 7, also press the PASTE key if requested
if (paste && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && device.supportsInputEvents()) {
device.injectKeycode(KeyEvent.KEYCODE_PASTE);
}
return ok;
}
}

View File

@@ -15,7 +15,7 @@ public final class Ln {
DEBUG, INFO, WARN, ERROR
}
private static Level threshold;
private static Level threshold = Level.INFO;
private Ln() {
// not instantiable

View File

@@ -200,6 +200,7 @@ public class ControlMessageReaderTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_GET_CLIPBOARD);
dos.writeByte(1); // copy
byte[] packet = bos.toByteArray();
@@ -207,6 +208,7 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.next();
Assert.assertEquals(ControlMessage.TYPE_GET_CLIPBOARD, event.getType());
Assert.assertTrue(event.getPressCopyOrPaste());
}
@Test
@@ -216,6 +218,7 @@ public class ControlMessageReaderTest {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
dos.writeByte(1); // paste
byte[] text = "testé".getBytes(StandardCharsets.UTF_8);
dos.writeShort(text.length);
dos.write(text);
@@ -227,6 +230,7 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals("testé", event.getText());
Assert.assertTrue(event.getPressCopyOrPaste());
}
@Test
@@ -238,6 +242,7 @@ public class ControlMessageReaderTest {
dos.writeByte(ControlMessage.TYPE_SET_CLIPBOARD);
byte[] rawText = new byte[ControlMessageReader.CLIPBOARD_TEXT_MAX_LENGTH];
dos.writeByte(1); // paste
Arrays.fill(rawText, (byte) 'a');
String text = new String(rawText, 0, rawText.length);
@@ -251,6 +256,7 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals(text, event.getText());
Assert.assertTrue(event.getPressCopyOrPaste());
}
@Test