From b029b1de9d19f54a84710156ecc1771d0897aef9 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 16 Apr 2021 18:37:50 +0200 Subject: [PATCH] Forward DOWN and UP separately for right-click The shortcut "back on screen on" is a bit special: the control is requested by the client, but the actual event injection (POWER or BACK) is determined on the device. To properly inject DOWN and UP events for BACK, transmit the action as a control parameter. If the screen is off: - on DOWN, inject POWER (DOWN and UP) (wake up the device immediately) - on UP, do nothing If the screen is on: - on DOWN, inject BACK DOWN - on UP, inject BACK UP A corner case is when the screen turns off between the DOWN and UP event. In that case, a BACK UP event will be injected, so it's harmless. As a consequence of this change, the BACK button is now handled by Android on mouse released. This is consistent with the keyboard shortcut (Mod+b) behavior. PR #2259 Refs #2258 --- app/src/control_msg.c | 4 +++- app/src/control_msg.h | 4 ++++ app/src/input_manager.c | 22 ++++++++++++++----- app/tests/test_control_msg_serialize.c | 6 ++++- .../com/genymobile/scrcpy/ControlMessage.java | 7 ++++++ .../scrcpy/ControlMessageReader.java | 13 ++++++++++- .../com/genymobile/scrcpy/Controller.java | 20 ++++++++++++----- .../scrcpy/ControlMessageReaderTest.java | 2 ++ 8 files changed, 64 insertions(+), 14 deletions(-) diff --git a/app/src/control_msg.c b/app/src/control_msg.c index 436a886142..69e750140a 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -67,6 +67,9 @@ 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_BACK_OR_SCREEN_ON: + buf[1] = msg->inject_keycode.action; + return 2; case CONTROL_MSG_TYPE_SET_CLIPBOARD: { buf[1] = !!msg->set_clipboard.paste; size_t len = write_string(msg->set_clipboard.text, @@ -77,7 +80,6 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) { case CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE: buf[1] = msg->set_screen_power_mode.mode; return 2; - 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: diff --git a/app/src/control_msg.h b/app/src/control_msg.h index 1b25591de1..8d9ab7d42d 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -64,6 +64,10 @@ struct control_msg { int32_t hscroll; int32_t vscroll; } inject_scroll_event; + struct { + enum android_keyevent_action action; // action for the BACK key + // screen may only be turned on on ACTION_DOWN + } back_or_screen_on; struct { char *text; // owned, to be freed by free() bool paste; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 10af6e8b4e..c10e53af5d 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -146,13 +146,25 @@ action_cut(struct controller *controller, int actions) { } // turn the screen on if it was off, press BACK otherwise +// If the screen is off, it is turned on only on ACTION_DOWN static void -press_back_or_turn_screen_on(struct controller *controller) { +press_back_or_turn_screen_on(struct controller *controller, int actions) { struct control_msg msg; msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON; - if (!controller_push_msg(controller, &msg)) { - LOGW("Could not request 'press back or turn screen on'"); + if (actions & ACTION_DOWN) { + msg.back_or_screen_on.action = AKEY_EVENT_ACTION_DOWN; + if (!controller_push_msg(controller, &msg)) { + LOGW("Could not request 'press back or turn screen on'"); + return; + } + } + + if (actions & ACTION_UP) { + msg.back_or_screen_on.action = AKEY_EVENT_ACTION_UP; + if (!controller_push_msg(controller, &msg)) { + LOGW("Could not request 'press back or turn screen on'"); + } } } @@ -650,9 +662,7 @@ input_manager_process_mouse_button(struct input_manager *im, int action = down ? ACTION_DOWN : ACTION_UP; if (control && event->button == SDL_BUTTON_RIGHT) { - if (down) { - press_back_or_turn_screen_on(im->controller); - } + press_back_or_turn_screen_on(im->controller, action); return; } if (control && event->button == SDL_BUTTON_MIDDLE) { diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index dc6e082109..4771ce1fe5 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -146,14 +146,18 @@ static void test_serialize_inject_scroll_event(void) { static void test_serialize_back_or_screen_on(void) { struct control_msg msg = { .type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON, + .back_or_screen_on = { + .action = AKEY_EVENT_ACTION_UP, + }, }; unsigned char buf[CONTROL_MSG_MAX_SIZE]; size_t size = control_msg_serialize(&msg, buf); - assert(size == 1); + assert(size == 2); const unsigned char expected[] = { CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON, + 0x01, // AKEY_EVENT_ACTION_UP }; 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 736acf80b9..44cb1b599f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java @@ -71,6 +71,13 @@ public static ControlMessage createInjectScrollEvent(Position position, int hScr return msg; } + public static ControlMessage createBackOrScreenOn(int action) { + ControlMessage msg = new ControlMessage(); + msg.type = TYPE_BACK_OR_SCREEN_ON; + msg.action = action; + return msg; + } + public static ControlMessage createSetClipboard(String text, boolean paste) { ControlMessage msg = new ControlMessage(); msg.type = TYPE_SET_CLIPBOARD; diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java index ce18510318..7ebecf76ac 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java @@ -11,6 +11,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 BACK_OR_SCREEN_ON_LENGTH = 1; static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1; static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1; @@ -66,13 +67,15 @@ public ControlMessage next() { case ControlMessage.TYPE_INJECT_SCROLL_EVENT: msg = parseInjectScrollEvent(); break; + case ControlMessage.TYPE_BACK_OR_SCREEN_ON: + msg = parseBackOrScreenOnEvent(); + break; case ControlMessage.TYPE_SET_CLIPBOARD: msg = parseSetClipboard(); break; case ControlMessage.TYPE_SET_SCREEN_POWER_MODE: msg = parseSetScreenPowerMode(); break; - case ControlMessage.TYPE_BACK_OR_SCREEN_ON: case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL: case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL: case ControlMessage.TYPE_GET_CLIPBOARD: @@ -150,6 +153,14 @@ private ControlMessage parseInjectScrollEvent() { return ControlMessage.createInjectScrollEvent(position, hScroll, vScroll); } + private ControlMessage parseBackOrScreenOnEvent() { + if (buffer.remaining() < BACK_OR_SCREEN_ON_LENGTH) { + return null; + } + int action = toUnsigned(buffer.get()); + return ControlMessage.createBackOrScreenOn(action); + } + private ControlMessage parseSetClipboard() { if (buffer.remaining() < SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH) { return null; diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index 8f262ab6de..6af5ddf649 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -101,7 +101,7 @@ private void handleEvent() throws IOException { break; case ControlMessage.TYPE_BACK_OR_SCREEN_ON: if (device.supportsInputEvents()) { - pressBackOrTurnScreenOn(); + pressBackOrTurnScreenOn(msg.getAction()); } break; case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL: @@ -255,12 +255,22 @@ public void run() { }, 200, TimeUnit.MILLISECONDS); } - private boolean pressBackOrTurnScreenOn() { - int keycode = Device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER; - if (keepPowerModeOff && keycode == KeyEvent.KEYCODE_POWER) { + private boolean pressBackOrTurnScreenOn(int action) { + if (Device.isScreenOn()) { + return device.injectKeyEvent(action, KeyEvent.KEYCODE_BACK, 0, 0); + } + + // Screen is off + // Only press POWER on ACTION_DOWN + if (action != KeyEvent.ACTION_DOWN) { + // do nothing, + return true; + } + + if (keepPowerModeOff) { schedulePowerModeOff(); } - return device.injectKeycode(keycode); + return device.injectKeycode(KeyEvent.KEYCODE_POWER); } private boolean setClipboard(String text, boolean paste) { diff --git a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java index 5eb52760fe..8dfaa5aa1b 100644 --- a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java @@ -154,6 +154,7 @@ public void testParseBackOrScreenOnEvent() throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); dos.writeByte(ControlMessage.TYPE_BACK_OR_SCREEN_ON); + dos.writeByte(KeyEvent.ACTION_UP); byte[] packet = bos.toByteArray(); @@ -161,6 +162,7 @@ public void testParseBackOrScreenOnEvent() throws IOException { ControlMessage event = reader.next(); Assert.assertEquals(ControlMessage.TYPE_BACK_OR_SCREEN_ON, event.getType()); + Assert.assertEquals(KeyEvent.ACTION_DOWN, event.getAction()); } @Test