@yume-chan/scrcpy 0.0.14 → 0.0.15

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 (51) hide show
  1. package/CHANGELOG.json +15 -0
  2. package/CHANGELOG.md +9 -1
  3. package/esm/client.d.ts +14 -2
  4. package/esm/client.d.ts.map +1 -1
  5. package/esm/client.js +275 -143
  6. package/esm/client.js.map +1 -1
  7. package/esm/connection.js +74 -85
  8. package/esm/connection.js.map +1 -1
  9. package/esm/decoder/tinyh264/index.js +48 -48
  10. package/esm/decoder/tinyh264/index.js.map +1 -1
  11. package/esm/decoder/tinyh264/wrapper.js +3 -2
  12. package/esm/decoder/tinyh264/wrapper.js.map +1 -1
  13. package/esm/decoder/web-codecs/index.js +26 -21
  14. package/esm/decoder/web-codecs/index.js.map +1 -1
  15. package/esm/message.d.ts +12 -8
  16. package/esm/message.d.ts.map +1 -1
  17. package/esm/message.js +13 -8
  18. package/esm/message.js.map +1 -1
  19. package/esm/options/1_16/index.d.ts +5 -4
  20. package/esm/options/1_16/index.d.ts.map +1 -1
  21. package/esm/options/1_16/index.js +33 -17
  22. package/esm/options/1_16/index.js.map +1 -1
  23. package/esm/options/1_16/sps.js +3 -2
  24. package/esm/options/1_16/sps.js.map +1 -1
  25. package/esm/options/1_18.d.ts +3 -2
  26. package/esm/options/1_18.d.ts.map +1 -1
  27. package/esm/options/1_18.js +24 -1
  28. package/esm/options/1_18.js.map +1 -1
  29. package/esm/options/1_21.js +4 -1
  30. package/esm/options/1_21.js.map +1 -1
  31. package/esm/options/1_22.d.ts +3 -3
  32. package/esm/options/1_22.d.ts.map +1 -1
  33. package/esm/options/1_22.js +9 -6
  34. package/esm/options/1_22.js.map +1 -1
  35. package/esm/options/1_23.js +4 -1
  36. package/esm/options/1_23.js.map +1 -1
  37. package/esm/options/1_24.js +4 -1
  38. package/esm/options/1_24.js.map +1 -1
  39. package/esm/options/common.d.ts +2 -0
  40. package/esm/options/common.d.ts.map +1 -1
  41. package/esm/options/common.js.map +1 -1
  42. package/esm/push-server.js +5 -10
  43. package/esm/push-server.js.map +1 -1
  44. package/package.json +4 -4
  45. package/src/client.ts +181 -21
  46. package/src/connection.ts +1 -1
  47. package/src/message.ts +10 -3
  48. package/src/options/1_16/index.ts +25 -10
  49. package/src/options/1_18.ts +21 -1
  50. package/src/options/1_22.ts +6 -8
  51. package/src/options/common.ts +3 -0
package/src/client.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { AdbBufferedStream, AdbSubprocessNoneProtocol, DecodeUtf8Stream, InspectStream, TransformStream, WritableStream, type Adb, type AdbSocket, type AdbSubprocessProtocol, type ReadableStream, type WritableStreamDefaultWriter } from '@yume-chan/adb';
1
+ import { AbortController, AdbBufferedStream, AdbSubprocessNoneProtocol, DecodeUtf8Stream, InspectStream, ReadableStream, TransformStream, WritableStream, type Adb, type AdbSocket, type AdbSubprocessProtocol, type WritableStreamDefaultWriter } from '@yume-chan/adb';
2
2
  import { EventEmitter } from '@yume-chan/event';
3
3
  import Struct from '@yume-chan/struct';
4
- import { AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, type AndroidKeyEventAction } from './message.js';
4
+ import { AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpySimpleControlMessage, type AndroidKeyEventAction } from './message.js';
5
5
  import type { ScrcpyInjectScrollControlMessage1_22, ScrcpyOptions, VideoStreamPacket } from "./options/index.js";
6
6
 
7
7
  function* splitLines(text: string): Generator<string, void, void> {
@@ -20,12 +20,87 @@ function* splitLines(text: string): Generator<string, void, void> {
20
20
  }
21
21
  }
22
22
 
23
+ class SplitLinesStream extends TransformStream<string, string>{
24
+ constructor() {
25
+ super({
26
+ transform(chunk, controller) {
27
+ for (const line of splitLines(chunk)) {
28
+ if (line === '') {
29
+ continue;
30
+ }
31
+ controller.enqueue(line);
32
+ }
33
+ },
34
+ });
35
+ }
36
+ }
37
+
38
+ class ArrayToStream<T> extends ReadableStream<T>{
39
+ private array!: T[];
40
+ private index = 0;
41
+
42
+ constructor(array: T[]) {
43
+ super({
44
+ start: async () => {
45
+ await Promise.resolve();
46
+ this.array = array;
47
+ },
48
+ pull: (controller) => {
49
+ if (this.index < this.array.length) {
50
+ controller.enqueue(this.array[this.index]!);
51
+ this.index += 1;
52
+ } else {
53
+ controller.close();
54
+ }
55
+ },
56
+ });
57
+ }
58
+ }
59
+
60
+ class ConcatStream<T> extends ReadableStream<T>{
61
+ private streams!: ReadableStream<T>[];
62
+ private index = 0;
63
+ private reader!: ReadableStreamDefaultReader<T>;
64
+
65
+ constructor(...streams: ReadableStream<T>[]) {
66
+ super({
67
+ start: async (controller) => {
68
+ await Promise.resolve();
69
+
70
+ this.streams = streams;
71
+ this.advance(controller);
72
+ },
73
+ pull: async (controller) => {
74
+ const result = await this.reader.read();
75
+ if (!result.done) {
76
+ controller.enqueue(result.value);
77
+ return;
78
+ }
79
+ this.advance(controller);
80
+ }
81
+ });
82
+ }
83
+
84
+ private advance(controller: ReadableStreamDefaultController<T>) {
85
+ if (this.index < this.streams.length) {
86
+ this.reader = this.streams[this.index]!.getReader();
87
+ this.index += 1;
88
+ } else {
89
+ controller.close();
90
+ }
91
+ }
92
+ }
93
+
23
94
  const ClipboardMessage =
24
95
  new Struct()
25
96
  .uint32('length')
26
97
  .string('content', { lengthField: 'length' });
27
98
 
28
99
  export class ScrcpyClient {
100
+ /**
101
+ * This method will modify the given `options`,
102
+ * so don't reuse it elsewhere.
103
+ */
29
104
  public static async getEncoders(
30
105
  adb: Adb,
31
106
  path: string,
@@ -37,6 +112,8 @@ export class ScrcpyClient {
37
112
  options.value.encoderName = '_';
38
113
  // Disable control for faster connection in 1.22+
39
114
  options.value.control = false;
115
+ options.value.sendDeviceMeta = false;
116
+ options.value.sendDummyByte = false;
40
117
 
41
118
  // Scrcpy server will open connections, before initializing encoder
42
119
  // Thus although an invalid encoder name is given, the start process will success
@@ -56,6 +133,45 @@ export class ScrcpyClient {
56
133
  return encoders;
57
134
  }
58
135
 
136
+ /**
137
+ * This method will modify the given `options`,
138
+ * so don't reuse it elsewhere.
139
+ */
140
+ public static async getDisplays(
141
+ adb: Adb,
142
+ path: string,
143
+ version: string,
144
+ options: ScrcpyOptions<any>
145
+ ): Promise<number[]> {
146
+ // Similar to `getEncoders`, pass an invalid option and parse the output
147
+ options.value.displayId = -1;
148
+
149
+ options.value.control = false;
150
+ options.value.sendDeviceMeta = false;
151
+ options.value.sendDummyByte = false;
152
+
153
+ try {
154
+ // Server will exit before opening connections when an invalid display id was given.
155
+ await ScrcpyClient.start(adb, path, version, options);
156
+ } catch (e) {
157
+ if (e instanceof Error) {
158
+ const output = (e as any).output as string[];
159
+
160
+ const displayIdRegex = /\s+scrcpy --display (\d+)/;
161
+ const displays: number[] = [];
162
+ for (const line of output) {
163
+ const match = line.match(displayIdRegex);
164
+ if (match) {
165
+ displays.push(Number.parseInt(match[1]!, 10));
166
+ }
167
+ }
168
+ return displays;
169
+ }
170
+ }
171
+
172
+ throw new Error('failed to get displays');
173
+ }
174
+
59
175
  public static async start(
60
176
  adb: Adb,
61
177
  path: string,
@@ -85,17 +201,50 @@ export class ScrcpyClient {
85
201
  }
86
202
  );
87
203
 
204
+ const stdout = process.stdout
205
+ .pipeThrough(new DecodeUtf8Stream())
206
+ .pipeThrough(new SplitLinesStream());
207
+
208
+ // Read stdout, otherwise `process.exit` won't resolve.
209
+ const output: string[] = [];
210
+ const abortController = new AbortController();
211
+ const pipe = stdout
212
+ .pipeTo(new WritableStream({
213
+ write(chunk) {
214
+ output.push(chunk);
215
+ }
216
+ }), {
217
+ signal: abortController.signal,
218
+ preventCancel: true,
219
+ })
220
+ .catch(() => { });
221
+
88
222
  const result = await Promise.race([
89
223
  process.exit,
90
224
  connection.getStreams(),
91
225
  ]);
92
226
 
93
227
  if (typeof result === 'number') {
94
- throw new Error('scrcpy server exited prematurely');
228
+ const error = new Error('scrcpy server exited prematurely');
229
+ (error as any).output = output;
230
+ throw error;
95
231
  }
96
232
 
233
+ abortController.abort();
234
+ await pipe;
235
+
97
236
  const [videoStream, controlStream] = result;
98
- return new ScrcpyClient(adb, options, process, videoStream, controlStream);
237
+ return new ScrcpyClient(
238
+ adb,
239
+ options,
240
+ process,
241
+ new ConcatStream(
242
+ new ArrayToStream(output),
243
+ stdout,
244
+ ),
245
+ videoStream,
246
+ controlStream
247
+ );
99
248
  } catch (e) {
100
249
  await process?.kill();
101
250
  throw e;
@@ -135,6 +284,7 @@ export class ScrcpyClient {
135
284
  adb: Adb,
136
285
  options: ScrcpyOptions<any>,
137
286
  process: AdbSubprocessProtocol,
287
+ stdout: ReadableStream<string>,
138
288
  videoStream: AdbSocket,
139
289
  controlStream: AdbSocket | undefined,
140
290
  ) {
@@ -142,18 +292,7 @@ export class ScrcpyClient {
142
292
  this.options = options;
143
293
  this.process = process;
144
294
 
145
- this._stdout = process.stdout
146
- .pipeThrough(new DecodeUtf8Stream())
147
- .pipeThrough(new TransformStream({
148
- transform(chunk, controller) {
149
- for (const line of splitLines(chunk)) {
150
- if (line === '') {
151
- continue;
152
- }
153
- controller.enqueue(line);
154
- }
155
- },
156
- }));
295
+ this._stdout = stdout;
157
296
 
158
297
  this._videoStream = videoStream.readable
159
298
  .pipeThrough(options.createVideoStreamTransformer())
@@ -195,12 +334,21 @@ export class ScrcpyClient {
195
334
  return this._controlStreamWriter;
196
335
  }
197
336
 
337
+ private getControlMessageTypeValue(type: ScrcpyControlMessageType) {
338
+ const list = this.options.getControlMessageTypes();
339
+ const index = list.indexOf(type);
340
+ if (index === -1) {
341
+ throw new Error('Not supported');
342
+ }
343
+ return index;
344
+ }
345
+
198
346
  public async injectKeyCode(message: Omit<ScrcpyInjectKeyCodeControlMessage, 'type'>) {
199
347
  const controlStream = this.checkControlStream('injectKeyCode');
200
348
 
201
349
  await controlStream.write(ScrcpyInjectKeyCodeControlMessage.serialize({
202
350
  ...message,
203
- type: ScrcpyControlMessageType.InjectKeycode,
351
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectKeycode),
204
352
  }));
205
353
  }
206
354
 
@@ -208,7 +356,7 @@ export class ScrcpyClient {
208
356
  const controlStream = this.checkControlStream('injectText');
209
357
 
210
358
  await controlStream.write(ScrcpyInjectTextControlMessage.serialize({
211
- type: ScrcpyControlMessageType.InjectText,
359
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectText),
212
360
  text,
213
361
  }));
214
362
  }
@@ -234,7 +382,7 @@ export class ScrcpyClient {
234
382
  this.lastTouchMessage = now;
235
383
  await controlStream.write(ScrcpyInjectTouchControlMessage.serialize({
236
384
  ...message,
237
- type: ScrcpyControlMessageType.InjectTouch,
385
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectTouch),
238
386
  screenWidth: this.screenWidth,
239
387
  screenHeight: this.screenHeight,
240
388
  }));
@@ -249,7 +397,7 @@ export class ScrcpyClient {
249
397
 
250
398
  const buffer = this.options!.serializeInjectScrollControlMessage({
251
399
  ...message,
252
- type: ScrcpyControlMessageType.InjectScroll,
400
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.InjectScroll),
253
401
  screenWidth: this.screenWidth,
254
402
  screenHeight: this.screenHeight,
255
403
  });
@@ -260,7 +408,7 @@ export class ScrcpyClient {
260
408
  const controlStream = this.checkControlStream('pressBackOrTurnOnScreen');
261
409
 
262
410
  const buffer = this.options!.serializeBackOrScreenOnControlMessage({
263
- type: ScrcpyControlMessageType.BackOrScreenOn,
411
+ type: this.getControlMessageTypeValue(ScrcpyControlMessageType.BackOrScreenOn),
264
412
  action,
265
413
  });
266
414
  if (buffer) {
@@ -268,6 +416,18 @@ export class ScrcpyClient {
268
416
  }
269
417
  }
270
418
 
419
+ private async sendSimpleControlMessage(type: ScrcpyControlMessageType, name: string) {
420
+ const controlStream = this.checkControlStream(name);
421
+ const buffer = ScrcpySimpleControlMessage.serialize({
422
+ type: this.getControlMessageTypeValue(type),
423
+ });
424
+ await controlStream.write(buffer);
425
+ }
426
+
427
+ public async rotateDevice() {
428
+ await this.sendSimpleControlMessage(ScrcpyControlMessageType.RotateDevice, 'rotateDevice');
429
+ }
430
+
271
431
  public async close() {
272
432
  // No need to close streams. Kill the process will destroy them from the other side.
273
433
  await this.process?.kill();
package/src/connection.ts CHANGED
@@ -105,7 +105,7 @@ export class ScrcpyClientReverseConnection extends ScrcpyClientConnection {
105
105
  const writer = queue.writable.getWriter();
106
106
  this.address = await this.device.reverse.add(
107
107
  'localabstract:scrcpy',
108
- 27183,
108
+ 'tcp:27183',
109
109
  socket => {
110
110
  writer.write(socket);
111
111
  return true;
package/src/message.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import Struct, { placeholder } from '@yume-chan/struct';
2
2
 
3
+ // https://github.com/Genymobile/scrcpy/blob/fa5b2a29e983a46b49531def9cf3d80c40c3de37/app/src/control_msg.h#L23
4
+ // For their message bodies, see https://github.com/Genymobile/scrcpy/blob/5c62f3419d252d10cd8c9cbb7c918b358b81f2d0/app/src/control_msg.c#L92
3
5
  export enum ScrcpyControlMessageType {
4
6
  InjectKeycode,
5
7
  InjectText,
@@ -7,6 +9,7 @@ export enum ScrcpyControlMessageType {
7
9
  InjectScroll,
8
10
  BackOrScreenOn,
9
11
  ExpandNotificationPanel,
12
+ ExpandSettingPanel,
10
13
  CollapseNotificationPanel,
11
14
  GetClipboard,
12
15
  SetClipboard,
@@ -31,9 +34,13 @@ export enum AndroidMotionEventAction {
31
34
  ButtonRelease,
32
35
  }
33
36
 
37
+ export const ScrcpySimpleControlMessage =
38
+ new Struct()
39
+ .uint8('type');
40
+
34
41
  export const ScrcpyInjectTouchControlMessage =
35
42
  new Struct()
36
- .uint8('type', ScrcpyControlMessageType.InjectTouch as const)
43
+ .fields(ScrcpySimpleControlMessage)
37
44
  .uint8('action', placeholder<AndroidMotionEventAction>())
38
45
  .uint64('pointerId')
39
46
  .uint32('pointerX')
@@ -47,7 +54,7 @@ export type ScrcpyInjectTouchControlMessage = typeof ScrcpyInjectTouchControlMes
47
54
 
48
55
  export const ScrcpyInjectTextControlMessage =
49
56
  new Struct()
50
- .uint8('type', ScrcpyControlMessageType.InjectText as const)
57
+ .fields(ScrcpySimpleControlMessage)
51
58
  .uint32('length')
52
59
  .string('text', { lengthField: 'length' });
53
60
 
@@ -95,7 +102,7 @@ export enum AndroidKeyCode {
95
102
 
96
103
  export const ScrcpyInjectKeyCodeControlMessage =
97
104
  new Struct()
98
- .uint8('type', ScrcpyControlMessageType.InjectKeycode as const)
105
+ .fields(ScrcpySimpleControlMessage)
99
106
  .uint8('action', placeholder<AndroidKeyEventAction>())
100
107
  .uint32('keyCode')
101
108
  .uint32('repeat')
@@ -1,8 +1,8 @@
1
1
  import { StructDeserializeStream, TransformStream, type Adb } from "@yume-chan/adb";
2
- import Struct, { placeholder } from "@yume-chan/struct";
2
+ import Struct from "@yume-chan/struct";
3
3
  import type { AndroidCodecLevel, AndroidCodecProfile } from "../../codec.js";
4
4
  import { ScrcpyClientConnection, ScrcpyClientForwardConnection, ScrcpyClientReverseConnection, type ScrcpyClientConnectionOptions } from "../../connection.js";
5
- import { AndroidKeyEventAction, ScrcpyControlMessageType } from "../../message.js";
5
+ import { AndroidKeyEventAction, ScrcpyControlMessageType, ScrcpySimpleControlMessage } from "../../message.js";
6
6
  import type { ScrcpyBackOrScreenOnEvent1_18 } from "../1_18.js";
7
7
  import type { ScrcpyInjectScrollControlMessage1_22 } from "../1_22.js";
8
8
  import { toScrcpyOptionValue, type ScrcpyOptions, type ScrcpyOptionValue, type VideoStreamPacket } from "../common.js";
@@ -16,7 +16,7 @@ export enum ScrcpyLogLevel {
16
16
  Error = 'error',
17
17
  }
18
18
 
19
- export enum ScrcpyScreenOrientation {
19
+ export enum ScrcpyVideoOrientation {
20
20
  Initial = -2,
21
21
  Unlocked = -1,
22
22
  Portrait = 0,
@@ -75,7 +75,7 @@ export interface ScrcpyOptionsInit1_16 {
75
75
  * It will not keep the device screen in specific orientation,
76
76
  * only the captured video will in this orientation.
77
77
  */
78
- lockVideoOrientation: ScrcpyScreenOrientation;
78
+ lockVideoOrientation: ScrcpyVideoOrientation;
79
79
 
80
80
  tunnelForward: boolean;
81
81
 
@@ -119,12 +119,11 @@ export const VideoPacket =
119
119
  export const NO_PTS = BigInt(1) << BigInt(63);
120
120
 
121
121
  export const ScrcpyBackOrScreenOnEvent1_16 =
122
- new Struct()
123
- .uint8('type', placeholder<ScrcpyControlMessageType.BackOrScreenOn>());
122
+ ScrcpySimpleControlMessage;
124
123
 
125
124
  export const ScrcpyInjectScrollControlMessage1_16 =
126
125
  new Struct()
127
- .uint8('type', ScrcpyControlMessageType.InjectScroll as const)
126
+ .fields(ScrcpySimpleControlMessage)
128
127
  .uint32('pointerX')
129
128
  .uint32('pointerY')
130
129
  .uint16('screenWidth')
@@ -142,8 +141,8 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn
142
141
  }
143
142
 
144
143
  if (new.target === ScrcpyOptions1_16 &&
145
- value.lockVideoOrientation === ScrcpyScreenOrientation.Initial) {
146
- value.lockVideoOrientation = ScrcpyScreenOrientation.Unlocked;
144
+ value.lockVideoOrientation === ScrcpyVideoOrientation.Initial) {
145
+ value.lockVideoOrientation = ScrcpyVideoOrientation.Unlocked;
147
146
  }
148
147
 
149
148
  this.value = value as Partial<T>;
@@ -174,7 +173,7 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn
174
173
  maxSize: 0,
175
174
  bitRate: 8_000_000,
176
175
  maxFps: 0,
177
- lockVideoOrientation: ScrcpyScreenOrientation.Unlocked,
176
+ lockVideoOrientation: ScrcpyVideoOrientation.Unlocked,
178
177
  tunnelForward: false,
179
178
  crop: '-',
180
179
  sendFrameMeta: true,
@@ -297,6 +296,22 @@ export class ScrcpyOptions1_16<T extends ScrcpyOptionsInit1_16 = ScrcpyOptionsIn
297
296
  };
298
297
  }
299
298
 
299
+ public getControlMessageTypes(): ScrcpyControlMessageType[] {
300
+ return [
301
+ /* 0 */ ScrcpyControlMessageType.InjectKeycode,
302
+ /* 1 */ ScrcpyControlMessageType.InjectText,
303
+ /* 2 */ ScrcpyControlMessageType.InjectTouch,
304
+ /* 3 */ ScrcpyControlMessageType.InjectScroll,
305
+ /* 4 */ ScrcpyControlMessageType.BackOrScreenOn,
306
+ /* 5 */ ScrcpyControlMessageType.ExpandNotificationPanel,
307
+ /* 6 */ ScrcpyControlMessageType.CollapseNotificationPanel,
308
+ /* 7 */ ScrcpyControlMessageType.GetClipboard,
309
+ /* 8 */ ScrcpyControlMessageType.SetClipboard,
310
+ /* 9 */ ScrcpyControlMessageType.SetScreenPowerMode,
311
+ /* 10 */ ScrcpyControlMessageType.RotateDevice,
312
+ ];
313
+ }
314
+
300
315
  public serializeBackOrScreenOnControlMessage(
301
316
  message: ScrcpyBackOrScreenOnEvent1_18,
302
317
  ) {
@@ -1,5 +1,5 @@
1
1
  import Struct, { placeholder } from "@yume-chan/struct";
2
- import type { AndroidKeyEventAction } from "../message.js";
2
+ import { AndroidKeyEventAction, ScrcpyControlMessageType } from "../message.js";
3
3
  import { ScrcpyBackOrScreenOnEvent1_16, ScrcpyOptions1_16, type ScrcpyOptionsInit1_16 } from "./1_16/index.js";
4
4
 
5
5
  export interface ScrcpyOptionsInit1_18 extends ScrcpyOptionsInit1_16 {
@@ -33,6 +33,26 @@ export class ScrcpyOptions1_18<T extends ScrcpyOptionsInit1_18 = ScrcpyOptionsIn
33
33
  return /\s+scrcpy --encoder '(.*?)'/;
34
34
  }
35
35
 
36
+ public override getControlMessageTypes(): ScrcpyControlMessageType[] {
37
+ /**
38
+ * 0 InjectKeycode
39
+ * 1 InjectText
40
+ * 2 InjectTouch
41
+ * 3 InjectScroll
42
+ * 4 BackOrScreenOn
43
+ * 5 ExpandNotificationPanel
44
+ * 6 ExpandSettingsPanel
45
+ * 7 CollapseNotificationPanel
46
+ * 8 GetClipboard
47
+ * 9 SetClipboard
48
+ * 10 SetScreenPowerMode
49
+ * 11 RotateDevice
50
+ */
51
+ const types = super.getControlMessageTypes();
52
+ types.splice(6, 0, ScrcpyControlMessageType.ExpandSettingPanel);
53
+ return types;
54
+ }
55
+
36
56
  public override serializeBackOrScreenOnControlMessage(
37
57
  message: ScrcpyBackOrScreenOnEvent1_18,
38
58
  ) {
@@ -1,6 +1,6 @@
1
1
  import type { Adb } from "@yume-chan/adb";
2
2
  import Struct from "@yume-chan/struct";
3
- import { ScrcpyClientForwardConnection, ScrcpyClientReverseConnection, type ScrcpyClientConnection, type ScrcpyClientConnectionOptions } from "../connection.js";
3
+ import { ScrcpyClientForwardConnection, ScrcpyClientReverseConnection, type ScrcpyClientConnection } from "../connection.js";
4
4
  import { ScrcpyInjectScrollControlMessage1_16 } from "./1_16/index.js";
5
5
  import { ScrcpyOptions1_21, type ScrcpyOptionsInit1_21 } from "./1_21.js";
6
6
 
@@ -8,14 +8,14 @@ export interface ScrcpyOptionsInit1_22 extends ScrcpyOptionsInit1_21 {
8
8
  downsizeOnError: boolean;
9
9
 
10
10
  /**
11
- * Send device name and size
11
+ * Send device name and size at start of video stream.
12
12
  *
13
13
  * @default true
14
14
  */
15
15
  sendDeviceMeta: boolean;
16
16
 
17
17
  /**
18
- * Write a byte on start to detect connection issues
18
+ * Send a `0` byte on start of video stream to detect connection issues
19
19
  *
20
20
  * @default true
21
21
  */
@@ -59,11 +59,9 @@ export class ScrcpyOptions1_22<T extends ScrcpyOptionsInit1_22 = ScrcpyOptionsIn
59
59
  }
60
60
 
61
61
  public override createConnection(device: Adb): ScrcpyClientConnection {
62
- const defaultValue = this.getDefaultValue();
63
- const options: ScrcpyClientConnectionOptions = {
64
- control: this.value.control ?? defaultValue.control,
65
- sendDummyByte: this.value.sendDummyByte ?? defaultValue.sendDummyByte,
66
- sendDeviceMeta: this.value.sendDeviceMeta ?? defaultValue.sendDeviceMeta,
62
+ const options = {
63
+ ...this.getDefaultValue(),
64
+ ...this.value,
67
65
  };
68
66
  if (this.value.tunnelForward) {
69
67
  return new ScrcpyClientForwardConnection(device, options);
@@ -1,6 +1,7 @@
1
1
  import type { Adb, TransformStream } from "@yume-chan/adb";
2
2
  import type { ScrcpyClientConnection } from "../connection.js";
3
3
  import type { H264Configuration } from "../decoder/index.js";
4
+ import type { ScrcpyControlMessageType } from "../message.js";
4
5
  import type { ScrcpyBackOrScreenOnEvent1_18 } from "./1_18.js";
5
6
  import type { ScrcpyInjectScrollControlMessage1_22 } from "./1_22.js";
6
7
 
@@ -53,6 +54,8 @@ export interface ScrcpyOptions<T> {
53
54
 
54
55
  createVideoStreamTransformer(): TransformStream<Uint8Array, VideoStreamPacket>;
55
56
 
57
+ getControlMessageTypes(): ScrcpyControlMessageType[];
58
+
56
59
  serializeBackOrScreenOnControlMessage(
57
60
  message: ScrcpyBackOrScreenOnEvent1_18,
58
61
  ): Uint8Array | undefined;