susi-qemu 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/bin/susi +9 -4
  3. data/lib/disk.rb +7 -5
  4. data/lib/novnc/core/base64.js +104 -0
  5. data/lib/novnc/core/crypto/aes.js +178 -0
  6. data/lib/novnc/core/crypto/bigint.js +34 -0
  7. data/lib/novnc/core/crypto/crypto.js +90 -0
  8. data/lib/novnc/core/crypto/des.js +330 -0
  9. data/lib/novnc/core/crypto/dh.js +55 -0
  10. data/lib/novnc/core/crypto/md5.js +82 -0
  11. data/lib/novnc/core/crypto/rsa.js +132 -0
  12. data/lib/novnc/core/decoders/copyrect.js +27 -0
  13. data/lib/novnc/core/decoders/h264.js +321 -0
  14. data/lib/novnc/core/decoders/hextile.js +181 -0
  15. data/lib/novnc/core/decoders/jpeg.js +146 -0
  16. data/lib/novnc/core/decoders/raw.js +59 -0
  17. data/lib/novnc/core/decoders/rre.js +44 -0
  18. data/lib/novnc/core/decoders/tight.js +393 -0
  19. data/lib/novnc/core/decoders/tightpng.js +27 -0
  20. data/lib/novnc/core/decoders/zlib.js +51 -0
  21. data/lib/novnc/core/decoders/zrle.js +185 -0
  22. data/lib/novnc/core/deflator.js +84 -0
  23. data/lib/novnc/core/display.js +575 -0
  24. data/lib/novnc/core/encodings.js +53 -0
  25. data/lib/novnc/core/inflator.js +65 -0
  26. data/lib/novnc/core/input/domkeytable.js +311 -0
  27. data/lib/novnc/core/input/fixedkeys.js +129 -0
  28. data/lib/novnc/core/input/gesturehandler.js +567 -0
  29. data/lib/novnc/core/input/keyboard.js +294 -0
  30. data/lib/novnc/core/input/keysym.js +616 -0
  31. data/lib/novnc/core/input/keysymdef.js +688 -0
  32. data/lib/novnc/core/input/util.js +191 -0
  33. data/lib/novnc/core/input/vkeys.js +116 -0
  34. data/lib/novnc/core/input/xtscancodes.js +173 -0
  35. data/lib/novnc/core/ra2.js +312 -0
  36. data/lib/novnc/core/rfb.js +3257 -0
  37. data/lib/novnc/core/util/browser.js +172 -0
  38. data/lib/novnc/core/util/cursor.js +249 -0
  39. data/lib/novnc/core/util/element.js +32 -0
  40. data/lib/novnc/core/util/events.js +138 -0
  41. data/lib/novnc/core/util/eventtarget.js +35 -0
  42. data/lib/novnc/core/util/int.js +15 -0
  43. data/lib/novnc/core/util/logging.js +56 -0
  44. data/lib/novnc/core/util/strings.js +28 -0
  45. data/lib/novnc/core/websock.js +365 -0
  46. data/lib/novnc/screen.html +21 -0
  47. data/lib/novnc/vendor/pako/lib/utils/common.js +45 -0
  48. data/lib/novnc/vendor/pako/lib/zlib/adler32.js +27 -0
  49. data/lib/novnc/vendor/pako/lib/zlib/constants.js +47 -0
  50. data/lib/novnc/vendor/pako/lib/zlib/crc32.js +36 -0
  51. data/lib/novnc/vendor/pako/lib/zlib/deflate.js +1846 -0
  52. data/lib/novnc/vendor/pako/lib/zlib/gzheader.js +35 -0
  53. data/lib/novnc/vendor/pako/lib/zlib/inffast.js +324 -0
  54. data/lib/novnc/vendor/pako/lib/zlib/inflate.js +1527 -0
  55. data/lib/novnc/vendor/pako/lib/zlib/inftrees.js +322 -0
  56. data/lib/novnc/vendor/pako/lib/zlib/messages.js +11 -0
  57. data/lib/novnc/vendor/pako/lib/zlib/trees.js +1195 -0
  58. data/lib/novnc/vendor/pako/lib/zlib/zstream.js +24 -0
  59. data/lib/output.rb +11 -0
  60. data/lib/qmp.rb +6 -0
  61. data/lib/ssh.rb +3 -1
  62. data/lib/susi.rb +7 -6
  63. data/lib/version.rb +1 -1
  64. data/lib/vm.rb +36 -13
  65. data/lib/vnc.rb +34 -30
  66. metadata +57 -1
@@ -0,0 +1,321 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2024 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ import * as Log from '../util/logging.js';
11
+
12
+ export class H264Parser {
13
+ constructor(data) {
14
+ this._data = data;
15
+ this._index = 0;
16
+ this.profileIdc = null;
17
+ this.constraintSet = null;
18
+ this.levelIdc = null;
19
+ }
20
+
21
+ _getStartSequenceLen(index) {
22
+ let data = this._data;
23
+ if (data[index + 0] == 0 && data[index + 1] == 0 && data[index + 2] == 0 && data[index + 3] == 1) {
24
+ return 4;
25
+ }
26
+ if (data[index + 0] == 0 && data[index + 1] == 0 && data[index + 2] == 1) {
27
+ return 3;
28
+ }
29
+ return 0;
30
+ }
31
+
32
+ _indexOfNextNalUnit(index) {
33
+ let data = this._data;
34
+ for (let i = index; i < data.length; ++i) {
35
+ if (this._getStartSequenceLen(i) != 0) {
36
+ return i;
37
+ }
38
+ }
39
+ return -1;
40
+ }
41
+
42
+ _parseSps(index) {
43
+ this.profileIdc = this._data[index];
44
+ this.constraintSet = this._data[index + 1];
45
+ this.levelIdc = this._data[index + 2];
46
+ }
47
+
48
+ _parseNalUnit(index) {
49
+ const firstByte = this._data[index];
50
+ if (firstByte & 0x80) {
51
+ throw new Error('H264 parsing sanity check failed, forbidden zero bit is set');
52
+ }
53
+ const unitType = firstByte & 0x1f;
54
+
55
+ switch (unitType) {
56
+ case 1: // coded slice, non-idr
57
+ return { slice: true };
58
+ case 5: // coded slice, idr
59
+ return { slice: true, key: true };
60
+ case 6: // sei
61
+ return {};
62
+ case 7: // sps
63
+ this._parseSps(index + 1);
64
+ return {};
65
+ case 8: // pps
66
+ return {};
67
+ default:
68
+ Log.Warn("Unhandled unit type: ", unitType);
69
+ break;
70
+ }
71
+ return {};
72
+ }
73
+
74
+ parse() {
75
+ const startIndex = this._index;
76
+ let isKey = false;
77
+
78
+ while (this._index < this._data.length) {
79
+ const startSequenceLen = this._getStartSequenceLen(this._index);
80
+ if (startSequenceLen == 0) {
81
+ throw new Error('Invalid start sequence in bit stream');
82
+ }
83
+
84
+ const { slice, key } = this._parseNalUnit(this._index + startSequenceLen);
85
+
86
+ let nextIndex = this._indexOfNextNalUnit(this._index + startSequenceLen);
87
+ if (nextIndex == -1) {
88
+ this._index = this._data.length;
89
+ } else {
90
+ this._index = nextIndex;
91
+ }
92
+
93
+ if (key) {
94
+ isKey = true;
95
+ }
96
+ if (slice) {
97
+ break;
98
+ }
99
+ }
100
+
101
+ if (startIndex === this._index) {
102
+ return null;
103
+ }
104
+
105
+ return {
106
+ frame: this._data.subarray(startIndex, this._index),
107
+ key: isKey,
108
+ };
109
+ }
110
+ }
111
+
112
+ export class H264Context {
113
+ constructor(width, height) {
114
+ this.lastUsed = 0;
115
+ this._width = width;
116
+ this._height = height;
117
+ this._profileIdc = null;
118
+ this._constraintSet = null;
119
+ this._levelIdc = null;
120
+ this._decoder = null;
121
+ this._pendingFrames = [];
122
+ }
123
+
124
+ _handleFrame(frame) {
125
+ let pending = this._pendingFrames.shift();
126
+ if (pending === undefined) {
127
+ throw new Error("Pending frame queue empty when receiving frame from decoder");
128
+ }
129
+
130
+ if (pending.timestamp != frame.timestamp) {
131
+ throw new Error("Video frame timestamp mismatch. Expected " +
132
+ frame.timestamp + " but but got " + pending.timestamp);
133
+ }
134
+
135
+ pending.frame = frame;
136
+ pending.ready = true;
137
+ pending.resolve();
138
+
139
+ if (!pending.keep) {
140
+ frame.close();
141
+ }
142
+ }
143
+
144
+ _handleError(e) {
145
+ throw new Error("Failed to decode frame: " + e.message);
146
+ }
147
+
148
+ _configureDecoder(profileIdc, constraintSet, levelIdc) {
149
+ if (this._decoder === null || this._decoder.state === 'closed') {
150
+ this._decoder = new VideoDecoder({
151
+ output: frame => this._handleFrame(frame),
152
+ error: e => this._handleError(e),
153
+ });
154
+ }
155
+ const codec = 'avc1.' +
156
+ profileIdc.toString(16).padStart(2, '0') +
157
+ constraintSet.toString(16).padStart(2, '0') +
158
+ levelIdc.toString(16).padStart(2, '0');
159
+ this._decoder.configure({
160
+ codec: codec,
161
+ codedWidth: this._width,
162
+ codedHeight: this._height,
163
+ optimizeForLatency: true,
164
+ });
165
+ }
166
+
167
+ _preparePendingFrame(timestamp) {
168
+ let pending = {
169
+ timestamp: timestamp,
170
+ promise: null,
171
+ resolve: null,
172
+ frame: null,
173
+ ready: false,
174
+ keep: false,
175
+ };
176
+ pending.promise = new Promise((resolve) => {
177
+ pending.resolve = resolve;
178
+ });
179
+ this._pendingFrames.push(pending);
180
+
181
+ return pending;
182
+ }
183
+
184
+ decode(payload) {
185
+ let parser = new H264Parser(payload);
186
+ let result = null;
187
+
188
+ // Ideally, this timestamp should come from the server, but we'll just
189
+ // approximate it instead.
190
+ let timestamp = Math.round(window.performance.now() * 1e3);
191
+
192
+ while (true) {
193
+ let encodedFrame = parser.parse();
194
+ if (encodedFrame === null) {
195
+ break;
196
+ }
197
+
198
+ if (parser.profileIdc !== null) {
199
+ self._profileIdc = parser.profileIdc;
200
+ self._constraintSet = parser.constraintSet;
201
+ self._levelIdc = parser.levelIdc;
202
+ }
203
+
204
+ if (this._decoder === null || this._decoder.state !== 'configured') {
205
+ if (!encodedFrame.key) {
206
+ Log.Warn("Missing key frame. Can't decode until one arrives");
207
+ continue;
208
+ }
209
+ if (self._profileIdc === null) {
210
+ Log.Warn('Cannot config decoder. Have not received SPS and PPS yet.');
211
+ continue;
212
+ }
213
+ this._configureDecoder(self._profileIdc, self._constraintSet,
214
+ self._levelIdc);
215
+ }
216
+
217
+ result = this._preparePendingFrame(timestamp);
218
+
219
+ const chunk = new EncodedVideoChunk({
220
+ timestamp: timestamp,
221
+ type: encodedFrame.key ? 'key' : 'delta',
222
+ data: encodedFrame.frame,
223
+ });
224
+
225
+ try {
226
+ this._decoder.decode(chunk);
227
+ } catch (e) {
228
+ Log.Warn("Failed to decode:", e);
229
+ }
230
+ }
231
+
232
+ // We only keep last frame of each payload
233
+ if (result !== null) {
234
+ result.keep = true;
235
+ }
236
+
237
+ return result;
238
+ }
239
+ }
240
+
241
+ export default class H264Decoder {
242
+ constructor() {
243
+ this._tick = 0;
244
+ this._contexts = {};
245
+ }
246
+
247
+ _contextId(x, y, width, height) {
248
+ return [x, y, width, height].join(',');
249
+ }
250
+
251
+ _findOldestContextId() {
252
+ let oldestTick = Number.MAX_VALUE;
253
+ let oldestKey = undefined;
254
+ for (const [key, value] of Object.entries(this._contexts)) {
255
+ if (value.lastUsed < oldestTick) {
256
+ oldestTick = value.lastUsed;
257
+ oldestKey = key;
258
+ }
259
+ }
260
+ return oldestKey;
261
+ }
262
+
263
+ _createContext(x, y, width, height) {
264
+ const maxContexts = 64;
265
+ if (Object.keys(this._contexts).length >= maxContexts) {
266
+ let oldestContextId = this._findOldestContextId();
267
+ delete this._contexts[oldestContextId];
268
+ }
269
+ let context = new H264Context(width, height);
270
+ this._contexts[this._contextId(x, y, width, height)] = context;
271
+ return context;
272
+ }
273
+
274
+ _getContext(x, y, width, height) {
275
+ let context = this._contexts[this._contextId(x, y, width, height)];
276
+ return context !== undefined ? context : this._createContext(x, y, width, height);
277
+ }
278
+
279
+ _resetContext(x, y, width, height) {
280
+ delete this._contexts[this._contextId(x, y, width, height)];
281
+ }
282
+
283
+ _resetAllContexts() {
284
+ this._contexts = {};
285
+ }
286
+
287
+ decodeRect(x, y, width, height, sock, display, depth) {
288
+ const resetContextFlag = 1;
289
+ const resetAllContextsFlag = 2;
290
+
291
+ if (sock.rQwait("h264 header", 8)) {
292
+ return false;
293
+ }
294
+
295
+ const length = sock.rQshift32();
296
+ const flags = sock.rQshift32();
297
+
298
+ if (sock.rQwait("h264 payload", length, 8)) {
299
+ return false;
300
+ }
301
+
302
+ if (flags & resetAllContextsFlag) {
303
+ this._resetAllContexts();
304
+ } else if (flags & resetContextFlag) {
305
+ this._resetContext(x, y, width, height);
306
+ }
307
+
308
+ let context = this._getContext(x, y, width, height);
309
+ context.lastUsed = this._tick++;
310
+
311
+ if (length !== 0) {
312
+ let payload = sock.rQshiftBytes(length, false);
313
+ let frame = context.decode(payload);
314
+ if (frame !== null) {
315
+ display.videoFrame(x, y, width, height, frame);
316
+ }
317
+ }
318
+
319
+ return true;
320
+ }
321
+ }
@@ -0,0 +1,181 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ import * as Log from '../util/logging.js';
11
+
12
+ export default class HextileDecoder {
13
+ constructor() {
14
+ this._tiles = 0;
15
+ this._lastsubencoding = 0;
16
+ this._tileBuffer = new Uint8Array(16 * 16 * 4);
17
+ }
18
+
19
+ decodeRect(x, y, width, height, sock, display, depth) {
20
+ if (this._tiles === 0) {
21
+ this._tilesX = Math.ceil(width / 16);
22
+ this._tilesY = Math.ceil(height / 16);
23
+ this._totalTiles = this._tilesX * this._tilesY;
24
+ this._tiles = this._totalTiles;
25
+ }
26
+
27
+ while (this._tiles > 0) {
28
+ let bytes = 1;
29
+
30
+ if (sock.rQwait("HEXTILE", bytes)) {
31
+ return false;
32
+ }
33
+
34
+ let subencoding = sock.rQpeek8();
35
+ if (subencoding > 30) { // Raw
36
+ throw new Error("Illegal hextile subencoding (subencoding: " +
37
+ subencoding + ")");
38
+ }
39
+
40
+ const currTile = this._totalTiles - this._tiles;
41
+ const tileX = currTile % this._tilesX;
42
+ const tileY = Math.floor(currTile / this._tilesX);
43
+ const tx = x + tileX * 16;
44
+ const ty = y + tileY * 16;
45
+ const tw = Math.min(16, (x + width) - tx);
46
+ const th = Math.min(16, (y + height) - ty);
47
+
48
+ // Figure out how much we are expecting
49
+ if (subencoding & 0x01) { // Raw
50
+ bytes += tw * th * 4;
51
+ } else {
52
+ if (subencoding & 0x02) { // Background
53
+ bytes += 4;
54
+ }
55
+ if (subencoding & 0x04) { // Foreground
56
+ bytes += 4;
57
+ }
58
+ if (subencoding & 0x08) { // AnySubrects
59
+ bytes++; // Since we aren't shifting it off
60
+
61
+ if (sock.rQwait("HEXTILE", bytes)) {
62
+ return false;
63
+ }
64
+
65
+ let subrects = sock.rQpeekBytes(bytes).at(-1);
66
+ if (subencoding & 0x10) { // SubrectsColoured
67
+ bytes += subrects * (4 + 2);
68
+ } else {
69
+ bytes += subrects * 2;
70
+ }
71
+ }
72
+ }
73
+
74
+ if (sock.rQwait("HEXTILE", bytes)) {
75
+ return false;
76
+ }
77
+
78
+ // We know the encoding and have a whole tile
79
+ sock.rQshift8();
80
+ if (subencoding === 0) {
81
+ if (this._lastsubencoding & 0x01) {
82
+ // Weird: ignore blanks are RAW
83
+ Log.Debug(" Ignoring blank after RAW");
84
+ } else {
85
+ display.fillRect(tx, ty, tw, th, this._background);
86
+ }
87
+ } else if (subencoding & 0x01) { // Raw
88
+ let pixels = tw * th;
89
+ let data = sock.rQshiftBytes(pixels * 4, false);
90
+ // Max sure the image is fully opaque
91
+ for (let i = 0;i < pixels;i++) {
92
+ data[i * 4 + 3] = 255;
93
+ }
94
+ display.blitImage(tx, ty, tw, th, data, 0);
95
+ } else {
96
+ if (subencoding & 0x02) { // Background
97
+ this._background = new Uint8Array(sock.rQshiftBytes(4));
98
+ }
99
+ if (subencoding & 0x04) { // Foreground
100
+ this._foreground = new Uint8Array(sock.rQshiftBytes(4));
101
+ }
102
+
103
+ this._startTile(tx, ty, tw, th, this._background);
104
+ if (subencoding & 0x08) { // AnySubrects
105
+ let subrects = sock.rQshift8();
106
+
107
+ for (let s = 0; s < subrects; s++) {
108
+ let color;
109
+ if (subencoding & 0x10) { // SubrectsColoured
110
+ color = sock.rQshiftBytes(4);
111
+ } else {
112
+ color = this._foreground;
113
+ }
114
+ const xy = sock.rQshift8();
115
+ const sx = (xy >> 4);
116
+ const sy = (xy & 0x0f);
117
+
118
+ const wh = sock.rQshift8();
119
+ const sw = (wh >> 4) + 1;
120
+ const sh = (wh & 0x0f) + 1;
121
+
122
+ this._subTile(sx, sy, sw, sh, color);
123
+ }
124
+ }
125
+ this._finishTile(display);
126
+ }
127
+ this._lastsubencoding = subencoding;
128
+ this._tiles--;
129
+ }
130
+
131
+ return true;
132
+ }
133
+
134
+ // start updating a tile
135
+ _startTile(x, y, width, height, color) {
136
+ this._tileX = x;
137
+ this._tileY = y;
138
+ this._tileW = width;
139
+ this._tileH = height;
140
+
141
+ const red = color[0];
142
+ const green = color[1];
143
+ const blue = color[2];
144
+
145
+ const data = this._tileBuffer;
146
+ for (let i = 0; i < width * height * 4; i += 4) {
147
+ data[i] = red;
148
+ data[i + 1] = green;
149
+ data[i + 2] = blue;
150
+ data[i + 3] = 255;
151
+ }
152
+ }
153
+
154
+ // update sub-rectangle of the current tile
155
+ _subTile(x, y, w, h, color) {
156
+ const red = color[0];
157
+ const green = color[1];
158
+ const blue = color[2];
159
+ const xend = x + w;
160
+ const yend = y + h;
161
+
162
+ const data = this._tileBuffer;
163
+ const width = this._tileW;
164
+ for (let j = y; j < yend; j++) {
165
+ for (let i = x; i < xend; i++) {
166
+ const p = (i + (j * width)) * 4;
167
+ data[p] = red;
168
+ data[p + 1] = green;
169
+ data[p + 2] = blue;
170
+ data[p + 3] = 255;
171
+ }
172
+ }
173
+ }
174
+
175
+ // draw the current tile to the screen
176
+ _finishTile(display) {
177
+ display.blitImage(this._tileX, this._tileY,
178
+ this._tileW, this._tileH,
179
+ this._tileBuffer, 0);
180
+ }
181
+ }
@@ -0,0 +1,146 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ export default class JPEGDecoder {
11
+ constructor() {
12
+ // RealVNC will reuse the quantization tables
13
+ // and Huffman tables, so we need to cache them.
14
+ this._cachedQuantTables = [];
15
+ this._cachedHuffmanTables = [];
16
+
17
+ this._segments = [];
18
+ }
19
+
20
+ decodeRect(x, y, width, height, sock, display, depth) {
21
+ // A rect of JPEG encodings is simply a JPEG file
22
+ while (true) {
23
+ let segment = this._readSegment(sock);
24
+ if (segment === null) {
25
+ return false;
26
+ }
27
+ this._segments.push(segment);
28
+ // End of image?
29
+ if (segment[1] === 0xD9) {
30
+ break;
31
+ }
32
+ }
33
+
34
+ let huffmanTables = [];
35
+ let quantTables = [];
36
+ for (let segment of this._segments) {
37
+ let type = segment[1];
38
+ if (type === 0xC4) {
39
+ // Huffman tables
40
+ huffmanTables.push(segment);
41
+ } else if (type === 0xDB) {
42
+ // Quantization tables
43
+ quantTables.push(segment);
44
+ }
45
+ }
46
+
47
+ const sofIndex = this._segments.findIndex(
48
+ x => x[1] == 0xC0 || x[1] == 0xC2
49
+ );
50
+ if (sofIndex == -1) {
51
+ throw new Error("Illegal JPEG image without SOF");
52
+ }
53
+
54
+ if (quantTables.length === 0) {
55
+ this._segments.splice(sofIndex+1, 0,
56
+ ...this._cachedQuantTables);
57
+ }
58
+ if (huffmanTables.length === 0) {
59
+ this._segments.splice(sofIndex+1, 0,
60
+ ...this._cachedHuffmanTables);
61
+ }
62
+
63
+ let length = 0;
64
+ for (let segment of this._segments) {
65
+ length += segment.length;
66
+ }
67
+
68
+ let data = new Uint8Array(length);
69
+ length = 0;
70
+ for (let segment of this._segments) {
71
+ data.set(segment, length);
72
+ length += segment.length;
73
+ }
74
+
75
+ display.imageRect(x, y, width, height, "image/jpeg", data);
76
+
77
+ if (huffmanTables.length !== 0) {
78
+ this._cachedHuffmanTables = huffmanTables;
79
+ }
80
+ if (quantTables.length !== 0) {
81
+ this._cachedQuantTables = quantTables;
82
+ }
83
+
84
+ this._segments = [];
85
+
86
+ return true;
87
+ }
88
+
89
+ _readSegment(sock) {
90
+ if (sock.rQwait("JPEG", 2)) {
91
+ return null;
92
+ }
93
+
94
+ let marker = sock.rQshift8();
95
+ if (marker != 0xFF) {
96
+ throw new Error("Illegal JPEG marker received (byte: " +
97
+ marker + ")");
98
+ }
99
+ let type = sock.rQshift8();
100
+ if (type >= 0xD0 && type <= 0xD9 || type == 0x01) {
101
+ // No length after marker
102
+ return new Uint8Array([marker, type]);
103
+ }
104
+
105
+ if (sock.rQwait("JPEG", 2, 2)) {
106
+ return null;
107
+ }
108
+
109
+ let length = sock.rQshift16();
110
+ if (length < 2) {
111
+ throw new Error("Illegal JPEG length received (length: " +
112
+ length + ")");
113
+ }
114
+
115
+ if (sock.rQwait("JPEG", length-2, 4)) {
116
+ return null;
117
+ }
118
+
119
+ let extra = 0;
120
+ if (type === 0xDA) {
121
+ // start of scan
122
+ extra += 2;
123
+ while (true) {
124
+ if (sock.rQwait("JPEG", length-2+extra, 4)) {
125
+ return null;
126
+ }
127
+ let data = sock.rQpeekBytes(length-2+extra, false);
128
+ if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 &&
129
+ !(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) {
130
+ extra -= 2;
131
+ break;
132
+ }
133
+ extra++;
134
+ }
135
+ }
136
+
137
+ let segment = new Uint8Array(2 + length + extra);
138
+ segment[0] = marker;
139
+ segment[1] = type;
140
+ segment[2] = length >> 8;
141
+ segment[3] = length;
142
+ segment.set(sock.rQshiftBytes(length-2+extra, false), 4);
143
+
144
+ return segment;
145
+ }
146
+ }
@@ -0,0 +1,59 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 The noVNC Authors
4
+ * Licensed under MPL 2.0 (see LICENSE.txt)
5
+ *
6
+ * See README.md for usage and integration instructions.
7
+ *
8
+ */
9
+
10
+ export default class RawDecoder {
11
+ constructor() {
12
+ this._lines = 0;
13
+ }
14
+
15
+ decodeRect(x, y, width, height, sock, display, depth) {
16
+ if ((width === 0) || (height === 0)) {
17
+ return true;
18
+ }
19
+
20
+ if (this._lines === 0) {
21
+ this._lines = height;
22
+ }
23
+
24
+ const pixelSize = depth == 8 ? 1 : 4;
25
+ const bytesPerLine = width * pixelSize;
26
+
27
+ while (this._lines > 0) {
28
+ if (sock.rQwait("RAW", bytesPerLine)) {
29
+ return false;
30
+ }
31
+
32
+ const curY = y + (height - this._lines);
33
+
34
+ let data = sock.rQshiftBytes(bytesPerLine, false);
35
+
36
+ // Convert data if needed
37
+ if (depth == 8) {
38
+ const newdata = new Uint8Array(width * 4);
39
+ for (let i = 0; i < width; i++) {
40
+ newdata[i * 4 + 0] = ((data[i] >> 0) & 0x3) * 255 / 3;
41
+ newdata[i * 4 + 1] = ((data[i] >> 2) & 0x3) * 255 / 3;
42
+ newdata[i * 4 + 2] = ((data[i] >> 4) & 0x3) * 255 / 3;
43
+ newdata[i * 4 + 3] = 255;
44
+ }
45
+ data = newdata;
46
+ }
47
+
48
+ // Max sure the image is fully opaque
49
+ for (let i = 0; i < width; i++) {
50
+ data[i * 4 + 3] = 255;
51
+ }
52
+
53
+ display.blitImage(x, curY, width, 1, data, 0);
54
+ this._lines--;
55
+ }
56
+
57
+ return true;
58
+ }
59
+ }