Compare commits

..

3 Commits

Author SHA1 Message Date
d95a52f277 Forward Alt to the device
There is no reason not to forward it.
2020-05-29 01:08:03 +02:00
ad78ca9cfb Forward Shift to the device
This allows to select text with Shift + arrow keys.

Fixes #942 <https://github.com/Genymobile/scrcpy/issues/942>.
2020-05-29 01:07:42 +02:00
3bdc45925f Forward Right-Ctrl to the device
Only capture Left-Ctrl for scrcpy shortcuts, so that Right-Ctrl can be
forwarded to the device.

Fixes #555 <https://github.com/Genymobile/scrcpy/issues/555>
2020-05-29 01:07:25 +02:00
12 changed files with 184 additions and 183 deletions

3
.gitignore vendored
View File

@ -1,8 +1,5 @@
build/ build/
/dist/ /dist/
/build-*/
/build_*/
/release-*/
.idea/ .idea/
.gradle/ .gradle/
/x/ /x/

View File

@ -354,7 +354,7 @@ scrcpy --fullscreen
scrcpy -f # short version scrcpy -f # short version
``` ```
Fullscreen can then be toggled dynamically with `RCtrl`+`f`. Fullscreen can then be toggled dynamically with `Ctrl`+`f`.
#### Rotation #### Rotation
@ -370,18 +370,18 @@ Possibles values are:
- `2`: 180 degrees - `2`: 180 degrees
- `3`: 90 degrees clockwise - `3`: 90 degrees clockwise
The rotation can also be changed dynamically with `RCtrl`+`←` _(left)_ and The rotation can also be changed dynamically with `Ctrl`+`←` _(left)_ and
`RCtrl`+`→` _(right)_. `Ctrl`+`→` _(right)_.
Note that _scrcpy_ manages 3 different rotations: Note that _scrcpy_ manages 3 different rotations:
- `RCtrl`+`r` requests the device to switch between portrait and landscape (the - `Ctrl`+`r` requests the device to switch between portrait and landscape (the
current running app may refuse, if it does support the requested current running app may refuse, if it does support the requested
orientation). orientation).
- `--lock-video-orientation` changes the mirroring orientation (the orientation - `--lock-video-orientation` changes the mirroring orientation (the orientation
of the video sent from the device to the computer). This affects the of the video sent from the device to the computer). This affects the
recording. recording.
- `--rotation` (or `RCtrl`+`←`/`RCtrl`+`→`) rotates only the window content. - `--rotation` (or `Ctrl`+`←`/`Ctrl`+`→`) rotates only the window content. This
This affects only the display, not the recording. affects only the display, not the recording.
### Other mirroring options ### Other mirroring options
@ -437,9 +437,9 @@ scrcpy --turn-screen-off
scrcpy -S scrcpy -S
``` ```
Or by pressing `RCtrl`+`o` at any time. Or by pressing `Ctrl`+`o` at any time.
To turn it back on, press `RCtrl`+`Shift`+`o` (or `POWER`, `RCtrl`+`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: It can be useful to also prevent the device to sleep:
@ -483,22 +483,24 @@ Note that it only shows _physical_ touches (with the finger on the device).
#### Rotate device screen #### Rotate device screen
Press `RCtrl`+`r` to switch between portrait and landscape modes. Press `Ctrl`+`r` to switch between portrait and landscape modes.
Note that it rotates only if the application in foreground supports the Note that it rotates only if the application in foreground supports the
requested orientation. requested orientation.
#### Copy-paste #### Copy-paste
Any time the Android clipboard changes, it is automatically synchronized to the It is possible to synchronize clipboards between the computer and the device, in
computer clipboard. both directions:
`Ctrl`+`c` (copy), `Ctrl`+`x` (cut) and `LCtrl`+`v` (paste) work as you expect. - `Ctrl`+`c` copies the device clipboard to the computer clipboard;
- `Ctrl`+`Shift`+`v` copies the computer clipboard to the device clipboard (and
In addition, `RCtrl`+`v` allows to inject the computer clipboard content as a pastes if the device runs Android >= 7);
sequence of text event. Even if it can break non-ASCII content, this is - `Ctrl`+`v` _pastes_ the computer clipboard as a sequence of text events (but
sometimes necessary when pasting directly is not possible. breaks non-ASCII characters).
Moreover, any time the Android clipboard changes, it is automatically
synchronized to the computer clipboard.
#### Text injection preference #### Text injection preference
@ -558,34 +560,33 @@ Also see [issue #14].
## Shortcuts ## Shortcuts
`RCtrl` is the right `Ctrl` key (the left `Ctrl` key is forwarded to the `LCtrl` is the left `Ctrl` key (the right `Ctrl` key is forwarded to the
device). device).
On macOS, `Cmd` also works (for shortcuts which are not already captured by the | Action | Shortcut | Shortcut (macOS)
system). | ------------------------------------------- |:----------------------------- |:-----------------------------
| Switch fullscreen mode | `LCtrl`+`f` | `Cmd`+`f`
| Action | Shortcut | Rotate display left | `LCtrl`+`←` _(left)_ | `Cmd`+`←` _(left)_
| ------------------------------------------- |:----------------------------- | Rotate display right | `LCtrl`+`→` _(right)_ | `Cmd`+`→` _(right)_
| Switch fullscreen mode | `RCtrl`+`f` | Resize window to 1:1 (pixel-perfect) | `LCtrl`+`g` | `Cmd`+`g`
| Rotate display left | `RCtrl`+`` | Resize window to remove black borders | `LCtrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_
| Rotate display right | `RCtrl`+`` | Click on `HOME` | `LCtrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_
| Resize window to 1:1 (pixel-perfect) | `RCtrl`+`g` | Click on `BACK` | `LCtrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_
| Resize window to remove black borders | `RCtrl`+`w` \| _Double-click¹_ | Click on `APP_SWITCH` | `LCtrl`+`s` | `Cmd`+`s`
| Click on `HOME` | `RCtrl`+`h` \| _Middle-click_ | Click on `MENU` | `LCtrl`+`m` | `Ctrl`+`m`
| Click on `BACK` | `RCtrl`+`b` \| _Right-click²_ | Click on `VOLUME_UP` | `LCtrl`+`` _(up)_ | `Cmd`+`↑` _(up)_
| Click on `APP_SWITCH` | `RCtrl`+`s` | Click on `VOLUME_DOWN` | `LCtrl`+`` _(down)_ | `Cmd`+`↓` _(down)_
| Click on `MENU` | `RCtrl`+`m` | Click on `POWER` | `LCtrl`+`p` | `Cmd`+`p`
| Click on `VOLUME_UP` | `RCtrl`+`↑` _(up)_ | Power on | _Right-click²_ | _Right-click²_
| Click on `VOLUME_DOWN` | `RCtrl`+`↓` _(down)_ | Turn device screen off (keep mirroring) | `LCtrl`+`o` | `Cmd`+`o`
| Click on `POWER` | `RCtrl`+`p` | Turn device screen on | `LCtrl`+`Shift`+`o` | `Cmd`+`Shift`+`o`
| Power on | _Right-click²_ | Rotate device screen | `LCtrl`+`r` | `Cmd`+`r`
| Turn device screen off (keep mirroring) | `RCtrl`+`o` | Expand notification panel | `LCtrl`+`n` | `Cmd`+`n`
| Turn device screen on | `RCtrl`+`Shift`+`o` | Collapse notification panel | `LCtrl`+`Shift`+`n` | `Cmd`+`Shift`+`n`
| Rotate device screen | `RCtrl`+`r` | Copy device clipboard to computer | `LCtrl`+`c` | `Cmd`+`c`
| Expand notification panel | `RCtrl`+`n` | Paste computer clipboard to device | `LCtrl`+`v` | `Cmd`+`v`
| Collapse notification panel | `RCtrl`+`Shift`+`n` | Copy computer clipboard to device and paste | `LCtrl`+`Shift`+`v` | `Cmd`+`Shift`+`v`
| Inject computer clipboard text | `RCtrl`+`v` | Enable/disable FPS counter (on stdout) | `LCtrl`+`i` | `Cmd`+`i`
| Enable/disable FPS counter (on stdout) | `RCtrl`+`i`
_¹Double-click on black borders to remove them._ _¹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._

View File

@ -203,54 +203,54 @@ Default is 0 (automatic).\n
.SH SHORTCUTS .SH SHORTCUTS
RCtrl is the right Ctrl key (the left Ctrl key is forwarded to the device). \fBLCtrl\fR is the left Ctrl key (the right Ctrl key is forwarded to the device).
.TP .TP
.B RCtrl+f .B LCtrl+f
Switch fullscreen mode Switch fullscreen mode
.TP .TP
.B RCtrl+Left .B LCtrl+Left
Rotate display left Rotate display left
.TP .TP
.B RCtrl+Right .B LCtrl+Right
Rotate display right Rotate display right
.TP .TP
.B RCtrl+g .B LCtrl+g
Resize window to 1:1 (pixel\-perfect) Resize window to 1:1 (pixel\-perfect)
.TP .TP
.B RCtrl+w, Double\-click on black borders .B LCtrl+x, Double\-click on black borders
Resize window to remove black borders Resize window to remove black borders
.TP .TP
.B RCtrl+h, Home, Middle\-click .B LCtrl+h, Home, Middle\-click
Click on HOME Click on HOME
.TP .TP
.B RCtrl+b, RCtrl+Backspace, Right\-click (when screen is on) .B LCtrl+b, Ctrl+Backspace, Right\-click (when screen is on)
Click on BACK Click on BACK
.TP .TP
.B RCtrl+s .B LCtrl+s
Click on APP_SWITCH Click on APP_SWITCH
.TP .TP
.B RCtrl+m .B LCtrl+m
Click on MENU Click on MENU
.TP .TP
.B RCtrl+Up .B LCtrl+Up
Click on VOLUME_UP Click on VOLUME_UP
.TP .TP
.B RCtrl+Down .B LCtrl+Down
Click on VOLUME_DOWN Click on VOLUME_DOWN
.TP .TP
.B RCtrl+p .B LCtrl+p
Click on POWER (turn screen on/off) Click on POWER (turn screen on/off)
.TP .TP
@ -258,31 +258,39 @@ Click on POWER (turn screen on/off)
Turn screen on Turn screen on
.TP .TP
.B RCtrl+o .B LCtrl+o
Turn device screen off (keep mirroring) Turn device screen off (keep mirroring)
.TP .TP
.B RCtrl+Shift+o .B LCtrl+Shift+o
Turn device screen on Turn device screen on
.TP .TP
.B RCtrl+r .B LCtrl+r
Rotate device screen Rotate device screen
.TP .TP
.B RCtrl+n .B LCtrl+n
Expand notification panel Expand notification panel
.TP .TP
.B RCtrl+Shift+n .B LCtrl+Shift+n
Collapse notification panel Collapse notification panel
.TP .TP
.B RCtrl+v .B LCtrl+c
Inject computer clipboard text Copy device clipboard to computer
.TP .TP
.B RCtrl+i .B LCtrl+v
Paste computer clipboard to device
.TP
.B LCtrl+Shift+v
Copy computer clipboard to device (and paste if the device runs Android >= 7)
.TP
.B LCtrl+i
Enable/disable FPS counter (print frames/second in logs) Enable/disable FPS counter (print frames/second in logs)
.TP .TP

View File

@ -12,6 +12,11 @@
void void
scrcpy_print_usage(const char *arg0) { scrcpy_print_usage(const char *arg0) {
#ifdef __APPLE__
# define CTRL_OR_CMD "Cmd"
#else
# define CTRL_OR_CMD "LCtrl"
#endif
fprintf(stderr, fprintf(stderr,
"Usage: %s [options]\n" "Usage: %s [options]\n"
"\n" "\n"
@ -181,71 +186,78 @@ scrcpy_print_usage(const char *arg0) {
"\n" "\n"
"Shortcuts:\n" "Shortcuts:\n"
"\n" "\n"
" RCtrl is the right Ctrl key (the left Ctrl key is forwarded to\n" " LCtrl is the left Ctrl key (the right Ctrl key is forwarded to\n"
" the device.\n" " the device).\n"
"\n" "\n"
" RCtrl+f\n" " " CTRL_OR_CMD "+f\n"
" Switch fullscreen mode\n" " Switch fullscreen mode\n"
"\n" "\n"
" RCtrl+Left\n" " " CTRL_OR_CMD "+Left\n"
" Rotate display left\n" " Rotate display left\n"
"\n" "\n"
" RCtrl+Right\n" " " CTRL_OR_CMD "+Right\n"
" Rotate display right\n" " Rotate display right\n"
"\n" "\n"
" RCtrl+g\n" " " CTRL_OR_CMD "+g\n"
" Resize window to 1:1 (pixel-perfect)\n" " Resize window to 1:1 (pixel-perfect)\n"
"\n" "\n"
" RCtrl+w\n" " " CTRL_OR_CMD "+x\n"
" Double-click on black borders\n" " Double-click on black borders\n"
" Resize window to remove black borders\n" " Resize window to remove black borders\n"
"\n" "\n"
" RCtrl+h\n" " LCtrl+h\n"
" Middle-click\n" " Middle-click\n"
" Click on HOME\n" " Click on HOME\n"
"\n" "\n"
" RCtrl+b\n" " " CTRL_OR_CMD "+b\n"
" RCtrl+Backspace\n" " " CTRL_OR_CMD "+Backspace\n"
" Right-click (when screen is on)\n" " Right-click (when screen is on)\n"
" Click on BACK\n" " Click on BACK\n"
"\n" "\n"
" RCtrl+s\n" " " CTRL_OR_CMD "+s\n"
" Click on APP_SWITCH\n" " Click on APP_SWITCH\n"
"\n" "\n"
" RCtrl+m\n" " LCtrl+m\n"
" Click on MENU\n" " Click on MENU\n"
"\n" "\n"
" RCtrl+Up\n" " " CTRL_OR_CMD "+Up\n"
" Click on VOLUME_UP\n" " Click on VOLUME_UP\n"
"\n" "\n"
" RCtrl+Down\n" " " CTRL_OR_CMD "+Down\n"
" Click on VOLUME_DOWN\n" " Click on VOLUME_DOWN\n"
"\n" "\n"
" RCtrl+p\n" " " CTRL_OR_CMD "+p\n"
" Click on POWER (turn screen on/off)\n" " Click on POWER (turn screen on/off)\n"
"\n" "\n"
" Right-click (when screen is off)\n" " Right-click (when screen is off)\n"
" Power on\n" " Power on\n"
"\n" "\n"
" RCtrl+o\n" " " CTRL_OR_CMD "+o\n"
" Turn device screen off (keep mirroring)\n" " Turn device screen off (keep mirroring)\n"
"\n" "\n"
" RCtrl+Shift+o\n" " " CTRL_OR_CMD "+Shift+o\n"
" Turn device screen on\n" " Turn device screen on\n"
"\n" "\n"
" RCtrl+r\n" " " CTRL_OR_CMD "+r\n"
" Rotate device screen\n" " Rotate device screen\n"
"\n" "\n"
" RCtrl+n\n" " " CTRL_OR_CMD "+n\n"
" Expand notification panel\n" " Expand notification panel\n"
"\n" "\n"
" RCtrl+Shift+n\n" " " CTRL_OR_CMD "+Shift+n\n"
" Collapse notification panel\n" " Collapse notification panel\n"
"\n" "\n"
" RCtrl+v\n" " " CTRL_OR_CMD "+c\n"
" Inject computer clipboard text\n" " Copy device clipboard to computer\n"
"\n" "\n"
" RCtrl+i\n" " " CTRL_OR_CMD "+v\n"
" Paste computer clipboard to device\n"
"\n"
" " CTRL_OR_CMD "+Shift+v\n"
" Copy computer clipboard to device (and paste if the device\n"
" runs Android >= 7)\n"
"\n"
" " CTRL_OR_CMD "+i\n"
" Enable/disable FPS counter (print frames/second in logs)\n" " Enable/disable FPS counter (print frames/second in logs)\n"
"\n" "\n"
" Drag & drop APK file\n" " Drag & drop APK file\n"

View File

@ -92,9 +92,11 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT); MAP(SDLK_LEFT, AKEYCODE_DPAD_LEFT);
MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN); MAP(SDLK_DOWN, AKEYCODE_DPAD_DOWN);
MAP(SDLK_UP, AKEYCODE_DPAD_UP); 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_LSHIFT, AKEYCODE_SHIFT_LEFT);
MAP(SDLK_RSHIFT, AKEYCODE_SHIFT_RIGHT); MAP(SDLK_RSHIFT, AKEYCODE_SHIFT_RIGHT);
MAP(SDLK_LALT, AKEYCODE_ALT_LEFT);
MAP(SDLK_RALT, AKEYCODE_ALT_RIGHT);
} }
if (!(mod & (KMOD_NUM | KMOD_SHIFT))) { if (!(mod & (KMOD_NUM | KMOD_SHIFT))) {
@ -114,7 +116,7 @@ convert_keycode(SDL_Keycode from, enum android_keycode *to, uint16_t mod,
} }
} }
if (prefer_text && !(mod & KMOD_LCTRL)) { if (prefer_text) {
// do not forward alpha and space key events // do not forward alpha and space key events
return false; return false;
} }

View File

@ -101,6 +101,16 @@ collapse_notification_panel(struct controller *controller) {
} }
} }
static void
request_device_clipboard(struct controller *controller) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_GET_CLIPBOARD;
if (!controller_push_msg(controller, &msg)) {
LOGW("Could not request device clipboard");
}
}
static void static void
set_device_clipboard(struct controller *controller, bool paste) { set_device_clipboard(struct controller *controller, bool paste) {
char *text = SDL_GetClipboardText(); char *text = SDL_GetClipboardText();
@ -242,25 +252,6 @@ convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
return true; return true;
} }
static void
inject_as_ctrl(struct input_manager *im, const SDL_KeyboardEvent *event) {
struct control_msg msg;
if (!convert_input_key(event, &msg, false)) {
return;
}
// Disable RCtrl and Meta
msg.inject_keycode.metastate &=
~(AMETA_CTRL_RIGHT_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
// Enable LCtrl
msg.inject_keycode.metastate |= AMETA_CTRL_LEFT_ON;
if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject keycode'");
}
}
void void
input_manager_process_key(struct input_manager *im, input_manager_process_key(struct input_manager *im,
const SDL_KeyboardEvent *event, const SDL_KeyboardEvent *event,
@ -268,69 +259,64 @@ input_manager_process_key(struct input_manager *im,
// control: indicates the state of the command-line option --no-control // control: indicates the state of the command-line option --no-control
// ctrl: the Ctrl key // ctrl: the Ctrl key
bool lctrl = event->keysym.mod & KMOD_LCTRL; // Only capture Left-Ctrl, Right-Ctrl is forwarded to the device
bool rctrl = event->keysym.mod & KMOD_RCTRL; bool ctrl = event->keysym.mod & KMOD_LCTRL;
bool alt = event->keysym.mod & (KMOD_LALT | KMOD_RALT);
bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI); bool meta = event->keysym.mod & (KMOD_LGUI | KMOD_RGUI);
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
bool shortcut_key = rctrl; // use Cmd on macOS, Ctrl on other platforms
#ifdef __APPLE__ #ifdef __APPLE__
shortcut_key |= meta; bool cmd = !ctrl && meta;
#else #else
if (meta) { if (meta) {
// No shortcut involve Meta, and it is not forwarded to the device // no shortcuts involve Meta on platforms other than macOS, and it must
// not be forwarded to the device
return; return;
} }
bool cmd = ctrl; // && !meta, already guaranteed
#endif #endif
if (alt) {
// No shortcuts involve Alt, and it is not forwarded to the device
return;
}
struct controller *controller = im->controller; struct controller *controller = im->controller;
SDL_Keycode keycode = event->keysym.sym; // capture all Ctrl events
bool down = event->type == SDL_KEYDOWN; if (ctrl || cmd) {
SDL_Keycode keycode = event->keysym.sym;
// Capture all RCtrl events bool down = event->type == SDL_KEYDOWN;
if (shortcut_key) {
int action = down ? ACTION_DOWN : ACTION_UP; int action = down ? ACTION_DOWN : ACTION_UP;
bool repeat = event->repeat; bool repeat = event->repeat;
bool shift = event->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT);
switch (keycode) { switch (keycode) {
case SDLK_h: case SDLK_h:
// Ctrl+h on all platform, since Cmd+h is already captured by // Ctrl+h on all platform, since Cmd+h is already captured by
// the system on macOS to hide the window // the system on macOS to hide the window
if (control && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_home(controller, action); action_home(controller, action);
} }
return; return;
case SDLK_b: // fall-through case SDLK_b: // fall-through
case SDLK_BACKSPACE: case SDLK_BACKSPACE:
if (control && !shift && !repeat) { if (control && cmd && !shift && !repeat) {
action_back(controller, action); action_back(controller, action);
} }
return; return;
case SDLK_s: case SDLK_s:
if (control && !shift && !repeat) { if (control && cmd && !shift && !repeat) {
action_app_switch(controller, action); action_app_switch(controller, action);
} }
return; return;
case SDLK_m: case SDLK_m:
// Ctrl+m on all platform, since Cmd+m is already captured by // Ctrl+m on all platform, since Cmd+m is already captured by
// the system on macOS to minimize the window // the system on macOS to minimize the window
if (control && !shift && !repeat) { if (control && ctrl && !meta && !shift && !repeat) {
action_menu(controller, action); action_menu(controller, action);
} }
return; return;
case SDLK_p: case SDLK_p:
if (control && !shift && !repeat) { if (control && cmd && !shift && !repeat) {
action_power(controller, action); action_power(controller, action);
} }
return; return;
case SDLK_o: case SDLK_o:
if (control && !repeat && down) { if (control && cmd && down) {
enum screen_power_mode mode = shift enum screen_power_mode mode = shift
? SCREEN_POWER_MODE_NORMAL ? SCREEN_POWER_MODE_NORMAL
: SCREEN_POWER_MODE_OFF; : SCREEN_POWER_MODE_OFF;
@ -338,57 +324,67 @@ input_manager_process_key(struct input_manager *im,
} }
return; return;
case SDLK_DOWN: case SDLK_DOWN:
if (control && !shift) { if (control && cmd && !shift) {
// forward repeated events // forward repeated events
action_volume_down(controller, action); action_volume_down(controller, action);
} }
return; return;
case SDLK_UP: case SDLK_UP:
if (control && !shift) { if (control && cmd && !shift) {
// forward repeated events // forward repeated events
action_volume_up(controller, action); action_volume_up(controller, action);
} }
return; return;
case SDLK_LEFT: case SDLK_LEFT:
if (!shift && !repeat && down) { if (cmd && !shift && down) {
rotate_client_left(im->screen); rotate_client_left(im->screen);
} }
return; return;
case SDLK_RIGHT: case SDLK_RIGHT:
if (!shift && !repeat && down) { if (cmd && !shift && down) {
rotate_client_right(im->screen); rotate_client_right(im->screen);
} }
return; return;
case SDLK_c:
if (control && cmd && !shift && !repeat && down) {
request_device_clipboard(controller);
}
return;
case SDLK_v: case SDLK_v:
if (control && !shift && !repeat && down) { if (control && cmd && !repeat && down) {
// Inject the text as input events if (shift) {
clipboard_paste(controller); // store the text in the device clipboard and paste
set_device_clipboard(controller, true);
} else {
// inject the text as input events
clipboard_paste(controller);
}
} }
return; return;
case SDLK_f: case SDLK_f:
if (!shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_switch_fullscreen(im->screen); screen_switch_fullscreen(im->screen);
} }
return; return;
case SDLK_w: case SDLK_x:
if (!shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_resize_to_fit(im->screen); screen_resize_to_fit(im->screen);
} }
return; return;
case SDLK_g: case SDLK_g:
if (!shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
screen_resize_to_pixel_perfect(im->screen); screen_resize_to_pixel_perfect(im->screen);
} }
return; return;
case SDLK_i: case SDLK_i:
if (!shift && !repeat && down) { if (!shift && cmd && !repeat && down) {
struct fps_counter *fps_counter = struct fps_counter *fps_counter =
im->video_buffer->fps_counter; im->video_buffer->fps_counter;
switch_fps_counter_state(fps_counter); switch_fps_counter_state(fps_counter);
} }
return; return;
case SDLK_n: case SDLK_n:
if (control && !repeat && down) { if (control && cmd && !repeat && down) {
if (shift) { if (shift) {
collapse_notification_panel(controller); collapse_notification_panel(controller);
} else { } else {
@ -397,19 +393,10 @@ input_manager_process_key(struct input_manager *im,
} }
return; return;
case SDLK_r: case SDLK_r:
if (control && !shift && !repeat && down) { if (control && cmd && !shift && !repeat && down) {
rotate_device(controller); rotate_device(controller);
} }
return; return;
case SDLK_c:
case SDLK_x:
if (control && !shift) {
// For convenience, forward shortcut_key+c and
// shortcut_key+x as Ctrl+c and Ctrl+x (typically "copy" and
// "cut", but not always) to the device
inject_as_ctrl(im, event);
}
return;
} }
return; return;
@ -419,12 +406,6 @@ input_manager_process_key(struct input_manager *im,
return; return;
} }
if (lctrl && !shift && keycode == SDLK_v && down) {
// Synchronize the computer clipboard to the device clipboard before
// sending Ctrl+V, to allow seamless copy-paste.
set_device_clipboard(controller, false);
}
struct control_msg msg; struct control_msg msg;
if (convert_input_key(event, &msg, im->prefer_text)) { if (convert_input_key(event, &msg, im->prefer_text)) {
if (!controller_push_msg(controller, &msg)) { if (!controller_push_msg(controller, &msg)) {

View File

@ -42,7 +42,7 @@ convert_log_level_to_sdl(enum sc_log_level level) {
return SDL_LOG_PRIORITY_ERROR; return SDL_LOG_PRIORITY_ERROR;
default: default:
assert(!"unexpected log level"); assert(!"unexpected log level");
return SDL_LOG_PRIORITY_INFO; return SC_LOG_LEVEL_INFO;
} }
} }
@ -71,7 +71,7 @@ main(int argc, char *argv[]) {
} }
SDL_LogPriority sdl_log = convert_log_level_to_sdl(args.opts.log_level); SDL_LogPriority sdl_log = convert_log_level_to_sdl(args.opts.log_level);
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, sdl_log); SDL_LogSetAllPriority(sdl_log);
if (args.help) { if (args.help) {
scrcpy_print_usage(argv[0]); scrcpy_print_usage(argv[0]);

View File

@ -17,6 +17,8 @@ public final class ControlMessage {
public static final int TYPE_SET_SCREEN_POWER_MODE = 9; public static final int TYPE_SET_SCREEN_POWER_MODE = 9;
public static final int TYPE_ROTATE_DEVICE = 10; public static final int TYPE_ROTATE_DEVICE = 10;
public static final int FLAGS_PASTE = 1;
private int type; private int type;
private String text; private String text;
private int metaState; // KeyEvent.META_* private int metaState; // KeyEvent.META_*
@ -28,7 +30,7 @@ public final class ControlMessage {
private Position position; private Position position;
private int hScroll; private int hScroll;
private int vScroll; private int vScroll;
private boolean paste; private int flags;
private ControlMessage() { private ControlMessage() {
} }
@ -73,7 +75,9 @@ public final class ControlMessage {
ControlMessage msg = new ControlMessage(); ControlMessage msg = new ControlMessage();
msg.type = TYPE_SET_CLIPBOARD; msg.type = TYPE_SET_CLIPBOARD;
msg.text = text; msg.text = text;
msg.paste = paste; if (paste) {
msg.flags = FLAGS_PASTE;
}
return msg; return msg;
} }
@ -137,7 +141,7 @@ public final class ControlMessage {
return vScroll; return vScroll;
} }
public boolean getPaste() { public int getFlags() {
return paste; return flags;
} }
} }

View File

@ -21,6 +21,7 @@ public class ControlMessageReader {
private final byte[] rawBuffer = new byte[RAW_BUFFER_SIZE]; private final byte[] rawBuffer = new byte[RAW_BUFFER_SIZE];
private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer); private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer);
private final byte[] textBuffer = new byte[CLIPBOARD_TEXT_MAX_LENGTH];
public ControlMessageReader() { public ControlMessageReader() {
// invariant: the buffer is always in "get" mode // invariant: the buffer is always in "get" mode
@ -110,10 +111,8 @@ public class ControlMessageReader {
if (buffer.remaining() < len) { if (buffer.remaining() < len) {
return null; return null;
} }
int position = buffer.position(); buffer.get(textBuffer, 0, len);
// Move the buffer position to consume the text return new String(textBuffer, 0, len, StandardCharsets.UTF_8);
buffer.position(position + len);
return new String(rawBuffer, position, len, StandardCharsets.UTF_8);
} }
private ControlMessage parseInjectText() { private ControlMessage parseInjectText() {

View File

@ -110,7 +110,8 @@ public class Controller {
} }
break; break;
case ControlMessage.TYPE_SET_CLIPBOARD: case ControlMessage.TYPE_SET_CLIPBOARD:
setClipboard(msg.getText(), msg.getPaste()); boolean paste = (msg.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
setClipboard(msg.getText(), paste);
break; break;
case ControlMessage.TYPE_SET_SCREEN_POWER_MODE: case ControlMessage.TYPE_SET_SCREEN_POWER_MODE:
if (device.supportsInputEvents()) { if (device.supportsInputEvents()) {

View File

@ -207,14 +207,6 @@ public final class Device {
} }
public boolean setClipboardText(String text) { public boolean setClipboardText(String text) {
String currentClipboard = getClipboardText();
if (currentClipboard == null || currentClipboard.equals(text)) {
// The clipboard already contains the requested text.
// Since pasting text from the computer involves setting the device clipboard, it could be set twice on a copy-paste. This would cause
// the clipboard listeners to be notified twice, and that would flood the Android keyboard clipboard history. To workaround this
// problem, do not explicitly set the clipboard text if it already contains the expected content.
return false;
}
isSettingClipboard.set(true); isSettingClipboard.set(true);
boolean ok = serviceManager.getClipboardManager().setText(text); boolean ok = serviceManager.getClipboardManager().setText(text);
isSettingClipboard.set(false); isSettingClipboard.set(false);

View File

@ -228,7 +228,9 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType()); Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals("testé", event.getText()); Assert.assertEquals("testé", event.getText());
Assert.assertTrue(event.getPaste());
boolean parse = (event.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
Assert.assertTrue(parse);
} }
@Test @Test
@ -254,7 +256,9 @@ public class ControlMessageReaderTest {
Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType()); Assert.assertEquals(ControlMessage.TYPE_SET_CLIPBOARD, event.getType());
Assert.assertEquals(text, event.getText()); Assert.assertEquals(text, event.getText());
Assert.assertTrue(event.getPaste());
boolean parse = (event.getFlags() & ControlMessage.FLAGS_PASTE) != 0;
Assert.assertTrue(parse);
} }
@Test @Test