ai-remote 0.1.0
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.
- package/README.md +50 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +7 -0
- package/dist/protocols/index.js +35 -0
- package/dist/protocols/index.js.map +7 -0
- package/dist/protocols/rdp/buffer.js +170 -0
- package/dist/protocols/rdp/buffer.js.map +7 -0
- package/dist/protocols/rdp/caps.js +260 -0
- package/dist/protocols/rdp/caps.js.map +7 -0
- package/dist/protocols/rdp/cert.js +135 -0
- package/dist/protocols/rdp/cert.js.map +7 -0
- package/dist/protocols/rdp/client.js +1068 -0
- package/dist/protocols/rdp/client.js.map +7 -0
- package/dist/protocols/rdp/cliprdr.js +240 -0
- package/dist/protocols/rdp/cliprdr.js.map +7 -0
- package/dist/protocols/rdp/credssp.js +254 -0
- package/dist/protocols/rdp/credssp.js.map +7 -0
- package/dist/protocols/rdp/crypto.js +369 -0
- package/dist/protocols/rdp/crypto.js.map +7 -0
- package/dist/protocols/rdp/display.js +589 -0
- package/dist/protocols/rdp/display.js.map +7 -0
- package/dist/protocols/rdp/gcc.js +132 -0
- package/dist/protocols/rdp/gcc.js.map +7 -0
- package/dist/protocols/rdp/keymap.js +132 -0
- package/dist/protocols/rdp/keymap.js.map +7 -0
- package/dist/protocols/rdp/mcs.js +223 -0
- package/dist/protocols/rdp/mcs.js.map +7 -0
- package/dist/protocols/rdp/ntlm.js +305 -0
- package/dist/protocols/rdp/ntlm.js.map +7 -0
- package/dist/protocols/rdp/pdu.js +444 -0
- package/dist/protocols/rdp/pdu.js.map +7 -0
- package/dist/protocols/rdp/rail.js +383 -0
- package/dist/protocols/rdp/rail.js.map +7 -0
- package/dist/protocols/rdp/rle.js +276 -0
- package/dist/protocols/rdp/rle.js.map +7 -0
- package/dist/protocols/rdp/sec.js +156 -0
- package/dist/protocols/rdp/sec.js.map +7 -0
- package/dist/protocols/rdp/session.js +307 -0
- package/dist/protocols/rdp/session.js.map +7 -0
- package/dist/protocols/rdp/tls.js +354 -0
- package/dist/protocols/rdp/tls.js.map +7 -0
- package/dist/protocols/rdp/vchannel.js +59 -0
- package/dist/protocols/rdp/vchannel.js.map +7 -0
- package/dist/protocols/rdp/x224.js +110 -0
- package/dist/protocols/rdp/x224.js.map +7 -0
- package/dist/protocols/ssh/kex.js +177 -0
- package/dist/protocols/ssh/kex.js.map +7 -0
- package/dist/protocols/ssh/messages.js +55 -0
- package/dist/protocols/ssh/messages.js.map +7 -0
- package/dist/protocols/ssh/session.js +608 -0
- package/dist/protocols/ssh/session.js.map +7 -0
- package/dist/protocols/ssh/terminal.js +191 -0
- package/dist/protocols/ssh/terminal.js.map +7 -0
- package/dist/protocols/ssh/transport.js +611 -0
- package/dist/protocols/ssh/transport.js.map +7 -0
- package/dist/protocols/ssh/wire.js +189 -0
- package/dist/protocols/ssh/wire.js.map +7 -0
- package/dist/protocols/types.js +1 -0
- package/dist/protocols/types.js.map +7 -0
- package/dist/protocols/vnc/input.js +16 -0
- package/dist/protocols/vnc/input.js.map +7 -0
- package/dist/protocols/vnc/session.js +259 -0
- package/dist/protocols/vnc/session.js.map +7 -0
- package/dist/shared/connection.js +114 -0
- package/dist/shared/connection.js.map +7 -0
- package/dist/shared/device.js +1 -0
- package/dist/shared/device.js.map +7 -0
- package/dist/shared/hosts.js +133 -0
- package/dist/shared/hosts.js.map +7 -0
- package/dist/shared/icons.js +42 -0
- package/dist/shared/icons.js.map +7 -0
- package/dist/shared/protocol.js +40 -0
- package/dist/shared/protocol.js.map +7 -0
- package/dist/shared/signals.js +42 -0
- package/dist/shared/signals.js.map +7 -0
- package/package.json +82 -0
- package/src/index.ts +14 -0
- package/src/protocols/index.ts +72 -0
- package/src/protocols/rdp/buffer.ts +204 -0
- package/src/protocols/rdp/caps.ts +304 -0
- package/src/protocols/rdp/cert.ts +190 -0
- package/src/protocols/rdp/client.ts +1363 -0
- package/src/protocols/rdp/cliprdr.ts +344 -0
- package/src/protocols/rdp/credssp.ts +319 -0
- package/src/protocols/rdp/crypto.ts +416 -0
- package/src/protocols/rdp/display.ts +750 -0
- package/src/protocols/rdp/gcc.ts +162 -0
- package/src/protocols/rdp/keymap.ts +68 -0
- package/src/protocols/rdp/mcs.ts +283 -0
- package/src/protocols/rdp/ntlm.ts +380 -0
- package/src/protocols/rdp/pdu.ts +497 -0
- package/src/protocols/rdp/rail.ts +480 -0
- package/src/protocols/rdp/rle.ts +343 -0
- package/src/protocols/rdp/sec.ts +184 -0
- package/src/protocols/rdp/session.ts +393 -0
- package/src/protocols/rdp/tls.ts +452 -0
- package/src/protocols/rdp/vchannel.ts +89 -0
- package/src/protocols/rdp/x224.ts +139 -0
- package/src/protocols/ssh/kex.ts +227 -0
- package/src/protocols/ssh/messages.ts +54 -0
- package/src/protocols/ssh/session.ts +878 -0
- package/src/protocols/ssh/terminal.ts +242 -0
- package/src/protocols/ssh/transport.ts +732 -0
- package/src/protocols/ssh/wire.ts +220 -0
- package/src/protocols/types.ts +146 -0
- package/src/protocols/vnc/input.ts +13 -0
- package/src/protocols/vnc/novnc.d.ts +75 -0
- package/src/protocols/vnc/session.ts +367 -0
- package/src/shared/connection.ts +206 -0
- package/src/shared/device.ts +28 -0
- package/src/shared/hosts.ts +240 -0
- package/src/shared/icons.ts +49 -0
- package/src/shared/protocol.ts +55 -0
- package/src/shared/signals.ts +67 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
// The share layer: PDU headers, the client info PDU, input events, and the
|
|
2
|
+
// server update PDUs this client understands (MS-RDPBCGR 2.2.8, 2.2.9, 2.2.1.13).
|
|
3
|
+
|
|
4
|
+
import { Reader, Writer } from './buffer';
|
|
5
|
+
|
|
6
|
+
export const PDU_TYPE = {
|
|
7
|
+
DEMAND_ACTIVE: 1,
|
|
8
|
+
CONFIRM_ACTIVE: 3,
|
|
9
|
+
DEACTIVATE_ALL: 6,
|
|
10
|
+
DATA: 7,
|
|
11
|
+
SERVER_REDIRECT: 10,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const PDU_TYPE2 = {
|
|
15
|
+
UPDATE: 2,
|
|
16
|
+
CONTROL: 20,
|
|
17
|
+
POINTER: 27,
|
|
18
|
+
INPUT: 28,
|
|
19
|
+
SYNCHRONIZE: 31,
|
|
20
|
+
REFRESH_RECT: 33,
|
|
21
|
+
PLAY_SOUND: 34,
|
|
22
|
+
SUPPRESS_OUTPUT: 35,
|
|
23
|
+
SHUTDOWN_REQUEST: 36,
|
|
24
|
+
SHUTDOWN_DENIED: 37,
|
|
25
|
+
SAVE_SESSION_INFO: 38,
|
|
26
|
+
FONT_LIST: 39,
|
|
27
|
+
FONT_MAP: 40,
|
|
28
|
+
SET_ERROR_INFO: 47,
|
|
29
|
+
MONITOR_LAYOUT: 55,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const UPDATE_TYPE = {
|
|
33
|
+
ORDERS: 0,
|
|
34
|
+
BITMAP: 1,
|
|
35
|
+
PALETTE: 2,
|
|
36
|
+
SYNCHRONIZE: 3,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const CONTROL_ACTION = {
|
|
40
|
+
REQUEST_CONTROL: 1,
|
|
41
|
+
GRANTED_CONTROL: 2,
|
|
42
|
+
DETACH: 3,
|
|
43
|
+
COOPERATE: 4,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const INPUT_EVENT = {
|
|
47
|
+
SYNC: 0x0000,
|
|
48
|
+
SCANCODE: 0x0004,
|
|
49
|
+
UNICODE: 0x0005,
|
|
50
|
+
MOUSE: 0x8001,
|
|
51
|
+
MOUSEX: 0x8002,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const KEYBOARD_FLAG = {
|
|
55
|
+
EXTENDED: 0x0100,
|
|
56
|
+
DOWN: 0x4000,
|
|
57
|
+
RELEASE: 0x8000,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const POINTER_FLAG = {
|
|
61
|
+
WHEEL: 0x0200,
|
|
62
|
+
HWHEEL: 0x0400,
|
|
63
|
+
WHEEL_NEGATIVE: 0x0100,
|
|
64
|
+
MOVE: 0x0800,
|
|
65
|
+
DOWN: 0x8000,
|
|
66
|
+
BUTTON1: 0x1000,
|
|
67
|
+
BUTTON2: 0x2000,
|
|
68
|
+
BUTTON3: 0x4000,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Translate browser-style wheel direction to the signed RDP rotation flags. */
|
|
72
|
+
export function pointerWheelFlags(delta: number, horizontal = false): number {
|
|
73
|
+
const steps = horizontal ? Math.sign(delta) : -Math.sign(delta);
|
|
74
|
+
if (steps === 0) return 0;
|
|
75
|
+
const wheelFlag = horizontal ? POINTER_FLAG.HWHEEL : POINTER_FLAG.WHEEL;
|
|
76
|
+
const rotation = 120;
|
|
77
|
+
return steps > 0
|
|
78
|
+
? wheelFlag | rotation
|
|
79
|
+
: wheelFlag | POINTER_FLAG.WHEEL_NEGATIVE | ((-rotation) & 0xff);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const TOGGLE_FLAG = {
|
|
83
|
+
SCROLL_LOCK: 0x0001,
|
|
84
|
+
NUM_LOCK: 0x0002,
|
|
85
|
+
CAPS_LOCK: 0x0004,
|
|
86
|
+
KANA_LOCK: 0x0008,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const POINTER_MESSAGE = {
|
|
90
|
+
SYSTEM: 0x0001,
|
|
91
|
+
POSITION: 0x0003,
|
|
92
|
+
COLOR: 0x0006,
|
|
93
|
+
CACHED: 0x0007,
|
|
94
|
+
POINTER: 0x0008,
|
|
95
|
+
LARGE: 0x0009,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const INFO_FLAG =
|
|
99
|
+
0x00000001 | // INFO_MOUSE
|
|
100
|
+
0x00000002 | // INFO_DISABLECTRLALTDEL
|
|
101
|
+
0x00000010 | // INFO_UNICODE
|
|
102
|
+
0x00000020 | // INFO_MAXIMIZESHELL
|
|
103
|
+
0x00000040 | // INFO_LOGONNOTIFY
|
|
104
|
+
0x00000100 | // INFO_ENABLEWINDOWSKEY
|
|
105
|
+
0x00010000 | // INFO_LOGONERRORS
|
|
106
|
+
0x00020000 | // INFO_MOUSE_HAS_WHEEL
|
|
107
|
+
0x00080000; // INFO_NOAUDIOPLAYBACK
|
|
108
|
+
|
|
109
|
+
const INFO_AUTOLOGON = 0x00000008;
|
|
110
|
+
const INFO_RAIL = 0x00008000;
|
|
111
|
+
|
|
112
|
+
const ERROR_INFO_MESSAGES = {
|
|
113
|
+
0x00000001: 'The host disconnected the session (an administrative API call ended it).',
|
|
114
|
+
0x00000002: 'The host logged the session off.',
|
|
115
|
+
0x00000003: 'The session was disconnected after being idle too long.',
|
|
116
|
+
0x00000004: 'The session was disconnected because sign-in took too long.',
|
|
117
|
+
0x00000005: 'The session was taken over by another connection.',
|
|
118
|
+
0x00000006: 'The host ran out of memory.',
|
|
119
|
+
0x00000007: 'The host denied the connection.',
|
|
120
|
+
0x00000009: 'This account does not have permission to sign in remotely.',
|
|
121
|
+
0x0000000a: 'The host requires fresh credentials.',
|
|
122
|
+
0x0000000b: 'A user on the host ended the session.',
|
|
123
|
+
0x0000000c: 'The remote user signed out.',
|
|
124
|
+
0x00000100: 'Remote Desktop licensing failed on the host (internal error).',
|
|
125
|
+
0x00000101: 'The host could not reach a Remote Desktop licensing server.',
|
|
126
|
+
0x00000102: 'The host has no Remote Desktop licence available for this client.',
|
|
127
|
+
0x00000103: 'The host rejected this client during licensing.',
|
|
128
|
+
0x00000104: 'The stored Remote Desktop licence does not match this client.',
|
|
129
|
+
0x00000105: 'The host rejected the client licence.',
|
|
130
|
+
0x00000106: 'Remote Desktop licensing could not be completed.',
|
|
131
|
+
0x00000107: 'The client ended the licensing exchange.',
|
|
132
|
+
0x00000108: 'The host rejected the client licence encryption.',
|
|
133
|
+
0x00000109: 'The host could not upgrade the client licence.',
|
|
134
|
+
0x0000010a: 'The host does not accept remote connections.',
|
|
135
|
+
0x000010f9: 'The host rejected a malformed capability set from this client.',
|
|
136
|
+
0x000010fa: 'The host rejected the client capability exchange.',
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export function errorInfoMessage(errorInfo) {
|
|
140
|
+
return (
|
|
141
|
+
ERROR_INFO_MESSAGES[errorInfo] ||
|
|
142
|
+
`The host ended the session with error info 0x${errorInfo.toString(16).padStart(8, '0')}.`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --- Outbound PDUs ----------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
/** TS_SHARECONTROLHEADER + payload. */
|
|
149
|
+
export function shareControlPdu(pduType, userId: number, payload: Uint8Array) {
|
|
150
|
+
const writer = new Writer(payload.length + 6);
|
|
151
|
+
writer.u16le(payload.length + 6);
|
|
152
|
+
writer.u16le(pduType | 0x10); // low nibble is the type, high nibble the version
|
|
153
|
+
writer.u16le(userId);
|
|
154
|
+
writer.raw(payload);
|
|
155
|
+
return writer.take();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** TS_SHAREDATAHEADER inside a share control PDU of type DATA. */
|
|
159
|
+
export function shareDataPdu(shareId: number, userId: number, pduType2, payload: Uint8Array) {
|
|
160
|
+
const writer = new Writer(payload.length + 12);
|
|
161
|
+
writer.u32le(shareId);
|
|
162
|
+
writer.u8(0); // pad
|
|
163
|
+
writer.u8(1); // streamId: low priority
|
|
164
|
+
writer.u16le(payload.length + 18); // uncompressedLength, headers included
|
|
165
|
+
writer.u8(pduType2);
|
|
166
|
+
writer.u8(0); // compressedType: none
|
|
167
|
+
writer.u16le(0); // compressedLength
|
|
168
|
+
writer.raw(payload);
|
|
169
|
+
return shareControlPdu(PDU_TYPE.DATA, userId, writer.take());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Write a UTF-16LE field with its terminator, returning the length without it. */
|
|
173
|
+
function unicodeField(writer: Writer, text: string) {
|
|
174
|
+
const start = writer.length;
|
|
175
|
+
writer.utf16le(text);
|
|
176
|
+
const byteLength = writer.length - start;
|
|
177
|
+
writer.u16le(0); // terminator
|
|
178
|
+
return byteLength;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Client Info PDU (MS-RDPBCGR 2.2.1.11). The credential lengths sit in front of
|
|
183
|
+
* the strings, so the strings are written to a scratch buffer first.
|
|
184
|
+
*/
|
|
185
|
+
export function clientInfoPdu({ domain, username, password, performanceFlags, autoLogon, remoteApp = false }) {
|
|
186
|
+
const strings = new Writer(512);
|
|
187
|
+
const cbDomain = unicodeField(strings, domain || '');
|
|
188
|
+
const cbUsername = unicodeField(strings, username || '');
|
|
189
|
+
const cbPassword = unicodeField(strings, password || '');
|
|
190
|
+
const cbAlternateShell = unicodeField(strings, '');
|
|
191
|
+
const cbWorkingDir = unicodeField(strings, '');
|
|
192
|
+
|
|
193
|
+
const writer = new Writer(768);
|
|
194
|
+
writer.u32le(0); // codePage
|
|
195
|
+
writer.u32le((INFO_FLAG | (autoLogon ? INFO_AUTOLOGON : 0) | (remoteApp ? INFO_RAIL : 0)) >>> 0);
|
|
196
|
+
writer.u16le(cbDomain);
|
|
197
|
+
writer.u16le(cbUsername);
|
|
198
|
+
writer.u16le(cbPassword);
|
|
199
|
+
writer.u16le(cbAlternateShell);
|
|
200
|
+
writer.u16le(cbWorkingDir);
|
|
201
|
+
writer.raw(strings.take());
|
|
202
|
+
|
|
203
|
+
// TS_EXTENDED_INFO_PACKET
|
|
204
|
+
writer.u16le(2); // clientAddressFamily: AF_INET
|
|
205
|
+
writer.u16le(2); // cbClientAddress: just the terminator
|
|
206
|
+
writer.u16le(0);
|
|
207
|
+
writer.u16le(2); // cbClientDir
|
|
208
|
+
writer.u16le(0);
|
|
209
|
+
writer.zeros(172); // clientTimeZone
|
|
210
|
+
writer.u32le(0); // clientSessionId
|
|
211
|
+
writer.u32le(performanceFlags >>> 0);
|
|
212
|
+
writer.u16le(0); // cbAutoReconnectCookie
|
|
213
|
+
|
|
214
|
+
return writer.take();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function synchronizePdu(targetUser) {
|
|
218
|
+
const writer = new Writer(4);
|
|
219
|
+
writer.u16le(1); // SYNCMSGTYPE_SYNC
|
|
220
|
+
writer.u16le(targetUser);
|
|
221
|
+
return writer.take();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function controlPdu(action) {
|
|
225
|
+
const writer = new Writer(8);
|
|
226
|
+
writer.u16le(action);
|
|
227
|
+
writer.u16le(0); // grantId
|
|
228
|
+
writer.u32le(0); // controlId
|
|
229
|
+
return writer.take();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function fontListPdu() {
|
|
233
|
+
const writer = new Writer(8);
|
|
234
|
+
writer.u16le(0); // numberFonts
|
|
235
|
+
writer.u16le(0); // totalNumberFonts
|
|
236
|
+
writer.u16le(0x0003); // listFlags: first and last
|
|
237
|
+
writer.u16le(0x0032); // entrySize
|
|
238
|
+
return writer.take();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function confirmActivePdu(shareId: number, capabilities, capabilityCount) {
|
|
242
|
+
const sourceDescriptor = new TextEncoder().encode('MSTSC\0');
|
|
243
|
+
const writer = new Writer(capabilities.length + 32);
|
|
244
|
+
|
|
245
|
+
writer.u32le(shareId);
|
|
246
|
+
writer.u16le(0x03ea); // originatorId: the server channel
|
|
247
|
+
writer.u16le(sourceDescriptor.length);
|
|
248
|
+
writer.u16le(capabilities.length + 4); // lengthCombinedCapabilities
|
|
249
|
+
writer.raw(sourceDescriptor);
|
|
250
|
+
writer.u16le(capabilityCount);
|
|
251
|
+
writer.u16le(0); // pad
|
|
252
|
+
writer.raw(capabilities);
|
|
253
|
+
|
|
254
|
+
return writer.take();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** TS_INPUT_PDU_DATA: a batch of input events sharing one header. */
|
|
258
|
+
export function inputEventsPdu(events) {
|
|
259
|
+
const writer = new Writer(events.length * 12 + 4);
|
|
260
|
+
writer.u16le(events.length);
|
|
261
|
+
writer.u16le(0); // pad
|
|
262
|
+
for (const event of events) writer.raw(event);
|
|
263
|
+
return writer.take();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function inputEvent(messageType, write) {
|
|
267
|
+
const writer = new Writer(12);
|
|
268
|
+
writer.u32le(0); // eventTime, ignored by every host
|
|
269
|
+
writer.u16le(messageType);
|
|
270
|
+
write(writer);
|
|
271
|
+
return writer.take();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function scancodeEvent(scancode, pressed: boolean, extended) {
|
|
275
|
+
return inputEvent(INPUT_EVENT.SCANCODE, (writer: Writer) => {
|
|
276
|
+
writer.u16le(
|
|
277
|
+
(pressed ? KEYBOARD_FLAG.DOWN : KEYBOARD_FLAG.RELEASE) |
|
|
278
|
+
(extended ? KEYBOARD_FLAG.EXTENDED : 0)
|
|
279
|
+
);
|
|
280
|
+
writer.u16le(scancode);
|
|
281
|
+
writer.u16le(0);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function unicodeEvent(codeUnit, pressed: boolean) {
|
|
286
|
+
return inputEvent(INPUT_EVENT.UNICODE, (writer: Writer) => {
|
|
287
|
+
writer.u16le(pressed ? KEYBOARD_FLAG.DOWN : KEYBOARD_FLAG.RELEASE);
|
|
288
|
+
writer.u16le(codeUnit);
|
|
289
|
+
writer.u16le(0);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function mouseEvent(flags: number, x: number, y: number) {
|
|
294
|
+
return inputEvent(INPUT_EVENT.MOUSE, (writer: Writer) => {
|
|
295
|
+
writer.u16le(flags);
|
|
296
|
+
writer.u16le(Math.max(0, Math.min(0xffff, Math.round(x))));
|
|
297
|
+
writer.u16le(Math.max(0, Math.min(0xffff, Math.round(y))));
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function extendedMouseEvent(flags: number, x: number, y: number) {
|
|
302
|
+
return inputEvent(INPUT_EVENT.MOUSEX, (writer: Writer) => {
|
|
303
|
+
writer.u16le(flags);
|
|
304
|
+
writer.u16le(Math.max(0, Math.min(0xffff, Math.round(x))));
|
|
305
|
+
writer.u16le(Math.max(0, Math.min(0xffff, Math.round(y))));
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function syncEvent(toggleFlags: number) {
|
|
310
|
+
return inputEvent(INPUT_EVENT.SYNC, (writer: Writer) => {
|
|
311
|
+
writer.u16le(0);
|
|
312
|
+
writer.u32le(toggleFlags >>> 0);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function refreshRectPdu(rects) {
|
|
317
|
+
const writer = new Writer(4 + rects.length * 8);
|
|
318
|
+
writer.u8(rects.length);
|
|
319
|
+
writer.zeros(3); // pad
|
|
320
|
+
for (const rect of rects) {
|
|
321
|
+
writer.u16le(rect.left);
|
|
322
|
+
writer.u16le(rect.top);
|
|
323
|
+
writer.u16le(rect.right);
|
|
324
|
+
writer.u16le(rect.bottom);
|
|
325
|
+
}
|
|
326
|
+
return writer.take();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function suppressOutputPdu(allowDisplayUpdates, rect) {
|
|
330
|
+
const writer = new Writer(12);
|
|
331
|
+
writer.u8(allowDisplayUpdates ? 1 : 0);
|
|
332
|
+
writer.zeros(3); // pad
|
|
333
|
+
if (allowDisplayUpdates && rect) {
|
|
334
|
+
writer.u16le(rect.left);
|
|
335
|
+
writer.u16le(rect.top);
|
|
336
|
+
writer.u16le(rect.right);
|
|
337
|
+
writer.u16le(rect.bottom);
|
|
338
|
+
}
|
|
339
|
+
return writer.take();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// --- Inbound PDUs -----------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
export function parseShareControlHeader(bytes: Uint8Array) {
|
|
345
|
+
const reader = new Reader(bytes);
|
|
346
|
+
const totalLength = reader.u16le();
|
|
347
|
+
const typeAndVersion = reader.u16le();
|
|
348
|
+
const pduSource = reader.u16le();
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
totalLength,
|
|
352
|
+
pduType: typeAndVersion & 0x0f,
|
|
353
|
+
pduSource,
|
|
354
|
+
payload: reader.rest().subarray(0, Math.max(0, totalLength - 6)),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function parseShareDataHeader(bytes: Uint8Array) {
|
|
359
|
+
const reader = new Reader(bytes);
|
|
360
|
+
const shareId = reader.u32le();
|
|
361
|
+
reader.u8(); // pad
|
|
362
|
+
reader.u8(); // streamId
|
|
363
|
+
reader.u16le(); // uncompressedLength
|
|
364
|
+
const pduType2 = reader.u8();
|
|
365
|
+
const compressedType = reader.u8();
|
|
366
|
+
const compressedLength = reader.u16le();
|
|
367
|
+
|
|
368
|
+
return { shareId, pduType2, compressedType, compressedLength, payload: reader.rest() };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function parseDemandActive(bytes: Uint8Array) {
|
|
372
|
+
const reader = new Reader(bytes);
|
|
373
|
+
const shareId = reader.u32le();
|
|
374
|
+
const lengthSourceDescriptor = reader.u16le();
|
|
375
|
+
const lengthCombinedCapabilities = reader.u16le();
|
|
376
|
+
reader.skip(lengthSourceDescriptor);
|
|
377
|
+
|
|
378
|
+
const capabilities = reader.raw(Math.min(lengthCombinedCapabilities, reader.remaining));
|
|
379
|
+
return { shareId, capabilities };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const BITMAP_COMPRESSION = 0x0001;
|
|
383
|
+
const NO_BITMAP_COMPRESSION_HDR = 0x0400;
|
|
384
|
+
|
|
385
|
+
export function parseBitmapUpdate(reader: Reader) {
|
|
386
|
+
const count = reader.u16le();
|
|
387
|
+
const rects = [];
|
|
388
|
+
|
|
389
|
+
for (let i = 0; i < count && reader.remaining >= 18; i++) {
|
|
390
|
+
const left = reader.u16le();
|
|
391
|
+
const top = reader.u16le();
|
|
392
|
+
const right = reader.u16le();
|
|
393
|
+
const bottom = reader.u16le();
|
|
394
|
+
const width = reader.u16le();
|
|
395
|
+
const height = reader.u16le();
|
|
396
|
+
const bitsPerPixel = reader.u16le();
|
|
397
|
+
const flags = reader.u16le();
|
|
398
|
+
let bitmapLength = reader.u16le();
|
|
399
|
+
|
|
400
|
+
const compressed = Boolean(flags & BITMAP_COMPRESSION);
|
|
401
|
+
if (compressed && !(flags & NO_BITMAP_COMPRESSION_HDR)) {
|
|
402
|
+
// TS_CD_HEADER: the main body size is authoritative, and the eight header
|
|
403
|
+
// bytes are counted in bitmapLength.
|
|
404
|
+
reader.u16le(); // cbCompFirstRowSize, always zero
|
|
405
|
+
const mainBodySize = reader.u16le();
|
|
406
|
+
reader.u16le(); // cbScanWidth
|
|
407
|
+
reader.u16le(); // cbUncompressedSize
|
|
408
|
+
bitmapLength = mainBodySize;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
rects.push({
|
|
412
|
+
left,
|
|
413
|
+
top,
|
|
414
|
+
right,
|
|
415
|
+
bottom,
|
|
416
|
+
width,
|
|
417
|
+
height,
|
|
418
|
+
bitsPerPixel,
|
|
419
|
+
compressed,
|
|
420
|
+
data: reader.raw(Math.min(bitmapLength, reader.remaining)),
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return rects;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function parsePaletteUpdate(reader: Reader) {
|
|
428
|
+
reader.u16le(); // updateType, repeated
|
|
429
|
+
reader.u16le(); // pad
|
|
430
|
+
const count = reader.u32le();
|
|
431
|
+
const palette = new Uint8Array(256 * 3);
|
|
432
|
+
|
|
433
|
+
for (let i = 0; i < count && i < 256 && reader.remaining >= 3; i++) {
|
|
434
|
+
palette[i * 3] = reader.u8();
|
|
435
|
+
palette[i * 3 + 1] = reader.u8();
|
|
436
|
+
palette[i * 3 + 2] = reader.u8();
|
|
437
|
+
}
|
|
438
|
+
return palette;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function parseColorPointer(reader: Reader, xorBpp) {
|
|
442
|
+
const cacheIndex = reader.u16le();
|
|
443
|
+
const hotSpotX = reader.u16le();
|
|
444
|
+
const hotSpotY = reader.u16le();
|
|
445
|
+
const width = reader.u16le();
|
|
446
|
+
const height = reader.u16le();
|
|
447
|
+
const andMaskLength = reader.u16le();
|
|
448
|
+
const xorMaskLength = reader.u16le();
|
|
449
|
+
const xorMask = reader.raw(Math.min(xorMaskLength, reader.remaining)).slice();
|
|
450
|
+
const andMask = reader.raw(Math.min(andMaskLength, reader.remaining)).slice();
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
kind: 'bitmap',
|
|
454
|
+
cacheIndex,
|
|
455
|
+
hotSpotX,
|
|
456
|
+
hotSpotY,
|
|
457
|
+
width,
|
|
458
|
+
height,
|
|
459
|
+
xorBpp: xorBpp || 24,
|
|
460
|
+
xorMask,
|
|
461
|
+
andMask,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export function parsePointerUpdate(reader: Reader) {
|
|
466
|
+
const messageType = reader.u16le();
|
|
467
|
+
reader.u16le(); // pad
|
|
468
|
+
|
|
469
|
+
if (messageType === POINTER_MESSAGE.POSITION) {
|
|
470
|
+
return { kind: 'position', x: reader.u16le(), y: reader.u16le() };
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (messageType === POINTER_MESSAGE.SYSTEM) {
|
|
474
|
+
const systemPointerType = reader.u32le();
|
|
475
|
+
return { kind: 'system', hidden: systemPointerType === 0x00000000 };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (messageType === POINTER_MESSAGE.CACHED) {
|
|
479
|
+
return { kind: 'cached', cacheIndex: reader.u16le() };
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (messageType === POINTER_MESSAGE.COLOR) {
|
|
483
|
+
return parseColorPointer(reader, 24);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (messageType === POINTER_MESSAGE.POINTER) {
|
|
487
|
+
const xorBpp = reader.u16le();
|
|
488
|
+
return parseColorPointer(reader, xorBpp);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (messageType === POINTER_MESSAGE.LARGE) {
|
|
492
|
+
// Large pointers are only sent when advertised; treat as unsupported.
|
|
493
|
+
return { kind: 'unsupported', messageType };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return { kind: 'unsupported', messageType };
|
|
497
|
+
}
|