susi-qemu 0.0.2 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/susi +9 -4
- data/lib/disk.rb +7 -5
- data/lib/novnc/core/base64.js +104 -0
- data/lib/novnc/core/crypto/aes.js +178 -0
- data/lib/novnc/core/crypto/bigint.js +34 -0
- data/lib/novnc/core/crypto/crypto.js +90 -0
- data/lib/novnc/core/crypto/des.js +330 -0
- data/lib/novnc/core/crypto/dh.js +55 -0
- data/lib/novnc/core/crypto/md5.js +82 -0
- data/lib/novnc/core/crypto/rsa.js +132 -0
- data/lib/novnc/core/decoders/copyrect.js +27 -0
- data/lib/novnc/core/decoders/h264.js +321 -0
- data/lib/novnc/core/decoders/hextile.js +181 -0
- data/lib/novnc/core/decoders/jpeg.js +146 -0
- data/lib/novnc/core/decoders/raw.js +59 -0
- data/lib/novnc/core/decoders/rre.js +44 -0
- data/lib/novnc/core/decoders/tight.js +393 -0
- data/lib/novnc/core/decoders/tightpng.js +27 -0
- data/lib/novnc/core/decoders/zlib.js +51 -0
- data/lib/novnc/core/decoders/zrle.js +185 -0
- data/lib/novnc/core/deflator.js +84 -0
- data/lib/novnc/core/display.js +575 -0
- data/lib/novnc/core/encodings.js +53 -0
- data/lib/novnc/core/inflator.js +65 -0
- data/lib/novnc/core/input/domkeytable.js +311 -0
- data/lib/novnc/core/input/fixedkeys.js +129 -0
- data/lib/novnc/core/input/gesturehandler.js +567 -0
- data/lib/novnc/core/input/keyboard.js +294 -0
- data/lib/novnc/core/input/keysym.js +616 -0
- data/lib/novnc/core/input/keysymdef.js +688 -0
- data/lib/novnc/core/input/util.js +191 -0
- data/lib/novnc/core/input/vkeys.js +116 -0
- data/lib/novnc/core/input/xtscancodes.js +173 -0
- data/lib/novnc/core/ra2.js +312 -0
- data/lib/novnc/core/rfb.js +3257 -0
- data/lib/novnc/core/util/browser.js +172 -0
- data/lib/novnc/core/util/cursor.js +249 -0
- data/lib/novnc/core/util/element.js +32 -0
- data/lib/novnc/core/util/events.js +138 -0
- data/lib/novnc/core/util/eventtarget.js +35 -0
- data/lib/novnc/core/util/int.js +15 -0
- data/lib/novnc/core/util/logging.js +56 -0
- data/lib/novnc/core/util/strings.js +28 -0
- data/lib/novnc/core/websock.js +365 -0
- data/lib/novnc/screen.html +21 -0
- data/lib/novnc/vendor/pako/lib/utils/common.js +45 -0
- data/lib/novnc/vendor/pako/lib/zlib/adler32.js +27 -0
- data/lib/novnc/vendor/pako/lib/zlib/constants.js +47 -0
- data/lib/novnc/vendor/pako/lib/zlib/crc32.js +36 -0
- data/lib/novnc/vendor/pako/lib/zlib/deflate.js +1846 -0
- data/lib/novnc/vendor/pako/lib/zlib/gzheader.js +35 -0
- data/lib/novnc/vendor/pako/lib/zlib/inffast.js +324 -0
- data/lib/novnc/vendor/pako/lib/zlib/inflate.js +1527 -0
- data/lib/novnc/vendor/pako/lib/zlib/inftrees.js +322 -0
- data/lib/novnc/vendor/pako/lib/zlib/messages.js +11 -0
- data/lib/novnc/vendor/pako/lib/zlib/trees.js +1195 -0
- data/lib/novnc/vendor/pako/lib/zlib/zstream.js +24 -0
- data/lib/output.rb +11 -0
- data/lib/qmp.rb +6 -0
- data/lib/ssh.rb +3 -1
- data/lib/susi.rb +7 -6
- data/lib/version.rb +1 -1
- data/lib/vm.rb +44 -26
- data/lib/vnc.rb +34 -31
- metadata +57 -1
@@ -0,0 +1,44 @@
|
|
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 RREDecoder {
|
11
|
+
constructor() {
|
12
|
+
this._subrects = 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
decodeRect(x, y, width, height, sock, display, depth) {
|
16
|
+
if (this._subrects === 0) {
|
17
|
+
if (sock.rQwait("RRE", 4 + 4)) {
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
|
21
|
+
this._subrects = sock.rQshift32();
|
22
|
+
|
23
|
+
let color = sock.rQshiftBytes(4); // Background
|
24
|
+
display.fillRect(x, y, width, height, color);
|
25
|
+
}
|
26
|
+
|
27
|
+
while (this._subrects > 0) {
|
28
|
+
if (sock.rQwait("RRE", 4 + 8)) {
|
29
|
+
return false;
|
30
|
+
}
|
31
|
+
|
32
|
+
let color = sock.rQshiftBytes(4);
|
33
|
+
let sx = sock.rQshift16();
|
34
|
+
let sy = sock.rQshift16();
|
35
|
+
let swidth = sock.rQshift16();
|
36
|
+
let sheight = sock.rQshift16();
|
37
|
+
display.fillRect(x + sx, y + sy, swidth, sheight, color);
|
38
|
+
|
39
|
+
this._subrects--;
|
40
|
+
}
|
41
|
+
|
42
|
+
return true;
|
43
|
+
}
|
44
|
+
}
|
@@ -0,0 +1,393 @@
|
|
1
|
+
/*
|
2
|
+
* noVNC: HTML5 VNC client
|
3
|
+
* Copyright (C) 2019 The noVNC Authors
|
4
|
+
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
5
|
+
* Licensed under MPL 2.0 (see LICENSE.txt)
|
6
|
+
*
|
7
|
+
* See README.md for usage and integration instructions.
|
8
|
+
*
|
9
|
+
*/
|
10
|
+
|
11
|
+
import * as Log from '../util/logging.js';
|
12
|
+
import Inflator from "../inflator.js";
|
13
|
+
|
14
|
+
export default class TightDecoder {
|
15
|
+
constructor() {
|
16
|
+
this._ctl = null;
|
17
|
+
this._filter = null;
|
18
|
+
this._numColors = 0;
|
19
|
+
this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
|
20
|
+
this._len = 0;
|
21
|
+
|
22
|
+
this._zlibs = [];
|
23
|
+
for (let i = 0; i < 4; i++) {
|
24
|
+
this._zlibs[i] = new Inflator();
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
decodeRect(x, y, width, height, sock, display, depth) {
|
29
|
+
if (this._ctl === null) {
|
30
|
+
if (sock.rQwait("TIGHT compression-control", 1)) {
|
31
|
+
return false;
|
32
|
+
}
|
33
|
+
|
34
|
+
this._ctl = sock.rQshift8();
|
35
|
+
|
36
|
+
// Reset streams if the server requests it
|
37
|
+
for (let i = 0; i < 4; i++) {
|
38
|
+
if ((this._ctl >> i) & 1) {
|
39
|
+
this._zlibs[i].reset();
|
40
|
+
Log.Info("Reset zlib stream " + i);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
// Figure out filter
|
45
|
+
this._ctl = this._ctl >> 4;
|
46
|
+
}
|
47
|
+
|
48
|
+
let ret;
|
49
|
+
|
50
|
+
if (this._ctl === 0x08) {
|
51
|
+
ret = this._fillRect(x, y, width, height,
|
52
|
+
sock, display, depth);
|
53
|
+
} else if (this._ctl === 0x09) {
|
54
|
+
ret = this._jpegRect(x, y, width, height,
|
55
|
+
sock, display, depth);
|
56
|
+
} else if (this._ctl === 0x0A) {
|
57
|
+
ret = this._pngRect(x, y, width, height,
|
58
|
+
sock, display, depth);
|
59
|
+
} else if ((this._ctl & 0x08) == 0) {
|
60
|
+
ret = this._basicRect(this._ctl, x, y, width, height,
|
61
|
+
sock, display, depth);
|
62
|
+
} else {
|
63
|
+
throw new Error("Illegal tight compression received (ctl: " +
|
64
|
+
this._ctl + ")");
|
65
|
+
}
|
66
|
+
|
67
|
+
if (ret) {
|
68
|
+
this._ctl = null;
|
69
|
+
}
|
70
|
+
|
71
|
+
return ret;
|
72
|
+
}
|
73
|
+
|
74
|
+
_fillRect(x, y, width, height, sock, display, depth) {
|
75
|
+
if (sock.rQwait("TIGHT", 3)) {
|
76
|
+
return false;
|
77
|
+
}
|
78
|
+
|
79
|
+
let pixel = sock.rQshiftBytes(3);
|
80
|
+
display.fillRect(x, y, width, height, pixel, false);
|
81
|
+
|
82
|
+
return true;
|
83
|
+
}
|
84
|
+
|
85
|
+
_jpegRect(x, y, width, height, sock, display, depth) {
|
86
|
+
let data = this._readData(sock);
|
87
|
+
if (data === null) {
|
88
|
+
return false;
|
89
|
+
}
|
90
|
+
|
91
|
+
display.imageRect(x, y, width, height, "image/jpeg", data);
|
92
|
+
|
93
|
+
return true;
|
94
|
+
}
|
95
|
+
|
96
|
+
_pngRect(x, y, width, height, sock, display, depth) {
|
97
|
+
throw new Error("PNG received in standard Tight rect");
|
98
|
+
}
|
99
|
+
|
100
|
+
_basicRect(ctl, x, y, width, height, sock, display, depth) {
|
101
|
+
if (this._filter === null) {
|
102
|
+
if (ctl & 0x4) {
|
103
|
+
if (sock.rQwait("TIGHT", 1)) {
|
104
|
+
return false;
|
105
|
+
}
|
106
|
+
|
107
|
+
this._filter = sock.rQshift8();
|
108
|
+
} else {
|
109
|
+
// Implicit CopyFilter
|
110
|
+
this._filter = 0;
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
let streamId = ctl & 0x3;
|
115
|
+
|
116
|
+
let ret;
|
117
|
+
|
118
|
+
switch (this._filter) {
|
119
|
+
case 0: // CopyFilter
|
120
|
+
ret = this._copyFilter(streamId, x, y, width, height,
|
121
|
+
sock, display, depth);
|
122
|
+
break;
|
123
|
+
case 1: // PaletteFilter
|
124
|
+
ret = this._paletteFilter(streamId, x, y, width, height,
|
125
|
+
sock, display, depth);
|
126
|
+
break;
|
127
|
+
case 2: // GradientFilter
|
128
|
+
ret = this._gradientFilter(streamId, x, y, width, height,
|
129
|
+
sock, display, depth);
|
130
|
+
break;
|
131
|
+
default:
|
132
|
+
throw new Error("Illegal tight filter received (ctl: " +
|
133
|
+
this._filter + ")");
|
134
|
+
}
|
135
|
+
|
136
|
+
if (ret) {
|
137
|
+
this._filter = null;
|
138
|
+
}
|
139
|
+
|
140
|
+
return ret;
|
141
|
+
}
|
142
|
+
|
143
|
+
_copyFilter(streamId, x, y, width, height, sock, display, depth) {
|
144
|
+
const uncompressedSize = width * height * 3;
|
145
|
+
let data;
|
146
|
+
|
147
|
+
if (uncompressedSize === 0) {
|
148
|
+
return true;
|
149
|
+
}
|
150
|
+
|
151
|
+
if (uncompressedSize < 12) {
|
152
|
+
if (sock.rQwait("TIGHT", uncompressedSize)) {
|
153
|
+
return false;
|
154
|
+
}
|
155
|
+
|
156
|
+
data = sock.rQshiftBytes(uncompressedSize);
|
157
|
+
} else {
|
158
|
+
data = this._readData(sock);
|
159
|
+
if (data === null) {
|
160
|
+
return false;
|
161
|
+
}
|
162
|
+
|
163
|
+
this._zlibs[streamId].setInput(data);
|
164
|
+
data = this._zlibs[streamId].inflate(uncompressedSize);
|
165
|
+
this._zlibs[streamId].setInput(null);
|
166
|
+
}
|
167
|
+
|
168
|
+
let rgbx = new Uint8Array(width * height * 4);
|
169
|
+
for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {
|
170
|
+
rgbx[i] = data[j];
|
171
|
+
rgbx[i + 1] = data[j + 1];
|
172
|
+
rgbx[i + 2] = data[j + 2];
|
173
|
+
rgbx[i + 3] = 255; // Alpha
|
174
|
+
}
|
175
|
+
|
176
|
+
display.blitImage(x, y, width, height, rgbx, 0, false);
|
177
|
+
|
178
|
+
return true;
|
179
|
+
}
|
180
|
+
|
181
|
+
_paletteFilter(streamId, x, y, width, height, sock, display, depth) {
|
182
|
+
if (this._numColors === 0) {
|
183
|
+
if (sock.rQwait("TIGHT palette", 1)) {
|
184
|
+
return false;
|
185
|
+
}
|
186
|
+
|
187
|
+
const numColors = sock.rQpeek8() + 1;
|
188
|
+
const paletteSize = numColors * 3;
|
189
|
+
|
190
|
+
if (sock.rQwait("TIGHT palette", 1 + paletteSize)) {
|
191
|
+
return false;
|
192
|
+
}
|
193
|
+
|
194
|
+
this._numColors = numColors;
|
195
|
+
sock.rQskipBytes(1);
|
196
|
+
|
197
|
+
sock.rQshiftTo(this._palette, paletteSize);
|
198
|
+
}
|
199
|
+
|
200
|
+
const bpp = (this._numColors <= 2) ? 1 : 8;
|
201
|
+
const rowSize = Math.floor((width * bpp + 7) / 8);
|
202
|
+
const uncompressedSize = rowSize * height;
|
203
|
+
|
204
|
+
let data;
|
205
|
+
|
206
|
+
if (uncompressedSize === 0) {
|
207
|
+
return true;
|
208
|
+
}
|
209
|
+
|
210
|
+
if (uncompressedSize < 12) {
|
211
|
+
if (sock.rQwait("TIGHT", uncompressedSize)) {
|
212
|
+
return false;
|
213
|
+
}
|
214
|
+
|
215
|
+
data = sock.rQshiftBytes(uncompressedSize);
|
216
|
+
} else {
|
217
|
+
data = this._readData(sock);
|
218
|
+
if (data === null) {
|
219
|
+
return false;
|
220
|
+
}
|
221
|
+
|
222
|
+
this._zlibs[streamId].setInput(data);
|
223
|
+
data = this._zlibs[streamId].inflate(uncompressedSize);
|
224
|
+
this._zlibs[streamId].setInput(null);
|
225
|
+
}
|
226
|
+
|
227
|
+
// Convert indexed (palette based) image data to RGB
|
228
|
+
if (this._numColors == 2) {
|
229
|
+
this._monoRect(x, y, width, height, data, this._palette, display);
|
230
|
+
} else {
|
231
|
+
this._paletteRect(x, y, width, height, data, this._palette, display);
|
232
|
+
}
|
233
|
+
|
234
|
+
this._numColors = 0;
|
235
|
+
|
236
|
+
return true;
|
237
|
+
}
|
238
|
+
|
239
|
+
_monoRect(x, y, width, height, data, palette, display) {
|
240
|
+
// Convert indexed (palette based) image data to RGB
|
241
|
+
// TODO: reduce number of calculations inside loop
|
242
|
+
const dest = this._getScratchBuffer(width * height * 4);
|
243
|
+
const w = Math.floor((width + 7) / 8);
|
244
|
+
const w1 = Math.floor(width / 8);
|
245
|
+
|
246
|
+
for (let y = 0; y < height; y++) {
|
247
|
+
let dp, sp, x;
|
248
|
+
for (x = 0; x < w1; x++) {
|
249
|
+
for (let b = 7; b >= 0; b--) {
|
250
|
+
dp = (y * width + x * 8 + 7 - b) * 4;
|
251
|
+
sp = (data[y * w + x] >> b & 1) * 3;
|
252
|
+
dest[dp] = palette[sp];
|
253
|
+
dest[dp + 1] = palette[sp + 1];
|
254
|
+
dest[dp + 2] = palette[sp + 2];
|
255
|
+
dest[dp + 3] = 255;
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
for (let b = 7; b >= 8 - width % 8; b--) {
|
260
|
+
dp = (y * width + x * 8 + 7 - b) * 4;
|
261
|
+
sp = (data[y * w + x] >> b & 1) * 3;
|
262
|
+
dest[dp] = palette[sp];
|
263
|
+
dest[dp + 1] = palette[sp + 1];
|
264
|
+
dest[dp + 2] = palette[sp + 2];
|
265
|
+
dest[dp + 3] = 255;
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
display.blitImage(x, y, width, height, dest, 0, false);
|
270
|
+
}
|
271
|
+
|
272
|
+
_paletteRect(x, y, width, height, data, palette, display) {
|
273
|
+
// Convert indexed (palette based) image data to RGB
|
274
|
+
const dest = this._getScratchBuffer(width * height * 4);
|
275
|
+
const total = width * height * 4;
|
276
|
+
for (let i = 0, j = 0; i < total; i += 4, j++) {
|
277
|
+
const sp = data[j] * 3;
|
278
|
+
dest[i] = palette[sp];
|
279
|
+
dest[i + 1] = palette[sp + 1];
|
280
|
+
dest[i + 2] = palette[sp + 2];
|
281
|
+
dest[i + 3] = 255;
|
282
|
+
}
|
283
|
+
|
284
|
+
display.blitImage(x, y, width, height, dest, 0, false);
|
285
|
+
}
|
286
|
+
|
287
|
+
_gradientFilter(streamId, x, y, width, height, sock, display, depth) {
|
288
|
+
// assume the TPIXEL is 3 bytes long
|
289
|
+
const uncompressedSize = width * height * 3;
|
290
|
+
let data;
|
291
|
+
|
292
|
+
if (uncompressedSize === 0) {
|
293
|
+
return true;
|
294
|
+
}
|
295
|
+
|
296
|
+
if (uncompressedSize < 12) {
|
297
|
+
if (sock.rQwait("TIGHT", uncompressedSize)) {
|
298
|
+
return false;
|
299
|
+
}
|
300
|
+
|
301
|
+
data = sock.rQshiftBytes(uncompressedSize);
|
302
|
+
} else {
|
303
|
+
data = this._readData(sock);
|
304
|
+
if (data === null) {
|
305
|
+
return false;
|
306
|
+
}
|
307
|
+
|
308
|
+
this._zlibs[streamId].setInput(data);
|
309
|
+
data = this._zlibs[streamId].inflate(uncompressedSize);
|
310
|
+
this._zlibs[streamId].setInput(null);
|
311
|
+
}
|
312
|
+
|
313
|
+
let rgbx = new Uint8Array(4 * width * height);
|
314
|
+
|
315
|
+
let rgbxIndex = 0, dataIndex = 0;
|
316
|
+
let left = new Uint8Array(3);
|
317
|
+
for (let x = 0; x < width; x++) {
|
318
|
+
for (let c = 0; c < 3; c++) {
|
319
|
+
const prediction = left[c];
|
320
|
+
const value = data[dataIndex++] + prediction;
|
321
|
+
rgbx[rgbxIndex++] = value;
|
322
|
+
left[c] = value;
|
323
|
+
}
|
324
|
+
rgbx[rgbxIndex++] = 255;
|
325
|
+
}
|
326
|
+
|
327
|
+
let upperIndex = 0;
|
328
|
+
let upper = new Uint8Array(3),
|
329
|
+
upperleft = new Uint8Array(3);
|
330
|
+
for (let y = 1; y < height; y++) {
|
331
|
+
left.fill(0);
|
332
|
+
upperleft.fill(0);
|
333
|
+
for (let x = 0; x < width; x++) {
|
334
|
+
for (let c = 0; c < 3; c++) {
|
335
|
+
upper[c] = rgbx[upperIndex++];
|
336
|
+
let prediction = left[c] + upper[c] - upperleft[c];
|
337
|
+
if (prediction < 0) {
|
338
|
+
prediction = 0;
|
339
|
+
} else if (prediction > 255) {
|
340
|
+
prediction = 255;
|
341
|
+
}
|
342
|
+
const value = data[dataIndex++] + prediction;
|
343
|
+
rgbx[rgbxIndex++] = value;
|
344
|
+
upperleft[c] = upper[c];
|
345
|
+
left[c] = value;
|
346
|
+
}
|
347
|
+
rgbx[rgbxIndex++] = 255;
|
348
|
+
upperIndex++;
|
349
|
+
}
|
350
|
+
}
|
351
|
+
|
352
|
+
display.blitImage(x, y, width, height, rgbx, 0, false);
|
353
|
+
|
354
|
+
return true;
|
355
|
+
}
|
356
|
+
|
357
|
+
_readData(sock) {
|
358
|
+
if (this._len === 0) {
|
359
|
+
if (sock.rQwait("TIGHT", 3)) {
|
360
|
+
return null;
|
361
|
+
}
|
362
|
+
|
363
|
+
let byte;
|
364
|
+
|
365
|
+
byte = sock.rQshift8();
|
366
|
+
this._len = byte & 0x7f;
|
367
|
+
if (byte & 0x80) {
|
368
|
+
byte = sock.rQshift8();
|
369
|
+
this._len |= (byte & 0x7f) << 7;
|
370
|
+
if (byte & 0x80) {
|
371
|
+
byte = sock.rQshift8();
|
372
|
+
this._len |= byte << 14;
|
373
|
+
}
|
374
|
+
}
|
375
|
+
}
|
376
|
+
|
377
|
+
if (sock.rQwait("TIGHT", this._len)) {
|
378
|
+
return null;
|
379
|
+
}
|
380
|
+
|
381
|
+
let data = sock.rQshiftBytes(this._len, false);
|
382
|
+
this._len = 0;
|
383
|
+
|
384
|
+
return data;
|
385
|
+
}
|
386
|
+
|
387
|
+
_getScratchBuffer(size) {
|
388
|
+
if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {
|
389
|
+
this._scratchBuffer = new Uint8Array(size);
|
390
|
+
}
|
391
|
+
return this._scratchBuffer;
|
392
|
+
}
|
393
|
+
}
|
@@ -0,0 +1,27 @@
|
|
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 TightDecoder from './tight.js';
|
11
|
+
|
12
|
+
export default class TightPNGDecoder extends TightDecoder {
|
13
|
+
_pngRect(x, y, width, height, sock, display, depth) {
|
14
|
+
let data = this._readData(sock);
|
15
|
+
if (data === null) {
|
16
|
+
return false;
|
17
|
+
}
|
18
|
+
|
19
|
+
display.imageRect(x, y, width, height, "image/png", data);
|
20
|
+
|
21
|
+
return true;
|
22
|
+
}
|
23
|
+
|
24
|
+
_basicRect(ctl, x, y, width, height, sock, display, depth) {
|
25
|
+
throw new Error("BasicCompression received in TightPNG rect");
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,51 @@
|
|
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 Inflator from "../inflator.js";
|
11
|
+
|
12
|
+
export default class ZlibDecoder {
|
13
|
+
constructor() {
|
14
|
+
this._zlib = new Inflator();
|
15
|
+
this._length = 0;
|
16
|
+
}
|
17
|
+
|
18
|
+
decodeRect(x, y, width, height, sock, display, depth) {
|
19
|
+
if ((width === 0) || (height === 0)) {
|
20
|
+
return true;
|
21
|
+
}
|
22
|
+
|
23
|
+
if (this._length === 0) {
|
24
|
+
if (sock.rQwait("ZLIB", 4)) {
|
25
|
+
return false;
|
26
|
+
}
|
27
|
+
|
28
|
+
this._length = sock.rQshift32();
|
29
|
+
}
|
30
|
+
|
31
|
+
if (sock.rQwait("ZLIB", this._length)) {
|
32
|
+
return false;
|
33
|
+
}
|
34
|
+
|
35
|
+
let data = new Uint8Array(sock.rQshiftBytes(this._length, false));
|
36
|
+
this._length = 0;
|
37
|
+
|
38
|
+
this._zlib.setInput(data);
|
39
|
+
data = this._zlib.inflate(width * height * 4);
|
40
|
+
this._zlib.setInput(null);
|
41
|
+
|
42
|
+
// Max sure the image is fully opaque
|
43
|
+
for (let i = 0; i < width * height; i++) {
|
44
|
+
data[i * 4 + 3] = 255;
|
45
|
+
}
|
46
|
+
|
47
|
+
display.blitImage(x, y, width, height, data, 0);
|
48
|
+
|
49
|
+
return true;
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,185 @@
|
|
1
|
+
/*
|
2
|
+
* noVNC: HTML5 VNC client
|
3
|
+
* Copyright (C) 2021 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 Inflate from "../inflator.js";
|
11
|
+
|
12
|
+
const ZRLE_TILE_WIDTH = 64;
|
13
|
+
const ZRLE_TILE_HEIGHT = 64;
|
14
|
+
|
15
|
+
export default class ZRLEDecoder {
|
16
|
+
constructor() {
|
17
|
+
this._length = 0;
|
18
|
+
this._inflator = new Inflate();
|
19
|
+
|
20
|
+
this._pixelBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
|
21
|
+
this._tileBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
|
22
|
+
}
|
23
|
+
|
24
|
+
decodeRect(x, y, width, height, sock, display, depth) {
|
25
|
+
if (this._length === 0) {
|
26
|
+
if (sock.rQwait("ZLib data length", 4)) {
|
27
|
+
return false;
|
28
|
+
}
|
29
|
+
this._length = sock.rQshift32();
|
30
|
+
}
|
31
|
+
if (sock.rQwait("Zlib data", this._length)) {
|
32
|
+
return false;
|
33
|
+
}
|
34
|
+
|
35
|
+
const data = sock.rQshiftBytes(this._length, false);
|
36
|
+
|
37
|
+
this._inflator.setInput(data);
|
38
|
+
|
39
|
+
for (let ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) {
|
40
|
+
let th = Math.min(ZRLE_TILE_HEIGHT, y + height - ty);
|
41
|
+
|
42
|
+
for (let tx = x; tx < x + width; tx += ZRLE_TILE_WIDTH) {
|
43
|
+
let tw = Math.min(ZRLE_TILE_WIDTH, x + width - tx);
|
44
|
+
|
45
|
+
const tileSize = tw * th;
|
46
|
+
const subencoding = this._inflator.inflate(1)[0];
|
47
|
+
if (subencoding === 0) {
|
48
|
+
// raw data
|
49
|
+
const data = this._readPixels(tileSize);
|
50
|
+
display.blitImage(tx, ty, tw, th, data, 0, false);
|
51
|
+
} else if (subencoding === 1) {
|
52
|
+
// solid
|
53
|
+
const background = this._readPixels(1);
|
54
|
+
display.fillRect(tx, ty, tw, th, [background[0], background[1], background[2]]);
|
55
|
+
} else if (subencoding >= 2 && subencoding <= 16) {
|
56
|
+
const data = this._decodePaletteTile(subencoding, tileSize, tw, th);
|
57
|
+
display.blitImage(tx, ty, tw, th, data, 0, false);
|
58
|
+
} else if (subencoding === 128) {
|
59
|
+
const data = this._decodeRLETile(tileSize);
|
60
|
+
display.blitImage(tx, ty, tw, th, data, 0, false);
|
61
|
+
} else if (subencoding >= 130 && subencoding <= 255) {
|
62
|
+
const data = this._decodeRLEPaletteTile(subencoding - 128, tileSize);
|
63
|
+
display.blitImage(tx, ty, tw, th, data, 0, false);
|
64
|
+
} else {
|
65
|
+
throw new Error('Unknown subencoding: ' + subencoding);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
this._length = 0;
|
70
|
+
return true;
|
71
|
+
}
|
72
|
+
|
73
|
+
_getBitsPerPixelInPalette(paletteSize) {
|
74
|
+
if (paletteSize <= 2) {
|
75
|
+
return 1;
|
76
|
+
} else if (paletteSize <= 4) {
|
77
|
+
return 2;
|
78
|
+
} else if (paletteSize <= 16) {
|
79
|
+
return 4;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
_readPixels(pixels) {
|
84
|
+
let data = this._pixelBuffer;
|
85
|
+
const buffer = this._inflator.inflate(3*pixels);
|
86
|
+
for (let i = 0, j = 0; i < pixels*4; i += 4, j += 3) {
|
87
|
+
data[i] = buffer[j];
|
88
|
+
data[i + 1] = buffer[j + 1];
|
89
|
+
data[i + 2] = buffer[j + 2];
|
90
|
+
data[i + 3] = 255; // Add the Alpha
|
91
|
+
}
|
92
|
+
return data;
|
93
|
+
}
|
94
|
+
|
95
|
+
_decodePaletteTile(paletteSize, tileSize, tilew, tileh) {
|
96
|
+
const data = this._tileBuffer;
|
97
|
+
const palette = this._readPixels(paletteSize);
|
98
|
+
const bitsPerPixel = this._getBitsPerPixelInPalette(paletteSize);
|
99
|
+
const mask = (1 << bitsPerPixel) - 1;
|
100
|
+
|
101
|
+
let offset = 0;
|
102
|
+
let encoded = this._inflator.inflate(1)[0];
|
103
|
+
|
104
|
+
for (let y=0; y<tileh; y++) {
|
105
|
+
let shift = 8-bitsPerPixel;
|
106
|
+
for (let x=0; x<tilew; x++) {
|
107
|
+
if (shift<0) {
|
108
|
+
shift=8-bitsPerPixel;
|
109
|
+
encoded = this._inflator.inflate(1)[0];
|
110
|
+
}
|
111
|
+
let indexInPalette = (encoded>>shift) & mask;
|
112
|
+
|
113
|
+
data[offset] = palette[indexInPalette * 4];
|
114
|
+
data[offset + 1] = palette[indexInPalette * 4 + 1];
|
115
|
+
data[offset + 2] = palette[indexInPalette * 4 + 2];
|
116
|
+
data[offset + 3] = palette[indexInPalette * 4 + 3];
|
117
|
+
offset += 4;
|
118
|
+
shift-=bitsPerPixel;
|
119
|
+
}
|
120
|
+
if (shift<8-bitsPerPixel && y<tileh-1) {
|
121
|
+
encoded = this._inflator.inflate(1)[0];
|
122
|
+
}
|
123
|
+
}
|
124
|
+
return data;
|
125
|
+
}
|
126
|
+
|
127
|
+
_decodeRLETile(tileSize) {
|
128
|
+
const data = this._tileBuffer;
|
129
|
+
let i = 0;
|
130
|
+
while (i < tileSize) {
|
131
|
+
const pixel = this._readPixels(1);
|
132
|
+
const length = this._readRLELength();
|
133
|
+
for (let j = 0; j < length; j++) {
|
134
|
+
data[i * 4] = pixel[0];
|
135
|
+
data[i * 4 + 1] = pixel[1];
|
136
|
+
data[i * 4 + 2] = pixel[2];
|
137
|
+
data[i * 4 + 3] = pixel[3];
|
138
|
+
i++;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
return data;
|
142
|
+
}
|
143
|
+
|
144
|
+
_decodeRLEPaletteTile(paletteSize, tileSize) {
|
145
|
+
const data = this._tileBuffer;
|
146
|
+
|
147
|
+
// palette
|
148
|
+
const palette = this._readPixels(paletteSize);
|
149
|
+
|
150
|
+
let offset = 0;
|
151
|
+
while (offset < tileSize) {
|
152
|
+
let indexInPalette = this._inflator.inflate(1)[0];
|
153
|
+
let length = 1;
|
154
|
+
if (indexInPalette >= 128) {
|
155
|
+
indexInPalette -= 128;
|
156
|
+
length = this._readRLELength();
|
157
|
+
}
|
158
|
+
if (indexInPalette > paletteSize) {
|
159
|
+
throw new Error('Too big index in palette: ' + indexInPalette + ', palette size: ' + paletteSize);
|
160
|
+
}
|
161
|
+
if (offset + length > tileSize) {
|
162
|
+
throw new Error('Too big rle length in palette mode: ' + length + ', allowed length is: ' + (tileSize - offset));
|
163
|
+
}
|
164
|
+
|
165
|
+
for (let j = 0; j < length; j++) {
|
166
|
+
data[offset * 4] = palette[indexInPalette * 4];
|
167
|
+
data[offset * 4 + 1] = palette[indexInPalette * 4 + 1];
|
168
|
+
data[offset * 4 + 2] = palette[indexInPalette * 4 + 2];
|
169
|
+
data[offset * 4 + 3] = palette[indexInPalette * 4 + 3];
|
170
|
+
offset++;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
return data;
|
174
|
+
}
|
175
|
+
|
176
|
+
_readRLELength() {
|
177
|
+
let length = 0;
|
178
|
+
let current = 0;
|
179
|
+
do {
|
180
|
+
current = this._inflator.inflate(1)[0];
|
181
|
+
length += current;
|
182
|
+
} while (current === 255);
|
183
|
+
return length + 1;
|
184
|
+
}
|
185
|
+
}
|