@yume-chan/scrcpy 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/CHANGELOG.json +36 -3
  2. package/CHANGELOG.md +21 -4
  3. package/LICENSE +1 -1
  4. package/README.md +291 -37
  5. package/bin/scrcpy-server +0 -0
  6. package/bin/version.d.ts +1 -1
  7. package/bin/version.js +1 -1
  8. package/esm/adb/client.d.ts +41 -0
  9. package/esm/adb/client.d.ts.map +1 -0
  10. package/esm/adb/client.js +243 -0
  11. package/esm/adb/client.js.map +1 -0
  12. package/esm/adb/connection.d.ts +47 -0
  13. package/esm/adb/connection.d.ts.map +1 -0
  14. package/esm/{connection.js → adb/connection.js} +35 -31
  15. package/esm/adb/connection.js.map +1 -0
  16. package/esm/adb/index.d.ts +4 -0
  17. package/esm/adb/index.d.ts.map +1 -0
  18. package/esm/adb/index.js +4 -0
  19. package/esm/adb/index.js.map +1 -0
  20. package/esm/adb/options/1_16.d.ts +8 -0
  21. package/esm/adb/options/1_16.d.ts.map +1 -0
  22. package/esm/adb/options/1_16.js +20 -0
  23. package/esm/adb/options/1_16.js.map +1 -0
  24. package/esm/adb/options/1_22.d.ts +8 -0
  25. package/esm/adb/options/1_22.d.ts.map +1 -0
  26. package/esm/adb/options/1_22.js +17 -0
  27. package/esm/adb/options/1_22.js.map +1 -0
  28. package/esm/adb/options/index.d.ts +4 -0
  29. package/esm/adb/options/index.d.ts.map +1 -0
  30. package/esm/adb/options/index.js +4 -0
  31. package/esm/adb/options/index.js.map +1 -0
  32. package/esm/adb/options/types.d.ts +24 -0
  33. package/esm/adb/options/types.d.ts.map +1 -0
  34. package/esm/adb/options/types.js +37 -0
  35. package/esm/adb/options/types.js.map +1 -0
  36. package/esm/codec.js +2 -2
  37. package/esm/codec.js.map +1 -1
  38. package/esm/control/back-or-screen-on.d.ts +3 -0
  39. package/esm/control/back-or-screen-on.d.ts.map +1 -0
  40. package/esm/control/back-or-screen-on.js +2 -0
  41. package/esm/control/back-or-screen-on.js.map +1 -0
  42. package/esm/control/hover-helper.d.ts +18 -0
  43. package/esm/control/hover-helper.d.ts.map +1 -0
  44. package/esm/control/hover-helper.js +44 -0
  45. package/esm/control/hover-helper.js.map +1 -0
  46. package/esm/control/index.d.ts +12 -0
  47. package/esm/control/index.d.ts.map +1 -0
  48. package/esm/control/index.js +12 -0
  49. package/esm/control/index.js.map +1 -0
  50. package/esm/control/inject-keycode.d.ts +61 -0
  51. package/esm/control/inject-keycode.d.ts.map +1 -0
  52. package/esm/control/inject-keycode.js +62 -0
  53. package/esm/control/inject-keycode.js.map +1 -0
  54. package/esm/control/inject-scroll.d.ts +3 -0
  55. package/esm/control/inject-scroll.d.ts.map +1 -0
  56. package/esm/control/inject-scroll.js +2 -0
  57. package/esm/control/inject-scroll.js.map +1 -0
  58. package/esm/control/inject-text.d.ts +9 -0
  59. package/esm/control/inject-text.d.ts.map +1 -0
  60. package/esm/control/inject-text.js +7 -0
  61. package/esm/control/inject-text.js.map +1 -0
  62. package/esm/control/inject-touch.d.ts +38 -0
  63. package/esm/control/inject-touch.d.ts.map +1 -0
  64. package/esm/control/inject-touch.js +63 -0
  65. package/esm/control/inject-touch.js.map +1 -0
  66. package/esm/control/rotate-device.d.ts +7 -0
  67. package/esm/control/rotate-device.d.ts.map +1 -0
  68. package/esm/control/rotate-device.js +5 -0
  69. package/esm/control/rotate-device.js.map +1 -0
  70. package/esm/control/serializer.d.ts +34 -0
  71. package/esm/control/serializer.d.ts.map +1 -0
  72. package/esm/control/serializer.js +79 -0
  73. package/esm/control/serializer.js.map +1 -0
  74. package/esm/control/set-clipboard.d.ts +3 -0
  75. package/esm/control/set-clipboard.d.ts.map +1 -0
  76. package/esm/control/set-clipboard.js +2 -0
  77. package/esm/control/set-clipboard.js.map +1 -0
  78. package/esm/control/set-screen-power-mode.d.ts +12 -0
  79. package/esm/control/set-screen-power-mode.d.ts.map +1 -0
  80. package/esm/control/set-screen-power-mode.js +12 -0
  81. package/esm/control/set-screen-power-mode.js.map +1 -0
  82. package/esm/control/type.d.ts +15 -0
  83. package/esm/control/type.d.ts.map +1 -0
  84. package/esm/control/type.js +19 -0
  85. package/esm/control/type.js.map +1 -0
  86. package/esm/device-message/ack-clipboard.d.ts +9 -0
  87. package/esm/device-message/ack-clipboard.d.ts.map +1 -0
  88. package/esm/device-message/ack-clipboard.js +6 -0
  89. package/esm/device-message/ack-clipboard.js.map +1 -0
  90. package/esm/device-message/clipboard.d.ts +10 -0
  91. package/esm/device-message/clipboard.d.ts.map +1 -0
  92. package/esm/device-message/clipboard.js +7 -0
  93. package/esm/device-message/clipboard.js.map +1 -0
  94. package/esm/device-message/index.d.ts +5 -0
  95. package/esm/device-message/index.d.ts.map +1 -0
  96. package/esm/device-message/index.js +5 -0
  97. package/esm/device-message/index.js.map +1 -0
  98. package/esm/device-message/stream.d.ts +8 -0
  99. package/esm/device-message/stream.d.ts.map +1 -0
  100. package/esm/device-message/stream.js +20 -0
  101. package/esm/device-message/stream.js.map +1 -0
  102. package/esm/device-message/type.d.ts +5 -0
  103. package/esm/device-message/type.d.ts.map +1 -0
  104. package/esm/device-message/type.js +7 -0
  105. package/esm/device-message/type.js.map +1 -0
  106. package/esm/index.d.ts +3 -6
  107. package/esm/index.d.ts.map +1 -1
  108. package/esm/index.js +3 -6
  109. package/esm/index.js.map +1 -1
  110. package/esm/options/1_16/codec-options.d.ts +20 -0
  111. package/esm/options/1_16/codec-options.d.ts.map +1 -0
  112. package/esm/options/1_16/codec-options.js +26 -0
  113. package/esm/options/1_16/codec-options.js.map +1 -0
  114. package/esm/options/1_16/h264-configuration.d.ts +49 -0
  115. package/esm/options/1_16/h264-configuration.d.ts.map +1 -0
  116. package/esm/options/1_16/h264-configuration.js +414 -0
  117. package/esm/options/1_16/h264-configuration.js.map +1 -0
  118. package/esm/options/1_16/index.d.ts +4 -110
  119. package/esm/options/1_16/index.d.ts.map +1 -1
  120. package/esm/options/1_16/index.js +4 -226
  121. package/esm/options/1_16/index.js.map +1 -1
  122. package/esm/options/1_16/options.d.ts +94 -0
  123. package/esm/options/1_16/options.d.ts.map +1 -0
  124. package/esm/options/1_16/options.js +190 -0
  125. package/esm/options/1_16/options.js.map +1 -0
  126. package/esm/options/1_16/scroll.d.ts +27 -0
  127. package/esm/options/1_16/scroll.d.ts.map +1 -0
  128. package/esm/options/1_16/scroll.js +56 -0
  129. package/esm/options/1_16/scroll.js.map +1 -0
  130. package/esm/options/1_16/sps.d.ts +1 -1
  131. package/esm/options/1_16/sps.d.ts.map +1 -1
  132. package/esm/options/1_16/sps.js +25 -17
  133. package/esm/options/1_16/sps.js.map +1 -1
  134. package/esm/options/1_18.d.ts +8 -8
  135. package/esm/options/1_18.d.ts.map +1 -1
  136. package/esm/options/1_18.js +7 -7
  137. package/esm/options/1_18.js.map +1 -1
  138. package/esm/options/1_21.d.ts +13 -2
  139. package/esm/options/1_21.d.ts.map +1 -1
  140. package/esm/options/1_21.js +14 -3
  141. package/esm/options/1_21.js.map +1 -1
  142. package/esm/options/1_22/index.d.ts +3 -0
  143. package/esm/options/1_22/index.d.ts.map +1 -0
  144. package/esm/options/1_22/index.js +3 -0
  145. package/esm/options/1_22/index.js.map +1 -0
  146. package/esm/options/1_22/options.d.ts +29 -0
  147. package/esm/options/1_22/options.d.ts.map +1 -0
  148. package/esm/options/1_22/options.js +26 -0
  149. package/esm/options/1_22/options.js.map +1 -0
  150. package/esm/options/1_22/scroll.d.ts +17 -0
  151. package/esm/options/1_22/scroll.d.ts.map +1 -0
  152. package/esm/options/1_22/scroll.js +15 -0
  153. package/esm/options/1_22/scroll.js.map +1 -0
  154. package/esm/options/1_23.d.ts +5 -5
  155. package/esm/options/1_23.d.ts.map +1 -1
  156. package/esm/options/1_23.js +7 -6
  157. package/esm/options/1_23.js.map +1 -1
  158. package/esm/options/1_24.d.ts +1 -1
  159. package/esm/options/1_24.d.ts.map +1 -1
  160. package/esm/options/1_24.js.map +1 -1
  161. package/esm/options/1_25/index.d.ts +3 -0
  162. package/esm/options/1_25/index.d.ts.map +1 -0
  163. package/esm/options/1_25/index.js +3 -0
  164. package/esm/options/1_25/index.js.map +1 -0
  165. package/esm/options/1_25/options.d.ts +6 -0
  166. package/esm/options/1_25/options.d.ts.map +1 -0
  167. package/esm/options/1_25/options.js +8 -0
  168. package/esm/options/1_25/options.js.map +1 -0
  169. package/esm/options/1_25/scroll.d.ts +19 -0
  170. package/esm/options/1_25/scroll.d.ts.map +1 -0
  171. package/esm/options/1_25/scroll.js +33 -0
  172. package/esm/options/1_25/scroll.js.map +1 -0
  173. package/esm/options/index.d.ts +5 -4
  174. package/esm/options/index.d.ts.map +1 -1
  175. package/esm/options/index.js +5 -4
  176. package/esm/options/index.js.map +1 -1
  177. package/esm/options/types.d.ts +47 -0
  178. package/esm/options/types.d.ts.map +1 -0
  179. package/esm/options/types.js +18 -0
  180. package/esm/options/types.js.map +1 -0
  181. package/package.json +32 -25
  182. package/scripts/fetch-server.cjs +19 -9
  183. package/src/adb/client.ts +345 -0
  184. package/src/adb/connection.ts +192 -0
  185. package/src/adb/index.ts +3 -0
  186. package/src/adb/options/1_16.ts +30 -0
  187. package/src/adb/options/1_22.ts +26 -0
  188. package/src/adb/options/index.ts +3 -0
  189. package/src/adb/options/types.ts +75 -0
  190. package/src/control/back-or-screen-on.ts +4 -0
  191. package/src/control/hover-helper.ts +56 -0
  192. package/src/control/index.ts +11 -0
  193. package/src/control/inject-keycode.ts +72 -0
  194. package/src/control/inject-scroll.ts +4 -0
  195. package/src/control/inject-text.ts +12 -0
  196. package/src/control/inject-touch.ts +78 -0
  197. package/src/control/rotate-device.ts +10 -0
  198. package/src/control/serializer.ts +151 -0
  199. package/src/control/set-clipboard.ts +4 -0
  200. package/src/control/set-screen-power-mode.ts +17 -0
  201. package/src/control/type.ts +17 -0
  202. package/src/device-message/ack-clipboard.ts +10 -0
  203. package/src/device-message/clipboard.ts +11 -0
  204. package/src/device-message/index.ts +4 -0
  205. package/src/device-message/stream.ts +29 -0
  206. package/src/device-message/type.ts +5 -0
  207. package/src/index.ts +3 -6
  208. package/src/options/1_16/codec-options.ts +55 -0
  209. package/src/options/1_16/h264-configuration.ts +485 -0
  210. package/src/options/1_16/index.ts +4 -345
  211. package/src/options/1_16/options.ts +339 -0
  212. package/src/options/1_16/scroll.ts +76 -0
  213. package/src/options/1_18.ts +24 -12
  214. package/src/options/1_21.ts +34 -6
  215. package/src/options/1_22/index.ts +2 -0
  216. package/src/options/1_22/options.ts +58 -0
  217. package/src/options/1_22/scroll.ts +26 -0
  218. package/src/options/1_23.ts +33 -19
  219. package/src/options/1_24.ts +3 -2
  220. package/src/options/1_25/index.ts +2 -0
  221. package/src/options/1_25/options.ts +12 -0
  222. package/src/options/1_25/scroll.ts +52 -0
  223. package/src/options/index.ts +5 -4
  224. package/src/options/types.ts +102 -0
  225. package/tsconfig.build.json +3 -0
  226. package/tsconfig.build.tsbuildinfo +1 -0
  227. package/esm/client.d.ts +0 -48
  228. package/esm/client.d.ts.map +0 -1
  229. package/esm/client.js +0 -341
  230. package/esm/client.js.map +0 -1
  231. package/esm/connection.d.ts +0 -37
  232. package/esm/connection.d.ts.map +0 -1
  233. package/esm/connection.js.map +0 -1
  234. package/esm/decoder/index.d.ts +0 -4
  235. package/esm/decoder/index.d.ts.map +0 -1
  236. package/esm/decoder/index.js +0 -4
  237. package/esm/decoder/index.js.map +0 -1
  238. package/esm/decoder/tinyh264/index.d.ts +0 -20
  239. package/esm/decoder/tinyh264/index.d.ts.map +0 -1
  240. package/esm/decoder/tinyh264/index.js +0 -86
  241. package/esm/decoder/tinyh264/index.js.map +0 -1
  242. package/esm/decoder/tinyh264/worker.d.ts +0 -2
  243. package/esm/decoder/tinyh264/worker.d.ts.map +0 -1
  244. package/esm/decoder/tinyh264/worker.js +0 -3
  245. package/esm/decoder/tinyh264/worker.js.map +0 -1
  246. package/esm/decoder/tinyh264/wrapper.d.ts +0 -21
  247. package/esm/decoder/tinyh264/wrapper.d.ts.map +0 -1
  248. package/esm/decoder/tinyh264/wrapper.js +0 -68
  249. package/esm/decoder/tinyh264/wrapper.js.map +0 -1
  250. package/esm/decoder/types.d.ts +0 -28
  251. package/esm/decoder/types.d.ts.map +0 -1
  252. package/esm/decoder/types.js +0 -2
  253. package/esm/decoder/types.js.map +0 -1
  254. package/esm/decoder/web-codecs/index.d.ts +0 -22
  255. package/esm/decoder/web-codecs/index.d.ts.map +0 -1
  256. package/esm/decoder/web-codecs/index.js +0 -81
  257. package/esm/decoder/web-codecs/index.js.map +0 -1
  258. package/esm/message.d.ts +0 -98
  259. package/esm/message.d.ts.map +0 -1
  260. package/esm/message.js +0 -98
  261. package/esm/message.js.map +0 -1
  262. package/esm/options/1_22.d.ts +0 -43
  263. package/esm/options/1_22.d.ts.map +0 -1
  264. package/esm/options/1_22.js +0 -43
  265. package/esm/options/1_22.js.map +0 -1
  266. package/esm/options/common.d.ts +0 -34
  267. package/esm/options/common.d.ts.map +0 -1
  268. package/esm/options/common.js +0 -16
  269. package/esm/options/common.js.map +0 -1
  270. package/esm/push-server.d.ts +0 -6
  271. package/esm/push-server.d.ts.map +0 -1
  272. package/esm/push-server.js +0 -16
  273. package/esm/push-server.js.map +0 -1
  274. package/esm/utils.d.ts +0 -2
  275. package/esm/utils.d.ts.map +0 -1
  276. package/esm/utils.js +0 -6
  277. package/esm/utils.js.map +0 -1
  278. package/src/client.ts +0 -435
  279. package/src/connection.ts +0 -148
  280. package/src/decoder/index.ts +0 -3
  281. package/src/decoder/tinyh264/index.ts +0 -112
  282. package/src/decoder/tinyh264/types.d.ts +0 -137
  283. package/src/decoder/tinyh264/worker.ts +0 -2
  284. package/src/decoder/tinyh264/wrapper.ts +0 -89
  285. package/src/decoder/types.ts +0 -35
  286. package/src/decoder/web-codecs/index.ts +0 -99
  287. package/src/message.ts +0 -113
  288. package/src/options/1_16/sps.ts +0 -300
  289. package/src/options/1_22.ts +0 -78
  290. package/src/options/common.ts +0 -66
  291. package/src/push-server.ts +0 -24
  292. package/src/utils.ts +0 -5
@@ -0,0 +1,192 @@
1
+ import { type Adb } from "@yume-chan/adb";
2
+ import { delay } from "@yume-chan/async";
3
+ import { type Disposable } from "@yume-chan/event";
4
+ import {
5
+ TransformStream,
6
+ type ReadableStream,
7
+ type ReadableStreamDefaultReader,
8
+ type ReadableWritablePair,
9
+ } from "@yume-chan/stream-extra";
10
+ import { type ValueOrPromise } from "@yume-chan/struct";
11
+
12
+ export interface AdbScrcpyConnectionOptions {
13
+ control: boolean;
14
+
15
+ /**
16
+ * Write a byte on start to detect connection issues
17
+ */
18
+ sendDummyByte: boolean;
19
+
20
+ /**
21
+ * Send device name and size
22
+ */
23
+ sendDeviceMeta: boolean;
24
+ }
25
+
26
+ export abstract class AdbScrcpyConnection implements Disposable {
27
+ protected adb: Adb;
28
+
29
+ protected options: AdbScrcpyConnectionOptions;
30
+
31
+ public constructor(adb: Adb, options: AdbScrcpyConnectionOptions) {
32
+ this.adb = adb;
33
+ this.options = options;
34
+ }
35
+
36
+ public initialize(): ValueOrPromise<void> {
37
+ // do nothing
38
+ }
39
+
40
+ public abstract getStreams(): ValueOrPromise<
41
+ [
42
+ videoSteam: ReadableStream<Uint8Array>,
43
+ controlStream:
44
+ | ReadableWritablePair<Uint8Array, Uint8Array>
45
+ | undefined
46
+ ]
47
+ >;
48
+
49
+ public dispose(): void {
50
+ // do nothing
51
+ }
52
+ }
53
+
54
+ export class AdbScrcpyForwardConnection extends AdbScrcpyConnection {
55
+ private connect(): Promise<ReadableWritablePair<Uint8Array, Uint8Array>> {
56
+ return this.adb.createSocket("localabstract:scrcpy");
57
+ }
58
+
59
+ private async connectAndRetry(): Promise<
60
+ ReadableWritablePair<Uint8Array, Uint8Array>
61
+ > {
62
+ for (let i = 0; i < 100; i += 1) {
63
+ try {
64
+ return await this.connect();
65
+ } catch (e) {
66
+ await delay(100);
67
+ }
68
+ }
69
+ throw new Error(`Can't connect to server after 100 retries`);
70
+ }
71
+
72
+ private async connectVideoStream(): Promise<ReadableStream<Uint8Array>> {
73
+ const { readable: videoStream } = await this.connectAndRetry();
74
+ if (this.options.sendDummyByte) {
75
+ const reader = videoStream.getReader();
76
+ const { done, value } = await reader.read();
77
+ // server will write a `0` to signal connection success
78
+ if (done || value.byteLength !== 1 || value[0] !== 0) {
79
+ throw new Error("Unexpected response from server");
80
+ }
81
+ reader.releaseLock();
82
+ }
83
+ return videoStream;
84
+ }
85
+
86
+ public async getStreams(): Promise<
87
+ [
88
+ videoSteam: ReadableStream<Uint8Array>,
89
+ controlStream:
90
+ | ReadableWritablePair<Uint8Array, Uint8Array>
91
+ | undefined
92
+ ]
93
+ > {
94
+ const videoStream = await this.connectVideoStream();
95
+
96
+ let controlStream:
97
+ | ReadableWritablePair<Uint8Array, Uint8Array>
98
+ | undefined;
99
+ if (this.options.control) {
100
+ controlStream = await this.connectAndRetry();
101
+ }
102
+
103
+ // Server only writes device meta after control socket is connected (if enabled)
104
+ if (this.options.sendDeviceMeta) {
105
+ const reader = videoStream.getReader();
106
+ const { done, value } = await reader.read();
107
+ // 64 bytes device name + 2 bytes video width + 2 bytes video height
108
+ if (done || value.byteLength !== 64 + 2 + 2) {
109
+ throw new Error("Unexpected response from server");
110
+ }
111
+ reader.releaseLock();
112
+ }
113
+
114
+ return [videoStream, controlStream];
115
+ }
116
+ }
117
+
118
+ export class AdbScrcpyReverseConnection extends AdbScrcpyConnection {
119
+ private streams!: ReadableStreamDefaultReader<
120
+ ReadableWritablePair<Uint8Array, Uint8Array>
121
+ >;
122
+
123
+ private address!: string;
124
+
125
+ public override async initialize(): Promise<void> {
126
+ // try to unbind first, ignore errors
127
+ await this.adb.reverse.remove("localabstract:scrcpy").catch((e) => {
128
+ void e;
129
+ });
130
+
131
+ const queue = new TransformStream<
132
+ ReadableWritablePair<Uint8Array, Uint8Array>,
133
+ ReadableWritablePair<Uint8Array, Uint8Array>
134
+ >();
135
+ this.streams = queue.readable.getReader();
136
+ const writer = queue.writable.getWriter();
137
+ this.address = await this.adb.reverse.add(
138
+ "localabstract:scrcpy",
139
+ "tcp:27183",
140
+ (socket) => {
141
+ void writer.write(socket);
142
+ return true;
143
+ }
144
+ );
145
+ }
146
+
147
+ private async accept(): Promise<
148
+ ReadableWritablePair<Uint8Array, Uint8Array>
149
+ > {
150
+ return (await this.streams.read()).value!;
151
+ }
152
+
153
+ public async getStreams(): Promise<
154
+ [
155
+ videoSteam: ReadableStream<Uint8Array>,
156
+ controlStream:
157
+ | ReadableWritablePair<Uint8Array, Uint8Array>
158
+ | undefined
159
+ ]
160
+ > {
161
+ const { readable: videoStream } = await this.accept();
162
+
163
+ let controlStream:
164
+ | ReadableWritablePair<Uint8Array, Uint8Array>
165
+ | undefined;
166
+ if (this.options.control) {
167
+ controlStream = await this.accept();
168
+ }
169
+
170
+ // Server only writes device meta after control socket is connected (if enabled)
171
+ if (this.options.sendDeviceMeta) {
172
+ const reader = videoStream.getReader();
173
+ const { done, value } = await reader.read();
174
+ // 64 bytes device name + 2 bytes video width + 2 bytes video height
175
+ if (done || value.byteLength !== 64 + 2 + 2) {
176
+ throw new Error("Unexpected response from server");
177
+ }
178
+ reader.releaseLock();
179
+ }
180
+
181
+ return [videoStream, controlStream];
182
+ }
183
+
184
+ public override dispose() {
185
+ // Don't await this!
186
+ // `reverse.remove`'s response will never arrive
187
+ // before we read all pending data from `videoStream`
188
+ this.adb.reverse.remove(this.address).catch((e) => {
189
+ void e;
190
+ });
191
+ }
192
+ }
@@ -0,0 +1,3 @@
1
+ export * from './options/index.js';
2
+ export * from './client.js';
3
+ export * from './connection.js';
@@ -0,0 +1,30 @@
1
+ import { type Adb } from "@yume-chan/adb";
2
+
3
+ import { type ScrcpyOptionsInit1_16 } from "../../options/index.js";
4
+ import {
5
+ AdbScrcpyForwardConnection,
6
+ AdbScrcpyReverseConnection,
7
+ type AdbScrcpyConnection,
8
+ type AdbScrcpyConnectionOptions,
9
+ } from "../connection.js";
10
+
11
+ import { AdbScrcpyOptionsBase } from "./types.js";
12
+
13
+ export class AdbScrcpyOptions1_16<
14
+ T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsInit1_16
15
+ > extends AdbScrcpyOptionsBase<T> {
16
+ public override createConnection(adb: Adb): AdbScrcpyConnection {
17
+ const options: AdbScrcpyConnectionOptions = {
18
+ // Old versions always have control stream no matter what the option is
19
+ // Pass `control: false` to `Connection` will disable the control stream
20
+ control: true,
21
+ sendDummyByte: true,
22
+ sendDeviceMeta: true,
23
+ };
24
+ if (this.value.tunnelForward) {
25
+ return new AdbScrcpyForwardConnection(adb, options);
26
+ } else {
27
+ return new AdbScrcpyReverseConnection(adb, options);
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,26 @@
1
+ import { type Adb } from "@yume-chan/adb";
2
+
3
+ import { type ScrcpyOptionsInit1_22 } from "../../options/index.js";
4
+ import {
5
+ type AdbScrcpyConnection,
6
+ AdbScrcpyForwardConnection,
7
+ AdbScrcpyReverseConnection,
8
+ } from "../connection.js";
9
+
10
+ import { AdbScrcpyOptions1_16 } from "./1_16.js";
11
+
12
+ export class AdbScrcpyOptions1_22<
13
+ T extends ScrcpyOptionsInit1_22 = ScrcpyOptionsInit1_22
14
+ > extends AdbScrcpyOptions1_16<T> {
15
+ public override createConnection(adb: Adb): AdbScrcpyConnection {
16
+ const options = {
17
+ ...this.getDefaultValue(),
18
+ ...this.value,
19
+ };
20
+ if (this.value.tunnelForward) {
21
+ return new AdbScrcpyForwardConnection(adb, options);
22
+ } else {
23
+ return new AdbScrcpyReverseConnection(adb, options);
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,3 @@
1
+ export * from './1_16.js';
2
+ export * from './1_22.js';
3
+ export * from './types.js';
@@ -0,0 +1,75 @@
1
+ import { type Adb } from "@yume-chan/adb";
2
+ import { type TransformStream } from "@yume-chan/stream-extra";
3
+
4
+ import {
5
+ type ScrcpyBackOrScreenOnControlMessage,
6
+ type ScrcpyControlMessageType,
7
+ type ScrcpySetClipboardControlMessage,
8
+ } from "../../control/index.js";
9
+ import {
10
+ type ScrcpyOptions,
11
+ type ScrcpyVideoStreamPacket,
12
+ } from "../../options/index.js";
13
+ import { type AdbScrcpyConnection } from "../connection.js";
14
+
15
+ export interface AdbScrcpyOptions<T extends object> extends ScrcpyOptions<T> {
16
+ createConnection(adb: Adb): AdbScrcpyConnection;
17
+ }
18
+
19
+ export abstract class AdbScrcpyOptionsBase<T extends object>
20
+ implements ScrcpyOptions<T>
21
+ {
22
+ private raw: ScrcpyOptions<T>;
23
+
24
+ public get value(): Partial<T> {
25
+ return this.raw.value;
26
+ }
27
+ public set value(value: Partial<T>) {
28
+ this.raw.value = value;
29
+ }
30
+
31
+ public constructor(raw: ScrcpyOptions<T>) {
32
+ this.raw = raw;
33
+ }
34
+
35
+ public getDefaultValue(): T {
36
+ return this.raw.getDefaultValue();
37
+ }
38
+
39
+ public formatServerArguments(): string[] {
40
+ return this.raw.formatServerArguments();
41
+ }
42
+
43
+ public getOutputEncoderNameRegex(): RegExp {
44
+ return this.raw.getOutputEncoderNameRegex();
45
+ }
46
+
47
+ public createVideoStreamTransformer(): TransformStream<
48
+ Uint8Array,
49
+ ScrcpyVideoStreamPacket
50
+ > {
51
+ return this.raw.createVideoStreamTransformer();
52
+ }
53
+
54
+ public getControlMessageTypes(): ScrcpyControlMessageType[] {
55
+ return this.raw.getControlMessageTypes();
56
+ }
57
+
58
+ public serializeBackOrScreenOnControlMessage(
59
+ message: ScrcpyBackOrScreenOnControlMessage
60
+ ): Uint8Array | undefined {
61
+ return this.raw.serializeBackOrScreenOnControlMessage(message);
62
+ }
63
+
64
+ public serializeSetClipboardControlMessage(
65
+ message: ScrcpySetClipboardControlMessage
66
+ ): Uint8Array {
67
+ return this.raw.serializeSetClipboardControlMessage(message);
68
+ }
69
+
70
+ public getScrollController() {
71
+ return this.raw.getScrollController();
72
+ }
73
+
74
+ public abstract createConnection(adb: Adb): AdbScrcpyConnection;
75
+ }
@@ -0,0 +1,4 @@
1
+ import { type ScrcpyBackOrScreenOnControlMessage1_18 } from "../index.js";
2
+
3
+ export type ScrcpyBackOrScreenOnControlMessage =
4
+ ScrcpyBackOrScreenOnControlMessage1_18;
@@ -0,0 +1,56 @@
1
+ import {
2
+ AndroidMotionEventAction,
3
+ type ScrcpyInjectTouchControlMessage,
4
+ } from "./inject-touch.js";
5
+ import { ScrcpyControlMessageType } from "./type.js";
6
+
7
+ /**
8
+ * On both Android and Windows, while both mouse and touch are supported input devices,
9
+ * only one of them can be active at a time. Touch the screen with a finger will deactivate mouse,
10
+ * and move the mouse will deactivate touch.
11
+ *
12
+ * On Android, this is achieved by dispatching a `MotionEvent.ACTION_UP` event for the previous input type.
13
+ * But on Chrome, there is no such event, causing both mouse and touch to be active at the same time.
14
+ * This can cause the new input to appear as "ignored".
15
+ *
16
+ * This helper class synthesis `ACTION_UP` events when a different pointer type appears,
17
+ * so Scrcpy server can remove the previously hovering pointer.
18
+ */
19
+ export class ScrcpyHoverHelper {
20
+ // AFAIK, only mouse and pen can have hover state
21
+ // and you can't have two mouses or pens.
22
+ // So remember the last hovering pointer is enough.
23
+ private lastHoverMessage: ScrcpyInjectTouchControlMessage | undefined;
24
+
25
+ public process(
26
+ message: Omit<ScrcpyInjectTouchControlMessage, "type">
27
+ ): ScrcpyInjectTouchControlMessage[] {
28
+ const result: ScrcpyInjectTouchControlMessage[] = [];
29
+
30
+ // A different pointer appeared,
31
+ // Cancel previously hovering pointer so Scrcpy server can free up the pointer ID.
32
+ if (
33
+ this.lastHoverMessage &&
34
+ this.lastHoverMessage.pointerId !== message.pointerId
35
+ ) {
36
+ // TODO: Inject MotionEvent.ACTION_HOVER_EXIT
37
+ // From testing, it seems no App cares about this event.
38
+ result.push({
39
+ ...this.lastHoverMessage,
40
+ action: AndroidMotionEventAction.Up,
41
+ });
42
+ this.lastHoverMessage = undefined;
43
+ }
44
+
45
+ if (message.action === AndroidMotionEventAction.HoverMove) {
46
+ // TODO: Inject MotionEvent.ACTION_HOVER_ENTER
47
+ this.lastHoverMessage = message as ScrcpyInjectTouchControlMessage;
48
+ }
49
+
50
+ (message as ScrcpyInjectTouchControlMessage).type =
51
+ ScrcpyControlMessageType.InjectTouch;
52
+ result.push(message as ScrcpyInjectTouchControlMessage);
53
+
54
+ return result;
55
+ }
56
+ }
@@ -0,0 +1,11 @@
1
+ export * from "./back-or-screen-on.js";
2
+ export * from "./hover-helper.js";
3
+ export * from "./inject-keycode.js";
4
+ export * from "./inject-scroll.js";
5
+ export * from "./inject-text.js";
6
+ export * from "./inject-touch.js";
7
+ export * from "./rotate-device.js";
8
+ export * from "./serializer.js";
9
+ export * from "./set-clipboard.js";
10
+ export * from "./set-screen-power-mode.js";
11
+ export * from "./type.js";
@@ -0,0 +1,72 @@
1
+ import Struct, { placeholder } from "@yume-chan/struct";
2
+
3
+ import { ScrcpyControlMessageType } from "./type.js";
4
+
5
+ export enum AndroidKeyEventAction {
6
+ Down = 0,
7
+ Up = 1,
8
+ }
9
+
10
+ // https://github.com/Genymobile/scrcpy/blob/cabb102a04153f9c237b18257d3269a058558c63/app/src/android/keycodes.h#L26
11
+ export enum AndroidKeyCode {
12
+ Home = 3,
13
+ Back = 4,
14
+
15
+ DPadUp = 19,
16
+ DPadDown,
17
+ DPadLeft,
18
+ DPadRight,
19
+
20
+ VolumeUp = 24,
21
+ VolumeDown,
22
+
23
+ A = 29,
24
+ B,
25
+ C,
26
+ D,
27
+ E,
28
+ F,
29
+ G,
30
+ H,
31
+ I,
32
+ J,
33
+ K,
34
+ L,
35
+ M,
36
+ N,
37
+ O,
38
+ P,
39
+ Q,
40
+ R,
41
+ S,
42
+ T,
43
+ U,
44
+ V,
45
+ W,
46
+ X,
47
+ Y,
48
+ Z,
49
+
50
+ Tab = 61,
51
+ Space,
52
+ Enter = 66,
53
+ Delete,
54
+ Escape = 111,
55
+ ForwardDelete,
56
+
57
+ MoveHome = 122,
58
+ MoveEnd = 123,
59
+
60
+ VolumeMute = 164,
61
+ AppSwitch = 187,
62
+ }
63
+
64
+ export const ScrcpyInjectKeyCodeControlMessage = new Struct()
65
+ .uint8("type", ScrcpyControlMessageType.InjectKeyCode as const)
66
+ .uint8("action", placeholder<AndroidKeyEventAction>())
67
+ .uint32("keyCode")
68
+ .uint32("repeat")
69
+ .uint32("metaState");
70
+
71
+ export type ScrcpyInjectKeyCodeControlMessage =
72
+ typeof ScrcpyInjectKeyCodeControlMessage["TInit"];
@@ -0,0 +1,4 @@
1
+ import { type ScrcpyInjectScrollControlMessage1_25 } from "../options/index.js";
2
+
3
+ export type ScrcpyInjectScrollControlMessage =
4
+ ScrcpyInjectScrollControlMessage1_25;
@@ -0,0 +1,12 @@
1
+ import Struct from '@yume-chan/struct';
2
+
3
+ import { ScrcpyControlMessageType } from './type.js';
4
+
5
+ export const ScrcpyInjectTextControlMessage =
6
+ new Struct()
7
+ .uint8('type', ScrcpyControlMessageType.InjectText as const)
8
+ .uint32('length')
9
+ .string('text', { lengthField: 'length' });
10
+
11
+ export type ScrcpyInjectTextControlMessage =
12
+ typeof ScrcpyInjectTextControlMessage['TInit'];
@@ -0,0 +1,78 @@
1
+ import Struct, {
2
+ NumberFieldDefinition,
3
+ NumberFieldType,
4
+ placeholder,
5
+ } from "@yume-chan/struct";
6
+
7
+ import { ScrcpyControlMessageType } from "./type.js";
8
+
9
+ // https://developer.android.com/reference/android/view/MotionEvent#constants_1
10
+ export enum AndroidMotionEventAction {
11
+ Down,
12
+ Up,
13
+ Move,
14
+ Cancel,
15
+ Outside,
16
+ PointerDown,
17
+ PointerUp,
18
+ HoverMove,
19
+ Scroll,
20
+ HoverEnter,
21
+ HoverExit,
22
+ ButtonPress,
23
+ ButtonRelease,
24
+ }
25
+
26
+ // eslint-disable-next-line @typescript-eslint/no-namespace
27
+ export namespace ScrcpyPointerId {
28
+ export const Mouse = BigInt(-1);
29
+ export const Finger = BigInt(-2);
30
+ export const VirtualMouse = BigInt(-3);
31
+ export const VirtualFinger = BigInt(-4);
32
+ }
33
+
34
+ export function clamp(value: number, min: number, max: number): number {
35
+ if (value < min) {
36
+ return min;
37
+ }
38
+
39
+ if (value > max) {
40
+ return max;
41
+ }
42
+
43
+ return value;
44
+ }
45
+
46
+ export const ScrcpyFloatToUint16NumberType: NumberFieldType = {
47
+ size: 2,
48
+ signed: false,
49
+ deserialize(array, littleEndian) {
50
+ const value = NumberFieldType.Uint16.deserialize(array, littleEndian);
51
+ // https://github.com/Genymobile/scrcpy/blob/1f138aef41de651668043b32c4effc2d4adbfc44/server/src/main/java/com/genymobile/scrcpy/Binary.java#L22
52
+ return value === 0xffff ? 1 : value / 0x10000;
53
+ },
54
+ serialize(dataView, offset, value, littleEndian) {
55
+ // https://github.com/Genymobile/scrcpy/blob/1f138aef41de651668043b32c4effc2d4adbfc44/app/src/util/binary.h#L51
56
+ value = clamp(value, -1, 1);
57
+ value = value === 1 ? 0xffff : value * 0x10000;
58
+ NumberFieldType.Uint16.serialize(dataView, offset, value, littleEndian);
59
+ },
60
+ };
61
+
62
+ const ScrcpyFloatToUint16FieldDefinition = new NumberFieldDefinition(
63
+ ScrcpyFloatToUint16NumberType
64
+ );
65
+
66
+ export const ScrcpyInjectTouchControlMessage = new Struct()
67
+ .uint8("type", ScrcpyControlMessageType.InjectTouch as const)
68
+ .uint8("action", placeholder<AndroidMotionEventAction>())
69
+ .uint64("pointerId")
70
+ .uint32("pointerX")
71
+ .uint32("pointerY")
72
+ .uint16("screenWidth")
73
+ .uint16("screenHeight")
74
+ .field("pressure", ScrcpyFloatToUint16FieldDefinition)
75
+ .uint32("buttons");
76
+
77
+ export type ScrcpyInjectTouchControlMessage =
78
+ typeof ScrcpyInjectTouchControlMessage["TInit"];
@@ -0,0 +1,10 @@
1
+ import Struct from '@yume-chan/struct';
2
+
3
+ import { ScrcpyControlMessageType } from './type.js';
4
+
5
+ export const ScrcpyRotateDeviceControlMessage =
6
+ new Struct()
7
+ .uint8('type', ScrcpyControlMessageType.RotateDevice as const)
8
+
9
+ export type ScrcpyRotateDeviceControlMessage =
10
+ typeof ScrcpyRotateDeviceControlMessage['TInit'];