From 924375487e63a5a9114182015a68aedef2f52d14 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 31 Dec 2021 10:38:05 +0100 Subject: [PATCH] Pass buttons state in scroll events A scroll event might be produced when a mouse button is pressed (for example when scrolling while selecting a text). For consistency, pass the actual buttons state (instead of 0). In practice, it seems that this use case does not work properly with Android event injection, but it will work with HID mouse. --- app/src/control_msg.c | 8 +++++--- app/src/control_msg.h | 1 + app/src/input_events.h | 1 + app/src/input_manager.c | 4 +++- app/src/mouse_inject.c | 1 + app/tests/test_control_msg_serialize.c | 4 +++- .../main/java/com/genymobile/scrcpy/ControlMessage.java | 3 ++- .../java/com/genymobile/scrcpy/ControlMessageReader.java | 5 +++-- .../src/main/java/com/genymobile/scrcpy/Controller.java | 6 +++--- .../com/genymobile/scrcpy/ControlMessageReaderTest.java | 2 ++ 10 files changed, 24 insertions(+), 11 deletions(-) diff --git a/app/src/control_msg.c b/app/src/control_msg.c index 6ccdc054..75c74628 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -119,7 +119,8 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) { (uint32_t) msg->inject_scroll_event.hscroll); buffer_write32be(&buf[17], (uint32_t) msg->inject_scroll_event.vscroll); - return 21; + buffer_write32be(&buf[21], msg->inject_scroll_event.buttons); + return 25; case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON: buf[1] = msg->inject_keycode.action; return 2; @@ -192,11 +193,12 @@ control_msg_log(const struct control_msg *msg) { } case CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT: LOG_CMSG("scroll position=%" PRIi32 ",%" PRIi32 " hscroll=%" PRIi32 - " vscroll=%" PRIi32, + " vscroll=%" PRIi32 " buttons=%06lx", msg->inject_scroll_event.position.point.x, msg->inject_scroll_event.position.point.y, msg->inject_scroll_event.hscroll, - msg->inject_scroll_event.vscroll); + msg->inject_scroll_event.vscroll, + (long) msg->inject_scroll_event.buttons); break; case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON: LOG_CMSG("back-or-screen-on %s", diff --git a/app/src/control_msg.h b/app/src/control_msg.h index 7f3235d7..0eadd4f2 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -70,6 +70,7 @@ struct control_msg { struct sc_position position; int32_t hscroll; int32_t vscroll; + enum android_motionevent_buttons buttons; } inject_scroll_event; struct { enum android_keyevent_action action; // action for the BACK key diff --git a/app/src/input_events.h b/app/src/input_events.h index 48c51ee7..c27a7a47 100644 --- a/app/src/input_events.h +++ b/app/src/input_events.h @@ -360,6 +360,7 @@ struct sc_mouse_scroll_event { struct sc_position position; int32_t hscroll; int32_t vscroll; + uint8_t buttons_state; // bitwise-OR of sc_mouse_button values }; struct sc_mouse_motion_event { diff --git a/app/src/input_manager.c b/app/src/input_manager.c index ec91787a..64922f28 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -810,7 +810,7 @@ input_manager_process_mouse_wheel(struct input_manager *im, // mouse_x and mouse_y are expressed in pixels relative to the window int mouse_x; int mouse_y; - SDL_GetMouseState(&mouse_x, &mouse_y); + uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y); struct sc_mouse_scroll_event evt = { .position = { @@ -820,6 +820,8 @@ input_manager_process_mouse_wheel(struct input_manager *im, }, .hscroll = event->x, .vscroll = event->y, + .buttons_state = + sc_mouse_buttons_state_from_sdl(buttons, im->forward_all_clicks), }; im->mp->ops->process_mouse_scroll(im->mp, &evt); diff --git a/app/src/mouse_inject.c b/app/src/mouse_inject.c index 1d14509d..38bfa404 100644 --- a/app/src/mouse_inject.c +++ b/app/src/mouse_inject.c @@ -108,6 +108,7 @@ sc_mouse_processor_process_mouse_scroll(struct sc_mouse_processor *mp, .position = event->position, .hscroll = event->hscroll, .vscroll = event->vscroll, + .buttons = convert_mouse_buttons(event->buttons_state), }, }; diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index 42b72b59..d1f0f161 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -126,12 +126,13 @@ static void test_serialize_inject_scroll_event(void) { }, .hscroll = 1, .vscroll = -1, + .buttons = 1, }, }; unsigned char buf[CONTROL_MSG_MAX_SIZE]; size_t size = control_msg_serialize(&msg, buf); - assert(size == 21); + assert(size == 25); const unsigned char expected[] = { CONTROL_MSG_TYPE_INJECT_SCROLL_EVENT, @@ -139,6 +140,7 @@ static void test_serialize_inject_scroll_event(void) { 0x04, 0x38, 0x07, 0x80, // 1080 1920 0x00, 0x00, 0x00, 0x01, // 1 0xFF, 0xFF, 0xFF, 0xFF, // -1 + 0x00, 0x00, 0x00, 0x01, // 1 }; assert(!memcmp(buf, expected, sizeof(expected))); } diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java index 63ba0fa3..99eb805f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java @@ -71,12 +71,13 @@ public final class ControlMessage { return msg; } - public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll) { + public static ControlMessage createInjectScrollEvent(Position position, int hScroll, int vScroll, int buttons) { ControlMessage msg = new ControlMessage(); msg.type = TYPE_INJECT_SCROLL_EVENT; msg.position = position; msg.hScroll = hScroll; msg.vScroll = vScroll; + msg.buttons = buttons; return msg; } diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java index f09ed26f..24dc5e50 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java @@ -10,7 +10,7 @@ public class ControlMessageReader { static final int INJECT_KEYCODE_PAYLOAD_LENGTH = 13; static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 27; - static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20; + static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 24; static final int BACK_OR_SCREEN_ON_LENGTH = 1; static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1; static final int GET_CLIPBOARD_LENGTH = 1; @@ -154,7 +154,8 @@ public class ControlMessageReader { Position position = readPosition(buffer); int hScroll = buffer.getInt(); int vScroll = buffer.getInt(); - return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll); + int buttons = buffer.getInt(); + return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll, buttons); } private ControlMessage parseBackOrScreenOnEvent() { diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index 9246004a..481c512f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -98,7 +98,7 @@ public class Controller { break; case ControlMessage.TYPE_INJECT_SCROLL_EVENT: if (device.supportsInputEvents()) { - injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll()); + injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll(), msg.getButtons()); } break; case ControlMessage.TYPE_BACK_OR_SCREEN_ON: @@ -221,7 +221,7 @@ public class Controller { return device.injectEvent(event, Device.INJECT_MODE_ASYNC); } - private boolean injectScroll(Position position, int hScroll, int vScroll) { + private boolean injectScroll(Position position, int hScroll, int vScroll, int buttons) { long now = SystemClock.uptimeMillis(); Point point = device.getPhysicalPoint(position); if (point == null) { @@ -239,7 +239,7 @@ public class Controller { coords.setAxisValue(MotionEvent.AXIS_VSCROLL, vScroll); MotionEvent event = MotionEvent - .obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, 0, 1f, 1f, DEFAULT_DEVICE_ID, 0, + .obtain(lastTouchDown, now, MotionEvent.ACTION_SCROLL, 1, pointerProperties, pointerCoords, 0, buttons, 1f, 1f, DEFAULT_DEVICE_ID, 0, InputDevice.SOURCE_MOUSE, 0); return device.injectEvent(event, Device.INJECT_MODE_ASYNC); } diff --git a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java index 5e79d4f0..2a4ffe75 100644 --- a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java @@ -128,6 +128,7 @@ public class ControlMessageReaderTest { dos.writeShort(1920); dos.writeInt(1); dos.writeInt(-1); + dos.writeInt(1); byte[] packet = bos.toByteArray(); @@ -144,6 +145,7 @@ public class ControlMessageReaderTest { Assert.assertEquals(1920, event.getPosition().getScreenSize().getHeight()); Assert.assertEquals(1, event.getHScroll()); Assert.assertEquals(-1, event.getVScroll()); + Assert.assertEquals(1, event.getButtons()); } @Test