@yume-chan/scrcpy 0.0.17 → 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 (214) hide show
  1. package/CHANGELOG.json +21 -3
  2. package/CHANGELOG.md +13 -4
  3. package/LICENSE +1 -1
  4. package/README.md +41 -24
  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 +15 -14
  9. package/esm/adb/client.d.ts.map +1 -1
  10. package/esm/adb/client.js +66 -41
  11. package/esm/adb/client.js.map +1 -1
  12. package/esm/adb/connection.d.ts +16 -7
  13. package/esm/adb/connection.d.ts.map +1 -1
  14. package/esm/adb/connection.js +21 -13
  15. package/esm/adb/connection.js.map +1 -1
  16. package/esm/adb/options/1_16.d.ts +4 -4
  17. package/esm/adb/options/1_16.d.ts.map +1 -1
  18. package/esm/adb/options/1_16.js +2 -2
  19. package/esm/adb/options/1_16.js.map +1 -1
  20. package/esm/adb/options/1_22.d.ts +4 -4
  21. package/esm/adb/options/1_22.d.ts.map +1 -1
  22. package/esm/adb/options/1_22.js +2 -2
  23. package/esm/adb/options/1_22.js.map +1 -1
  24. package/esm/adb/options/types.d.ts +10 -9
  25. package/esm/adb/options/types.d.ts.map +1 -1
  26. package/esm/adb/options/types.js +11 -4
  27. package/esm/adb/options/types.js.map +1 -1
  28. package/esm/control/back-or-screen-on.d.ts +3 -0
  29. package/esm/control/back-or-screen-on.d.ts.map +1 -0
  30. package/esm/control/back-or-screen-on.js +2 -0
  31. package/esm/control/back-or-screen-on.js.map +1 -0
  32. package/esm/control/hover-helper.d.ts +18 -0
  33. package/esm/control/hover-helper.d.ts.map +1 -0
  34. package/esm/control/hover-helper.js +44 -0
  35. package/esm/control/hover-helper.js.map +1 -0
  36. package/esm/control/index.d.ts +11 -7
  37. package/esm/control/index.d.ts.map +1 -1
  38. package/esm/control/index.js +11 -7
  39. package/esm/control/index.js.map +1 -1
  40. package/esm/control/inject-keycode.d.ts +12 -4
  41. package/esm/control/inject-keycode.d.ts.map +1 -1
  42. package/esm/control/inject-keycode.js +15 -7
  43. package/esm/control/inject-keycode.js.map +1 -1
  44. package/esm/control/inject-scroll.d.ts +3 -0
  45. package/esm/control/inject-scroll.d.ts.map +1 -0
  46. package/esm/control/inject-scroll.js +2 -0
  47. package/esm/control/inject-scroll.js.map +1 -0
  48. package/esm/control/inject-text.d.ts +2 -2
  49. package/esm/control/inject-text.d.ts.map +1 -1
  50. package/esm/control/inject-touch.d.ts +12 -4
  51. package/esm/control/inject-touch.d.ts.map +1 -1
  52. package/esm/control/inject-touch.js +44 -11
  53. package/esm/control/inject-touch.js.map +1 -1
  54. package/esm/control/rotate-device.d.ts +2 -2
  55. package/esm/control/rotate-device.d.ts.map +1 -1
  56. package/esm/control/serializer.d.ts +22 -11
  57. package/esm/control/serializer.d.ts.map +1 -1
  58. package/esm/control/serializer.js +32 -26
  59. package/esm/control/serializer.js.map +1 -1
  60. package/esm/control/set-clipboard.d.ts +3 -0
  61. package/esm/control/set-clipboard.d.ts.map +1 -0
  62. package/esm/control/set-clipboard.js +2 -0
  63. package/esm/control/set-clipboard.js.map +1 -0
  64. package/esm/control/set-screen-power-mode.d.ts +2 -2
  65. package/esm/control/set-screen-power-mode.d.ts.map +1 -1
  66. package/esm/device-message/ack-clipboard.d.ts +9 -0
  67. package/esm/device-message/ack-clipboard.d.ts.map +1 -0
  68. package/esm/device-message/ack-clipboard.js +6 -0
  69. package/esm/device-message/ack-clipboard.js.map +1 -0
  70. package/esm/device-message/clipboard.d.ts +3 -3
  71. package/esm/device-message/clipboard.d.ts.map +1 -1
  72. package/esm/device-message/clipboard.js +4 -4
  73. package/esm/device-message/clipboard.js.map +1 -1
  74. package/esm/device-message/index.d.ts +4 -3
  75. package/esm/device-message/index.d.ts.map +1 -1
  76. package/esm/device-message/index.js +4 -3
  77. package/esm/device-message/index.js.map +1 -1
  78. package/esm/device-message/stream.d.ts +4 -3
  79. package/esm/device-message/stream.d.ts.map +1 -1
  80. package/esm/device-message/stream.js +8 -4
  81. package/esm/device-message/stream.js.map +1 -1
  82. package/esm/options/1_16/codec-options.d.ts +3 -2
  83. package/esm/options/1_16/codec-options.d.ts.map +1 -1
  84. package/esm/options/1_16/codec-options.js +6 -7
  85. package/esm/options/1_16/codec-options.js.map +1 -1
  86. package/esm/options/1_16/h264-configuration.d.ts +49 -0
  87. package/esm/options/1_16/h264-configuration.d.ts.map +1 -0
  88. package/esm/options/1_16/h264-configuration.js +414 -0
  89. package/esm/options/1_16/h264-configuration.js.map +1 -0
  90. package/esm/options/1_16/index.d.ts +4 -3
  91. package/esm/options/1_16/index.d.ts.map +1 -1
  92. package/esm/options/1_16/index.js +4 -3
  93. package/esm/options/1_16/index.js.map +1 -1
  94. package/esm/options/1_16/options.d.ts +17 -20
  95. package/esm/options/1_16/options.d.ts.map +1 -1
  96. package/esm/options/1_16/options.js +51 -49
  97. package/esm/options/1_16/options.js.map +1 -1
  98. package/esm/options/1_16/scroll.d.ts +27 -0
  99. package/esm/options/1_16/scroll.d.ts.map +1 -0
  100. package/esm/options/1_16/scroll.js +56 -0
  101. package/esm/options/1_16/scroll.js.map +1 -0
  102. package/esm/options/1_16/sps.d.ts +1 -1
  103. package/esm/options/1_16/sps.d.ts.map +1 -1
  104. package/esm/options/1_16/sps.js +25 -17
  105. package/esm/options/1_16/sps.js.map +1 -1
  106. package/esm/options/1_18.d.ts +6 -6
  107. package/esm/options/1_18.d.ts.map +1 -1
  108. package/esm/options/1_18.js +5 -5
  109. package/esm/options/1_18.js.map +1 -1
  110. package/esm/options/1_21.d.ts +12 -1
  111. package/esm/options/1_21.d.ts.map +1 -1
  112. package/esm/options/1_21.js +14 -3
  113. package/esm/options/1_21.js.map +1 -1
  114. package/esm/options/1_22/index.d.ts +3 -0
  115. package/esm/options/1_22/index.d.ts.map +1 -0
  116. package/esm/options/1_22/index.js +3 -0
  117. package/esm/options/1_22/index.js.map +1 -0
  118. package/esm/options/{1_22.d.ts → 1_22/options.d.ts} +4 -15
  119. package/esm/options/1_22/options.d.ts.map +1 -0
  120. package/esm/options/{1_22.js → 1_22/options.js} +5 -9
  121. package/esm/options/1_22/options.js.map +1 -0
  122. package/esm/options/1_22/scroll.d.ts +17 -0
  123. package/esm/options/1_22/scroll.d.ts.map +1 -0
  124. package/esm/options/1_22/scroll.js +15 -0
  125. package/esm/options/1_22/scroll.js.map +1 -0
  126. package/esm/options/1_23.d.ts +3 -3
  127. package/esm/options/1_23.d.ts.map +1 -1
  128. package/esm/options/1_23.js +7 -6
  129. package/esm/options/1_23.js.map +1 -1
  130. package/esm/options/1_25/index.d.ts +3 -0
  131. package/esm/options/1_25/index.d.ts.map +1 -0
  132. package/esm/options/1_25/index.js +3 -0
  133. package/esm/options/1_25/index.js.map +1 -0
  134. package/esm/options/1_25/options.d.ts +6 -0
  135. package/esm/options/1_25/options.d.ts.map +1 -0
  136. package/esm/options/1_25/options.js +8 -0
  137. package/esm/options/1_25/options.js.map +1 -0
  138. package/esm/options/1_25/scroll.d.ts +19 -0
  139. package/esm/options/1_25/scroll.d.ts.map +1 -0
  140. package/esm/options/1_25/scroll.js +33 -0
  141. package/esm/options/1_25/scroll.js.map +1 -0
  142. package/esm/options/index.d.ts +5 -4
  143. package/esm/options/index.d.ts.map +1 -1
  144. package/esm/options/index.js +5 -4
  145. package/esm/options/index.js.map +1 -1
  146. package/esm/options/types.d.ts +14 -12
  147. package/esm/options/types.d.ts.map +1 -1
  148. package/esm/options/types.js +6 -4
  149. package/esm/options/types.js.map +1 -1
  150. package/package.json +19 -12
  151. package/scripts/fetch-server.cjs +19 -9
  152. package/src/adb/client.ts +142 -89
  153. package/src/adb/connection.ts +77 -30
  154. package/src/adb/options/1_16.ts +13 -6
  155. package/src/adb/options/1_22.ts +12 -6
  156. package/src/adb/options/types.ts +34 -14
  157. package/src/control/back-or-screen-on.ts +4 -0
  158. package/src/control/hover-helper.ts +56 -0
  159. package/src/control/index.ts +11 -7
  160. package/src/control/inject-keycode.ts +23 -10
  161. package/src/control/inject-scroll.ts +4 -0
  162. package/src/control/inject-touch.ts +58 -14
  163. package/src/control/serializer.ts +106 -41
  164. package/src/control/set-clipboard.ts +4 -0
  165. package/src/device-message/ack-clipboard.ts +10 -0
  166. package/src/device-message/clipboard.ts +8 -8
  167. package/src/device-message/index.ts +4 -3
  168. package/src/device-message/stream.ts +17 -7
  169. package/src/options/1_16/codec-options.ts +17 -10
  170. package/src/options/1_16/h264-configuration.ts +485 -0
  171. package/src/options/1_16/index.ts +4 -3
  172. package/src/options/1_16/options.ts +175 -130
  173. package/src/options/1_16/scroll.ts +76 -0
  174. package/src/options/1_18.ts +22 -12
  175. package/src/options/1_21.ts +31 -13
  176. package/src/options/1_22/index.ts +2 -0
  177. package/src/options/{1_22.ts → 1_22/options.ts} +8 -17
  178. package/src/options/1_22/scroll.ts +26 -0
  179. package/src/options/1_23.ts +32 -20
  180. package/src/options/1_25/index.ts +2 -0
  181. package/src/options/1_25/options.ts +12 -0
  182. package/src/options/1_25/scroll.ts +52 -0
  183. package/src/options/index.ts +5 -4
  184. package/src/options/types.ts +34 -17
  185. package/tsconfig.build.json +1 -1
  186. package/tsconfig.build.tsbuildinfo +1 -1
  187. package/esm/decoder/index.d.ts +0 -28
  188. package/esm/decoder/index.d.ts.map +0 -1
  189. package/esm/decoder/index.js +0 -2
  190. package/esm/decoder/index.js.map +0 -1
  191. package/esm/decoder/tinyh264/index.d.ts +0 -20
  192. package/esm/decoder/tinyh264/index.d.ts.map +0 -1
  193. package/esm/decoder/tinyh264/index.js +0 -86
  194. package/esm/decoder/tinyh264/index.js.map +0 -1
  195. package/esm/decoder/tinyh264/worker.d.ts +0 -2
  196. package/esm/decoder/tinyh264/worker.d.ts.map +0 -1
  197. package/esm/decoder/tinyh264/worker.js +0 -3
  198. package/esm/decoder/tinyh264/worker.js.map +0 -1
  199. package/esm/decoder/tinyh264/wrapper.d.ts +0 -21
  200. package/esm/decoder/tinyh264/wrapper.d.ts.map +0 -1
  201. package/esm/decoder/tinyh264/wrapper.js +0 -74
  202. package/esm/decoder/tinyh264/wrapper.js.map +0 -1
  203. package/esm/decoder/types.d.ts +0 -2
  204. package/esm/decoder/types.d.ts.map +0 -1
  205. package/esm/decoder/types.js +0 -2
  206. package/esm/decoder/types.js.map +0 -1
  207. package/esm/decoder/web-codecs/index.d.ts +0 -22
  208. package/esm/decoder/web-codecs/index.d.ts.map +0 -1
  209. package/esm/decoder/web-codecs/index.js +0 -81
  210. package/esm/decoder/web-codecs/index.js.map +0 -1
  211. package/esm/options/1_22.d.ts.map +0 -1
  212. package/esm/options/1_22.js.map +0 -1
  213. package/src/options/1_16/sps.ts +0 -300
  214. package/tsconfig.test.tsbuildinfo +0 -1
@@ -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
+ }
@@ -1,7 +1,11 @@
1
- export * from './inject-keycode.js';
2
- export * from './inject-text.js';
3
- export * from './inject-touch.js';
4
- export * from './rotate-device.js';
5
- export * from './serializer.js';
6
- export * from './set-screen-power-mode.js';
7
- export * from './type.js';
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";
@@ -1,6 +1,6 @@
1
- import Struct, { placeholder } from '@yume-chan/struct';
1
+ import Struct, { placeholder } from "@yume-chan/struct";
2
2
 
3
- import { ScrcpyControlMessageType } from './type.js';
3
+ import { ScrcpyControlMessageType } from "./type.js";
4
4
 
5
5
  export enum AndroidKeyEventAction {
6
6
  Down = 0,
@@ -11,8 +11,15 @@ export enum AndroidKeyEventAction {
11
11
  export enum AndroidKeyCode {
12
12
  Home = 3,
13
13
  Back = 4,
14
+
15
+ DPadUp = 19,
16
+ DPadDown,
17
+ DPadLeft,
18
+ DPadRight,
19
+
14
20
  VolumeUp = 24,
15
21
  VolumeDown,
22
+
16
23
  A = 29,
17
24
  B,
18
25
  C,
@@ -39,21 +46,27 @@ export enum AndroidKeyCode {
39
46
  X,
40
47
  Y,
41
48
  Z,
49
+
42
50
  Tab = 61,
43
51
  Space,
44
52
  Enter = 66,
45
53
  Delete,
54
+ Escape = 111,
55
+ ForwardDelete,
56
+
57
+ MoveHome = 122,
58
+ MoveEnd = 123,
59
+
46
60
  VolumeMute = 164,
47
61
  AppSwitch = 187,
48
62
  }
49
63
 
50
- export const ScrcpyInjectKeyCodeControlMessage =
51
- new Struct()
52
- .uint8('type', ScrcpyControlMessageType.InjectKeyCode as const)
53
- .uint8('action', placeholder<AndroidKeyEventAction>())
54
- .uint32('keyCode')
55
- .uint32('repeat')
56
- .uint32('metaState');
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");
57
70
 
58
71
  export type ScrcpyInjectKeyCodeControlMessage =
59
- typeof ScrcpyInjectKeyCodeControlMessage['TInit'];
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;
@@ -1,5 +1,10 @@
1
- import Struct, { placeholder } from '@yume-chan/struct';
2
- import { ScrcpyControlMessageType } from './type.js';
1
+ import Struct, {
2
+ NumberFieldDefinition,
3
+ NumberFieldType,
4
+ placeholder,
5
+ } from "@yume-chan/struct";
6
+
7
+ import { ScrcpyControlMessageType } from "./type.js";
3
8
 
4
9
  // https://developer.android.com/reference/android/view/MotionEvent#constants_1
5
10
  export enum AndroidMotionEventAction {
@@ -18,17 +23,56 @@ export enum AndroidMotionEventAction {
18
23
  ButtonRelease,
19
24
  }
20
25
 
21
- export const ScrcpyInjectTouchControlMessage =
22
- new Struct()
23
- .uint8('type', ScrcpyControlMessageType.InjectTouch as const)
24
- .uint8('action', placeholder<AndroidMotionEventAction>())
25
- .uint64('pointerId')
26
- .uint32('pointerX')
27
- .uint32('pointerY')
28
- .uint16('screenWidth')
29
- .uint16('screenHeight')
30
- .uint16('pressure')
31
- .uint32('buttons');
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");
32
76
 
33
77
  export type ScrcpyInjectTouchControlMessage =
34
- typeof ScrcpyInjectTouchControlMessage['TInit'];
78
+ typeof ScrcpyInjectTouchControlMessage["TInit"];
@@ -1,65 +1,122 @@
1
- import type { WritableStream, WritableStreamDefaultWriter } from '@yume-chan/stream-extra';
1
+ import {
2
+ type WritableStream,
3
+ type WritableStreamDefaultWriter,
4
+ } from "@yume-chan/stream-extra";
2
5
 
3
- import type { ScrcpyInjectScrollControlMessage1_22, ScrcpyOptions } from '../options/index.js';
4
- import { AndroidKeyEventAction, ScrcpyInjectKeyCodeControlMessage } from './inject-keycode.js';
5
- import { ScrcpyInjectTextControlMessage } from './inject-text.js';
6
- import { ScrcpyInjectTouchControlMessage } from './inject-touch.js';
7
- import { ScrcpyRotateDeviceControlMessage } from './rotate-device.js';
8
- import { AndroidScreenPowerMode, ScrcpySetScreenPowerModeControlMessage } from './set-screen-power-mode.js';
9
- import { ScrcpyControlMessageType } from './type.js';
6
+ import {
7
+ type ScrcpyOptions,
8
+ type ScrcpyScrollController,
9
+ } from "../options/index.js";
10
+
11
+ import {
12
+ ScrcpyInjectKeyCodeControlMessage,
13
+ type AndroidKeyEventAction,
14
+ } from "./inject-keycode.js";
15
+ import { type ScrcpyInjectScrollControlMessage } from "./inject-scroll.js";
16
+ import { ScrcpyInjectTextControlMessage } from "./inject-text.js";
17
+ import { ScrcpyInjectTouchControlMessage } from "./inject-touch.js";
18
+ import { ScrcpyRotateDeviceControlMessage } from "./rotate-device.js";
19
+ import {
20
+ ScrcpySetScreenPowerModeControlMessage,
21
+ type AndroidScreenPowerMode,
22
+ } from "./set-screen-power-mode.js";
23
+ import { ScrcpyControlMessageType } from "./type.js";
10
24
 
11
25
  export class ScrcpyControlMessageSerializer {
12
- private options: ScrcpyOptions<any>;
26
+ private options: ScrcpyOptions<object>;
13
27
  /** Control message type values for current version of server */
14
28
  private types: ScrcpyControlMessageType[];
15
29
  private writer: WritableStreamDefaultWriter<Uint8Array>;
30
+ private scrollController: ScrcpyScrollController;
31
+
32
+ public constructor(
33
+ stream: WritableStream<Uint8Array>,
34
+ options: ScrcpyOptions<object>
35
+ ) {
36
+ this.writer = stream.getWriter();
16
37
 
17
- public constructor(stream: WritableStream<Uint8Array>, options: ScrcpyOptions<any>) {
18
38
  this.options = options;
19
39
  this.types = options.getControlMessageTypes();
20
- this.writer = stream.getWriter();
40
+ this.scrollController = options.getScrollController();
21
41
  }
22
42
 
23
- public getTypeValue(type: ScrcpyControlMessageType): number {
43
+ public getActualMessageType(type: ScrcpyControlMessageType): number {
24
44
  const value = this.types.indexOf(type);
25
45
  if (value === -1) {
26
- throw new Error('Not supported');
46
+ throw new Error("Not supported");
27
47
  }
28
48
  return value;
29
49
  }
30
50
 
31
- public injectKeyCode(message: Omit<ScrcpyInjectKeyCodeControlMessage, 'type'>) {
32
- return this.writer.write(ScrcpyInjectKeyCodeControlMessage.serialize({
33
- ...message,
34
- type: this.getTypeValue(ScrcpyControlMessageType.InjectKeyCode),
35
- }));
51
+ public addMessageType<T extends { type: ScrcpyControlMessageType }>(
52
+ message: Omit<T, "type">,
53
+ type: T["type"]
54
+ ): T {
55
+ (message as T).type = this.getActualMessageType(type);
56
+ return message as T;
57
+ }
58
+
59
+ public injectKeyCode(
60
+ message: Omit<ScrcpyInjectKeyCodeControlMessage, "type">
61
+ ) {
62
+ return this.writer.write(
63
+ ScrcpyInjectKeyCodeControlMessage.serialize(
64
+ this.addMessageType(
65
+ message,
66
+ ScrcpyControlMessageType.InjectKeyCode
67
+ )
68
+ )
69
+ );
36
70
  }
37
71
 
38
72
  public injectText(text: string) {
39
- return this.writer.write(ScrcpyInjectTextControlMessage.serialize({
40
- text,
41
- type: this.getTypeValue(ScrcpyControlMessageType.InjectText),
42
- }));
73
+ return this.writer.write(
74
+ ScrcpyInjectTextControlMessage.serialize({
75
+ text,
76
+ type: this.getActualMessageType(
77
+ ScrcpyControlMessageType.InjectText
78
+ ),
79
+ })
80
+ );
43
81
  }
44
82
 
45
- public injectTouch(message: Omit<ScrcpyInjectTouchControlMessage, 'type'>) {
46
- return this.writer.write(ScrcpyInjectTouchControlMessage.serialize({
47
- ...message,
48
- type: this.getTypeValue(ScrcpyControlMessageType.InjectTouch),
49
- }));
83
+ /**
84
+ * `pressure` is a float value between 0 and 1.
85
+ */
86
+ public injectTouch(message: Omit<ScrcpyInjectTouchControlMessage, "type">) {
87
+ return this.writer.write(
88
+ ScrcpyInjectTouchControlMessage.serialize(
89
+ this.addMessageType(
90
+ message,
91
+ ScrcpyControlMessageType.InjectTouch
92
+ )
93
+ )
94
+ );
50
95
  }
51
96
 
52
- public injectScroll(message: Omit<ScrcpyInjectScrollControlMessage1_22, 'type'>) {
53
- return this.writer.write(this.options.serializeInjectScrollControlMessage({
54
- ...message,
55
- type: this.getTypeValue(ScrcpyControlMessageType.InjectScroll),
56
- }));
97
+ /**
98
+ * `scrollX` and `scrollY` are float values between 0 and 1.
99
+ */
100
+ public injectScroll(
101
+ message: Omit<ScrcpyInjectScrollControlMessage, "type">
102
+ ) {
103
+ const data = this.scrollController.serializeScrollMessage(
104
+ this.addMessageType(message, ScrcpyControlMessageType.InjectScroll)
105
+ );
106
+
107
+ if (!data) {
108
+ return;
109
+ }
110
+
111
+ return this.writer.write(data);
57
112
  }
58
113
 
59
114
  public async backOrScreenOn(action: AndroidKeyEventAction) {
60
115
  const buffer = this.options.serializeBackOrScreenOnControlMessage({
61
116
  action,
62
- type: this.getTypeValue(ScrcpyControlMessageType.BackOrScreenOn),
117
+ type: this.getActualMessageType(
118
+ ScrcpyControlMessageType.BackOrScreenOn
119
+ ),
63
120
  });
64
121
 
65
122
  if (buffer) {
@@ -68,19 +125,27 @@ export class ScrcpyControlMessageSerializer {
68
125
  }
69
126
 
70
127
  public setScreenPowerMode(mode: AndroidScreenPowerMode) {
71
- return this.writer.write(ScrcpySetScreenPowerModeControlMessage.serialize({
72
- mode,
73
- type: this.getTypeValue(ScrcpyControlMessageType.SetScreenPowerMode),
74
- }));
128
+ return this.writer.write(
129
+ ScrcpySetScreenPowerModeControlMessage.serialize({
130
+ mode,
131
+ type: this.getActualMessageType(
132
+ ScrcpyControlMessageType.SetScreenPowerMode
133
+ ),
134
+ })
135
+ );
75
136
  }
76
137
 
77
138
  public rotateDevice() {
78
- return this.writer.write(ScrcpyRotateDeviceControlMessage.serialize({
79
- type: this.getTypeValue(ScrcpyControlMessageType.RotateDevice),
80
- }));
139
+ return this.writer.write(
140
+ ScrcpyRotateDeviceControlMessage.serialize({
141
+ type: this.getActualMessageType(
142
+ ScrcpyControlMessageType.RotateDevice
143
+ ),
144
+ })
145
+ );
81
146
  }
82
147
 
83
148
  public close() {
84
149
  return this.writer.close();
85
150
  }
86
- };
151
+ }
@@ -0,0 +1,4 @@
1
+ import { type ScrcpySetClipboardControlMessage1_21 } from "../options/index.js";
2
+
3
+ export type ScrcpySetClipboardControlMessage =
4
+ ScrcpySetClipboardControlMessage1_21;
@@ -0,0 +1,10 @@
1
+ import Struct from "@yume-chan/struct";
2
+
3
+ import { ScrcpyDeviceMessageType } from "./type.js";
4
+
5
+ export const ScrcpyAckClipboardDeviceMessage = new Struct()
6
+ .uint64("sequence")
7
+ .extra({ type: ScrcpyDeviceMessageType.AckClipboard as const });
8
+
9
+ export type ScrcpyAckClipboardDeviceMessage =
10
+ typeof ScrcpyAckClipboardDeviceMessage["TDeserializeResult"];
@@ -1,11 +1,11 @@
1
- import Struct from '@yume-chan/struct';
2
- import { ScrcpyDeviceMessageType } from './type.js';
1
+ import Struct from "@yume-chan/struct";
3
2
 
4
- export const ScrcpyClipboardDeviceMessage =
5
- new Struct()
6
- .uint32('length')
7
- .string('content', { lengthField: 'length' })
8
- .extra({ type: ScrcpyDeviceMessageType.Clipboard as const });
3
+ import { ScrcpyDeviceMessageType } from "./type.js";
4
+
5
+ export const ScrcpyClipboardDeviceMessage = new Struct()
6
+ .uint32("length")
7
+ .string("content", { lengthField: "length" })
8
+ .extra({ type: ScrcpyDeviceMessageType.Clipboard as const });
9
9
 
10
10
  export type ScrcpyClipboardDeviceMessage =
11
- typeof ScrcpyClipboardDeviceMessage['TDeserializeResult'];
11
+ typeof ScrcpyClipboardDeviceMessage["TDeserializeResult"];
@@ -1,3 +1,4 @@
1
- export * from './clipboard.js';
2
- export * from './stream.js';
3
- export * from './type.js';
1
+ export * from "./ack-clipboard.js";
2
+ export * from "./clipboard.js";
3
+ export * from "./stream.js";
4
+ export * from "./type.js";
@@ -1,19 +1,29 @@
1
- import { BufferedTransformStream } from '@yume-chan/stream-extra';
2
- import { ScrcpyClipboardDeviceMessage } from './clipboard.js';
1
+ import { BufferedTransformStream } from "@yume-chan/stream-extra";
2
+
3
+ import { ScrcpyAckClipboardDeviceMessage } from "./ack-clipboard.js";
4
+ import { ScrcpyClipboardDeviceMessage } from "./clipboard.js";
5
+ import { ScrcpyDeviceMessageType } from "./type.js";
3
6
 
4
7
  export type ScrcpyDeviceMessage =
5
- | ScrcpyClipboardDeviceMessage;
8
+ | ScrcpyClipboardDeviceMessage
9
+ | ScrcpyAckClipboardDeviceMessage;
6
10
 
7
11
  export class ScrcpyDeviceMessageDeserializeStream extends BufferedTransformStream<ScrcpyDeviceMessage> {
8
12
  constructor() {
9
13
  super(async (stream) => {
10
14
  const type = await stream.read(1);
11
15
  switch (type[0]) {
12
- case 0:
13
- return await ScrcpyClipboardDeviceMessage.deserialize(stream);
16
+ case ScrcpyDeviceMessageType.Clipboard:
17
+ return await ScrcpyClipboardDeviceMessage.deserialize(
18
+ stream
19
+ );
20
+ case ScrcpyDeviceMessageType.AckClipboard:
21
+ return await ScrcpyAckClipboardDeviceMessage.deserialize(
22
+ stream
23
+ );
14
24
  default:
15
- throw new Error('unknown control message type');
25
+ throw new Error("unknown control message type");
16
26
  }
17
- })
27
+ });
18
28
  }
19
29
  }
@@ -1,5 +1,8 @@
1
- import type { AndroidCodecLevel, AndroidCodecProfile } from '../../codec.js';
2
- import type { ScrcpyOptionValue } from '../types.js';
1
+ import {
2
+ type AndroidCodecLevel,
3
+ type AndroidCodecProfile,
4
+ } from "../../codec.js";
5
+ import { type ScrcpyOptionValue } from "../types.js";
3
6
 
4
7
  /**
5
8
  * If the option you need is not in this type,
@@ -10,17 +13,20 @@ export interface CodecOptionsInit {
10
13
  level: AndroidCodecLevel;
11
14
 
12
15
  iFrameInterval: number;
16
+ maxBframes: number;
13
17
  repeatPreviousFrameAfter: number;
14
18
  maxPtsGapToEncoder: number;
15
19
  }
16
20
 
17
21
  function toDashCase(input: string) {
18
- return input.replace(/([A-Z])/g, '-$1').toLowerCase();
22
+ return input.replace(/([A-Z])/g, "-$1").toLowerCase();
19
23
  }
20
24
 
21
- const CODEC_OPTION_TYPES: Partial<Record<keyof CodecOptionsInit, 'long' | 'float' | 'string'>> = {
22
- repeatPreviousFrameAfter: 'long',
23
- maxPtsGapToEncoder: 'long',
25
+ const CODEC_OPTION_TYPES: Partial<
26
+ Record<keyof CodecOptionsInit, "long" | "float" | "string">
27
+ > = {
28
+ repeatPreviousFrameAfter: "long",
29
+ maxPtsGapToEncoder: "long",
24
30
  };
25
31
 
26
32
  export class CodecOptions implements ScrcpyOptionValue {
@@ -31,8 +37,9 @@ export class CodecOptions implements ScrcpyOptionValue {
31
37
  }
32
38
 
33
39
  public toOptionValue(): string | undefined {
34
- const entries = Object.entries(this.value)
35
- .filter(([key, value]) => value !== undefined);
40
+ const entries = Object.entries(this.value).filter(
41
+ ([, value]) => value !== undefined
42
+ );
36
43
 
37
44
  if (entries.length === 0) {
38
45
  return undefined;
@@ -41,8 +48,8 @@ export class CodecOptions implements ScrcpyOptionValue {
41
48
  return entries
42
49
  .map(([key, value]) => {
43
50
  const type = CODEC_OPTION_TYPES[key as keyof CodecOptionsInit];
44
- return `${toDashCase(key)}${type ? `:${type}` : ''}=${value}`;
51
+ return `${toDashCase(key)}${type ? `:${type}` : ""}=${value}`;
45
52
  })
46
- .join(',');
53
+ .join(",");
47
54
  }
48
55
  }