diff --git a/app/src/control_msg.h b/app/src/control_msg.h index e0b480de40..6e3f239c91 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -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, diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 1d73980cae..6a53ce5c36 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -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 @@ -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) { @@ -512,9 +544,19 @@ 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); + if (!simulate_virtual_finger(im, AMOTION_EVENT_ACTION_MOVE, vfinger)) { + return; } } } @@ -587,7 +629,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; @@ -618,10 +662,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 inversed with respect to 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; } } diff --git a/app/src/input_manager.h b/app/src/input_manager.h index 8811c4578a..c3756e40cf 100644 --- a/app/src/input_manager.h +++ b/app/src/input_manager.h @@ -30,6 +30,8 @@ struct input_manager { unsigned data[SC_MAX_SHORTCUT_MODS]; unsigned count; } sdl_shortcut_mods; + + bool vfinger_down; }; void