aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java
diff options
context:
space:
mode:
authorLena <lena@omega>2026-01-01 00:00:00 +0000
committerLena <lena@omega>2026-06-24 22:14:50 +0300
commiteb0c8951196c637e44daf3c0617b131b997d5d2c (patch)
tree2f83b6a41cee745467e53fc401942b82cea286ea /app/src/main/java/invalid/lena/scrcpy/ControlMessages.java
downloadscrcpy-android-eb0c8951196c637e44daf3c0617b131b997d5d2c.tar.gz
scrcpy-android: mirror an Android device over wireless ADB0.1
Native Java app for Android 12+ that mirrors another Android device over wireless ADB, forwarding video, audio, touch input, and clipboard. Bundles a pinned scrcpy-server.jar and the vendored libadb-android stack. Supports h264/h265/av1 video and raw/opus audio with in-app codec selection. No NDK, no Kotlin. Includes JVM unit tests and a Docker-based emulator e2e rig.
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/ControlMessages.java')
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/ControlMessages.java72
1 files changed, 72 insertions, 0 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java b/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java
new file mode 100644
index 0000000..bd983ed
--- /dev/null
+++ b/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java
@@ -0,0 +1,72 @@
+package invalid.lena.scrcpy;
+
+import java.nio.charset.StandardCharsets;
+
+// Pure-java encoders for scrcpy control messages. Layouts mirror
+// com.genymobile.scrcpy.control.ControlMessageReader in the scrcpy
+// server source. Extracted from Controller so the wire format can be
+// unit-tested without android.* on the classpath.
+public final class ControlMessages {
+
+ public static final int TYPE_INJECT_KEYCODE = 0;
+ public static final int TYPE_INJECT_TEXT = 1;
+ public static final int TYPE_INJECT_TOUCH_EVENT = 2;
+ public static final int TYPE_BACK_OR_SCREEN_ON = 4;
+ public static final int TYPE_SET_CLIPBOARD = 9;
+
+ // KeyEvent.ACTION_DOWN / ACTION_UP. Mirror the int values rather
+ // than depend on android.view.KeyEvent so this stays android-free.
+ public static final int ACTION_DOWN = 0;
+ public static final int ACTION_UP = 1;
+
+ public static final int TOUCH_MSG_LEN = 32; // 1 + 1 + 8 + 4 + 4 + 2 + 2 + 2 + 4 + 4
+ public static final int KEY_MSG_LEN = 14; // 1 + 1 + 4 + 4 + 4
+ public static final int BACK_MSG_LEN = 2; // 1 + 1
+
+ private ControlMessages() {}
+
+ public static byte[] touch(int action, long pointerId,
+ int x, int y, int targetW, int targetH,
+ int pressureU16, int actionButton, int buttons) {
+ byte[] m = new byte[TOUCH_MSG_LEN];
+ m[0] = TYPE_INJECT_TOUCH_EVENT;
+ m[1] = (byte) action;
+ Wire.writeBe64(m, 2, pointerId);
+ Wire.writeBe32(m, 10, x);
+ Wire.writeBe32(m, 14, y);
+ m[18] = (byte)(targetW >>> 8); m[19] = (byte) targetW;
+ m[20] = (byte)(targetH >>> 8); m[21] = (byte) targetH;
+ m[22] = (byte)(pressureU16 >>> 8); m[23] = (byte) pressureU16;
+ Wire.writeBe32(m, 24, actionButton);
+ Wire.writeBe32(m, 28, buttons);
+ return m;
+ }
+
+ public static byte[] keycode(int action, int keycode, int repeat, int metaState) {
+ byte[] m = new byte[KEY_MSG_LEN];
+ m[0] = TYPE_INJECT_KEYCODE;
+ m[1] = (byte) action;
+ Wire.writeBe32(m, 2, keycode);
+ Wire.writeBe32(m, 6, repeat);
+ Wire.writeBe32(m, 10, metaState);
+ return m;
+ }
+
+ // Single button-press event. scrcpy server interprets this as Back
+ // when the screen is on, or screen-on when it's off. We send a
+ // DOWN+UP pair to make a tap; this helper builds one byte pair.
+ public static byte[] backOrScreenOn(int action) {
+ return new byte[]{(byte) TYPE_BACK_OR_SCREEN_ON, (byte) action};
+ }
+
+ public static byte[] setClipboard(long sequence, boolean paste, String text) {
+ byte[] data = text.getBytes(StandardCharsets.UTF_8);
+ byte[] m = new byte[1 + 8 + 1 + 4 + data.length];
+ m[0] = TYPE_SET_CLIPBOARD;
+ Wire.writeBe64(m, 1, sequence);
+ m[9] = (byte)(paste ? 1 : 0);
+ Wire.writeBe32(m, 10, data.length);
+ System.arraycopy(data, 0, m, 14, data.length);
+ return m;
+ }
+}