diff --git a/app/src/control_msg.c b/app/src/control_msg.c index 45113139..d544205c 100644 --- a/app/src/control_msg.c +++ b/app/src/control_msg.c @@ -75,6 +75,8 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) { buf[1] = msg->set_screen_power_mode.mode; return 2; case CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON: + buf[1] = msg->back_or_screen_on.action; + return 2; 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 49a159a6..665b9176 100644 --- a/app/src/control_msg.h +++ b/app/src/control_msg.h @@ -66,6 +66,9 @@ struct control_msg { struct { enum screen_power_mode mode; } set_screen_power_mode; + struct { + enum android_keyevent_action action; + } back_or_screen_on; }; }; diff --git a/app/src/input_manager.c b/app/src/input_manager.c index 03cfbd9a..0441c382 100644 --- a/app/src/input_manager.c +++ b/app/src/input_manager.c @@ -99,9 +99,14 @@ action_menu(struct controller *controller, int actions) { // turn the screen on if it was off, press BACK otherwise static void -press_back_or_turn_screen_on(struct controller *controller) { +press_back_or_turn_screen_on(struct controller *controller, int action) { + assert(action == ACTION_DOWN || action == ACTION_UP); + struct control_msg msg; msg.type = CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON; + msg.back_or_screen_on.action = action == ACTION_DOWN + ? AKEY_EVENT_ACTION_DOWN + : AKEY_EVENT_ACTION_UP; if (!controller_push_msg(controller, &msg)) { LOGW("Could not request 'press back or turn screen on'"); @@ -545,9 +550,9 @@ input_manager_process_mouse_button(struct input_manager *im, } if (event->button == SDL_BUTTON_RIGHT) { - if (event->type == SDL_MOUSEBUTTONDOWN) { - press_back_or_turn_screen_on(im->controller); - } + int action = event->type == SDL_MOUSEBUTTONDOWN ? ACTION_DOWN + : ACTION_UP; + press_back_or_turn_screen_on(im->controller, action); return; } diff --git a/app/tests/test_control_msg_serialize.c b/app/tests/test_control_msg_serialize.c index d6f556f3..cb6691f0 100644 --- a/app/tests/test_control_msg_serialize.c +++ b/app/tests/test_control_msg_serialize.c @@ -140,14 +140,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_SERIALIZED_MAX_SIZE]; int size = control_msg_serialize(&msg, buf); - assert(size == 1); + assert(size == 2); const unsigned char expected[] = { CONTROL_MSG_TYPE_BACK_OR_SCREEN_ON, + 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 195b04bf..365f9b2b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessage.java @@ -85,6 +85,13 @@ public final class ControlMessage { 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 createEmpty(int type) { ControlMessage msg = new ControlMessage(); msg.type = type; diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java index 726b5659..fbd946b2 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlMessageReader.java @@ -13,6 +13,7 @@ public class ControlMessageReader { private static final int INJECT_TOUCH_EVENT_PAYLOAD_LENGTH = 21; private static final int INJECT_SCROLL_EVENT_PAYLOAD_LENGTH = 20; private static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1; + private static final int BACK_OR_SCREEN_ON_PAYLOAD_LENGTH = 1; public static final int TEXT_MAX_LENGTH = 300; public static final int CLIPBOARD_TEXT_MAX_LENGTH = 4093; @@ -73,6 +74,8 @@ public class ControlMessageReader { msg = parseSetScreenPowerMode(); break; case ControlMessage.TYPE_BACK_OR_SCREEN_ON: + msg = parseBackOrScreenOn(); + break; case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL: case ControlMessage.TYPE_COLLAPSE_NOTIFICATION_PANEL: case ControlMessage.TYPE_GET_CLIPBOARD: @@ -164,6 +167,14 @@ public class ControlMessageReader { return ControlMessage.createSetScreenPowerMode(mode); } + private ControlMessage parseBackOrScreenOn() { + if (buffer.remaining() < BACK_OR_SCREEN_ON_PAYLOAD_LENGTH) { + return null; + } + int action = toUnsigned(buffer.get()); + return ControlMessage.createBackOrScreenOn(action); + } + private static Position readPosition(ByteBuffer buffer) { int x = buffer.getInt(); int y = buffer.getInt(); diff --git a/server/src/main/java/com/genymobile/scrcpy/Controller.java b/server/src/main/java/com/genymobile/scrcpy/Controller.java index dc0fa67b..263292aa 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/Controller.java @@ -26,6 +26,9 @@ public class Controller { private final MotionEvent.PointerProperties[] pointerProperties = new MotionEvent.PointerProperties[PointersState.MAX_POINTERS]; private final MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[PointersState.MAX_POINTERS]; + private boolean backPressed; + private boolean powerPressed; + public Controller(Device device, DesktopConnection connection) { this.device = device; this.connection = connection; @@ -88,7 +91,7 @@ public class Controller { injectScroll(msg.getPosition(), msg.getHScroll(), msg.getVScroll()); break; case ControlMessage.TYPE_BACK_OR_SCREEN_ON: - pressBackOrTurnScreenOn(); + pressBackOrTurnScreenOn(msg.getAction()); break; case ControlMessage.TYPE_EXPAND_NOTIFICATION_PANEL: device.expandNotificationPanel(); @@ -223,8 +226,33 @@ public class Controller { return device.injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); } - private boolean pressBackOrTurnScreenOn() { - int keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER; - return injectKeycode(keycode); + private boolean pressBackOrTurnScreenOn(int action) { + int keycode = -1; + if (action == KeyEvent.ACTION_UP) { + // if BACK or POWER were pressed, this action is the corresponding release event (regardless of the screen state) + if (backPressed) { + keycode = KeyEvent.KEYCODE_BACK; + backPressed = false; + } else if (powerPressed) { + keycode = KeyEvent.KEYCODE_POWER; + powerPressed = false; + } + } + + if (keycode == -1) { + keycode = device.isScreenOn() ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_POWER; + } + + if (action == KeyEvent.ACTION_DOWN) { + if (keycode == KeyEvent.KEYCODE_BACK) { + backPressed = true; + powerPressed = false; + } else { + backPressed = false; + powerPressed = true; + } + } + + return injectKeyEvent(action, keycode, 0, 0); } } diff --git a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java index 5e663bb9..bb867917 100644 --- a/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java +++ b/server/src/test/java/com/genymobile/scrcpy/ControlMessageReaderTest.java @@ -145,6 +145,7 @@ public class ControlMessageReaderTest { 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(); @@ -152,6 +153,7 @@ public class ControlMessageReaderTest { ControlMessage event = reader.next(); Assert.assertEquals(ControlMessage.TYPE_BACK_OR_SCREEN_ON, event.getType()); + Assert.assertEquals(KeyEvent.ACTION_UP, event.getAction()); } @Test