Skip to content

Commit

Permalink
Add pinch-to-zoom simulation
Browse files Browse the repository at this point in the history
If Ctrl is hold when the left-click button is pressed, enable
pinch-to-zoom to scale and rotate relative to the center of the screen.

Fixes #24 <#24>
  • Loading branch information
rom1v committed Aug 10, 2020
1 parent 95f1ea0 commit 198346d
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 8 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,19 @@ into the device clipboard. As a consequence, any Android application could read
its content. You should avoid to paste sensitive content (like passwords) that
way.


#### Pinch-to-zoom

To simulate "pinch-to-zoom": <kbd>Ctrl</kbd>+_click-and-move_.

More precisely, hold <kbd>Ctrl</kbd> while pressing the left-click button. Until
the left-click button is released, all mouse movements scale and rotate the
content (if supported by the app) relative to the center of the screen.

Concretely, scrcpy generates additional touch events from a "virtual finger" at
a location inverted through the center of the screen.


#### Text injection preference

There are two kinds of [events][textevents] generated when typing text:
Expand Down Expand Up @@ -659,6 +672,7 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
| Synchronize clipboards and paste³ | <kbd>MOD</kbd>+<kbd>v</kbd>
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
| Pinch-to-zoom | <kbd>Ctrl</kbd>+_click-and-move_

_¹Double-click on black borders to remove them._
_²Right-click turns the screen on if it was off, presses BACK otherwise._
Expand Down
4 changes: 4 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ Inject computer clipboard text as a sequence of key events
.B MOD+i
Enable/disable FPS counter (print frames/second in logs)

.TP
.B Ctrl+click-and-move
Pinch-to-zoom from the center of the screen

.TP
.B Drag & drop APK file
Install APK from computer
Expand Down
3 changes: 3 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ scrcpy_print_usage(const char *arg0) {
" 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",
Expand Down
1 change: 1 addition & 0 deletions app/src/control_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH (CONTROL_MSG_MAX_SIZE - 6)

#define POINTER_ID_MOUSE UINT64_C(-1);
#define POINTER_ID_VIRTUAL_FINGER UINT64_C(-2);

enum control_msg_type {
CONTROL_MSG_TYPE_INJECT_KEYCODE,
Expand Down
84 changes: 76 additions & 8 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ input_manager_init(struct input_manager *im,
im->sdl_shortcut_mods.data[i] = sdl_mod;
}
im->sdl_shortcut_mods.count = shortcut_mods->count;

im->vfinger_down = false;
}

static void
Expand Down Expand Up @@ -299,6 +301,36 @@ input_manager_process_text_input(struct input_manager *im,
}
}

static bool
simulate_virtual_finger(struct input_manager *im,
enum android_motionevent_action action,
struct point point) {
bool up = action == AMOTION_EVENT_ACTION_UP;

struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT;
msg.inject_touch_event.action = action;
msg.inject_touch_event.position.screen_size = im->screen->frame_size;
msg.inject_touch_event.position.point = point;
msg.inject_touch_event.pointer_id = POINTER_ID_VIRTUAL_FINGER;
msg.inject_touch_event.pressure = up ? 0.0f : 1.0f;
msg.inject_touch_event.buttons = 0;

if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject virtual finger event'");
return false;
}

return true;
}

static struct point
inverse_point(struct point point, struct size size) {
point.x = size.width - point.x;
point.y = size.height - point.y;
return point;
}

static bool
convert_input_key(const SDL_KeyboardEvent *from, struct control_msg *to,
bool prefer_text, uint32_t repeat) {
Expand Down Expand Up @@ -512,10 +544,18 @@ input_manager_process_mouse_motion(struct input_manager *im,
return;
}
struct control_msg msg;
if (convert_mouse_motion(event, im->screen, &msg)) {
if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject mouse motion event'");
}
if (!convert_mouse_motion(event, im->screen, &msg)) {
return;
}

if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject mouse motion event'");
}

if (im->vfinger_down) {
struct point mouse = msg.inject_touch_event.position.point;
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger);
}
}

Expand Down Expand Up @@ -587,7 +627,9 @@ input_manager_process_mouse_button(struct input_manager *im,
// simulated from touch events, so it's a duplicate
return;
}
if (event->type == SDL_MOUSEBUTTONDOWN) {

bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (down) {
if (control && event->button == SDL_BUTTON_RIGHT) {
press_back_or_turn_screen_on(im->controller);
return;
Expand Down Expand Up @@ -618,10 +660,36 @@ input_manager_process_mouse_button(struct input_manager *im,
}

struct control_msg msg;
if (convert_mouse_button(event, im->screen, &msg)) {
if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject mouse button event'");
if (!convert_mouse_button(event, im->screen, &msg)) {
return;
}

if (!controller_push_msg(im->controller, &msg)) {
LOGW("Could not request 'inject mouse button event'");
return;
}

// Pinch-to-zoom simulation.
//
// If Ctrl is hold when the left-click button is pressed, then
// pinch-to-zoom mode is enabled: on every mouse event until the left-click
// button is released, an additional "virtual finger" event is generated,
// having a position inverted through the center of the screen.
//
// In other words, the center of the rotation/scaling is the center of the
// screen.
#define CTRL_PRESSED (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL))
if ((down && !im->vfinger_down && CTRL_PRESSED)
|| (!down && im->vfinger_down)) {
struct point mouse = msg.inject_touch_event.position.point;
struct point vfinger = inverse_point(mouse, im->screen->frame_size);
enum android_motionevent_action action = down
? AMOTION_EVENT_ACTION_DOWN
: AMOTION_EVENT_ACTION_UP;
if (!simulate_virtual_finger(im, action, vfinger)) {
return;
}
im->vfinger_down = down;
}
}

Expand Down
2 changes: 2 additions & 0 deletions app/src/input_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ struct input_manager {
unsigned data[SC_MAX_SHORTCUT_MODS];
unsigned count;
} sdl_shortcut_mods;

bool vfinger_down;
};

void
Expand Down

0 comments on commit 198346d

Please sign in to comment.