@novnc/novnc 1.0.0 → 1.2.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/AUTHORS +13 -0
- package/LICENSE.txt +2 -1
- package/README.md +69 -3
- package/core/base64.js +35 -41
- package/core/decoders/copyrect.js +22 -0
- package/core/decoders/hextile.js +137 -0
- package/core/decoders/raw.js +56 -0
- package/core/decoders/rre.js +44 -0
- package/core/decoders/tight.js +315 -0
- package/core/decoders/tightpng.js +27 -0
- package/core/deflator.js +85 -0
- package/core/des.js +90 -95
- package/core/display.js +254 -297
- package/core/encodings.js +7 -3
- package/core/inflator.js +48 -20
- package/core/input/domkeytable.js +21 -24
- package/core/input/fixedkeys.js +3 -1
- package/core/input/gesturehandler.js +567 -0
- package/core/input/keyboard.js +194 -120
- package/core/input/keysym.js +2 -0
- package/core/input/keysymdef.js +3 -3
- package/core/input/util.js +53 -12
- package/core/input/vkeys.js +2 -1
- package/core/rfb.js +1937 -1496
- package/core/util/browser.js +80 -29
- package/core/util/cursor.js +253 -0
- package/core/util/element.js +32 -0
- package/core/util/events.js +59 -55
- package/core/util/eventtarget.js +25 -30
- package/core/util/int.js +15 -0
- package/core/util/logging.js +21 -16
- package/core/util/polyfill.js +15 -8
- package/core/util/strings.js +21 -8
- package/core/websock.js +145 -167
- package/docs/API.md +31 -10
- package/lib/base64.js +115 -0
- package/lib/decoders/copyrect.js +44 -0
- package/lib/decoders/hextile.js +173 -0
- package/lib/decoders/raw.js +78 -0
- package/lib/decoders/rre.js +65 -0
- package/lib/decoders/tight.js +350 -0
- package/lib/decoders/tightpng.js +67 -0
- package/lib/deflator.js +99 -0
- package/lib/des.js +314 -0
- package/lib/display.js +733 -0
- package/lib/encodings.js +64 -0
- package/lib/inflator.js +87 -0
- package/lib/input/domkeytable.js +282 -0
- package/lib/input/fixedkeys.js +123 -0
- package/lib/input/gesturehandler.js +642 -0
- package/lib/input/keyboard.js +429 -0
- package/lib/input/keysym.js +1135 -0
- package/lib/input/keysymdef.js +1354 -0
- package/lib/input/util.js +304 -0
- package/lib/input/vkeys.js +127 -0
- package/lib/input/xtscancodes.js +505 -0
- package/lib/rfb.js +3448 -0
- package/lib/util/browser.js +131 -0
- package/lib/util/cursor.js +314 -0
- package/lib/util/element.js +43 -0
- package/lib/util/events.js +142 -0
- package/lib/util/eventtarget.js +64 -0
- package/lib/util/int.js +22 -0
- package/lib/util/logging.js +79 -0
- package/lib/util/polyfill.js +72 -0
- package/lib/util/strings.js +38 -0
- package/lib/vendor/pako/lib/utils/common.js +67 -0
- package/lib/vendor/pako/lib/zlib/adler32.js +33 -0
- package/lib/vendor/pako/lib/zlib/constants.js +51 -0
- package/lib/vendor/pako/lib/zlib/crc32.js +42 -0
- package/lib/vendor/pako/lib/zlib/deflate.js +2159 -0
- package/lib/vendor/pako/lib/zlib/gzheader.js +53 -0
- package/lib/vendor/pako/lib/zlib/inffast.js +445 -0
- package/lib/vendor/pako/lib/zlib/inflate.js +2114 -0
- package/lib/vendor/pako/lib/zlib/inftrees.js +418 -0
- package/lib/vendor/pako/lib/zlib/messages.js +36 -0
- package/lib/vendor/pako/lib/zlib/trees.js +1499 -0
- package/lib/vendor/pako/lib/zlib/zstream.js +46 -0
- package/lib/vendor/promise.js +255 -0
- package/lib/websock.js +374 -0
- package/package.json +48 -28
- package/vendor/pako/lib/zlib/deflate.js +30 -30
- package/vendor/pako/lib/zlib/inflate.js +17 -17
- package/core/input/mouse.js +0 -280
- package/docs/API-internal.md +0 -125
- package/docs/EMBEDDING.md +0 -83
- package/docs/VERSION +0 -1
package/core/display.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* noVNC: HTML5 VNC client
|
|
3
|
-
* Copyright (C)
|
|
4
|
-
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
|
|
3
|
+
* Copyright (C) 2019 The noVNC Authors
|
|
5
4
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
|
6
5
|
*
|
|
7
6
|
* See README.md for usage and integration instructions.
|
|
@@ -9,111 +8,104 @@
|
|
|
9
8
|
|
|
10
9
|
import * as Log from './util/logging.js';
|
|
11
10
|
import Base64 from "./base64.js";
|
|
11
|
+
import { supportsImageMetadata } from './util/browser.js';
|
|
12
|
+
import { toSigned32bit } from './util/int.js';
|
|
12
13
|
|
|
13
|
-
export default
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export default class Display {
|
|
15
|
+
constructor(target) {
|
|
16
|
+
this._drawCtx = null;
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
this._renderQ = []; // queue drawing actions for in-oder rendering
|
|
19
|
+
this._flushing = false;
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// the full frame buffer (logical canvas) size
|
|
22
|
+
this._fbWidth = 0;
|
|
23
|
+
this._fbHeight = 0;
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
this._prevDrawStyle = "";
|
|
26
|
+
this._tile = null;
|
|
27
|
+
this._tile16x16 = null;
|
|
28
|
+
this._tileX = 0;
|
|
29
|
+
this._tileY = 0;
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
Log.Debug(">> Display.constructor");
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
// The visible canvas
|
|
34
|
+
this._target = target;
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
if (!this._target) {
|
|
37
|
+
throw new Error("Target must be set");
|
|
38
|
+
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
if (typeof this._target === 'string') {
|
|
41
|
+
throw new Error('target must be a DOM element');
|
|
42
|
+
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if (!this._target.getContext) {
|
|
45
|
+
throw new Error("no getContext method");
|
|
46
|
+
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
this._targetCtx = this._target.getContext('2d');
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
// the visible canvas viewport (i.e. what actually gets seen)
|
|
51
|
+
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
// The hidden canvas, where we do the actual rendering
|
|
54
|
+
this._backbuffer = document.createElement('canvas');
|
|
55
|
+
this._drawCtx = this._backbuffer.getContext('2d');
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
this._damageBounds = { left: 0, top: 0,
|
|
58
|
+
right: this._backbuffer.width,
|
|
59
|
+
bottom: this._backbuffer.height };
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
Log.Debug("User Agent: " + navigator.userAgent);
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
// Check canvas features
|
|
64
|
+
if (!('createImageData' in this._drawCtx)) {
|
|
65
|
+
throw new Error("Canvas does not support createImageData");
|
|
66
|
+
}
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
throw new Error("Canvas does not support createImageData");
|
|
67
|
-
}
|
|
68
|
+
this._tile16x16 = this._drawCtx.createImageData(16, 16);
|
|
69
|
+
Log.Debug("<< Display.constructor");
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
Log.Debug("<< Display.constructor");
|
|
71
|
-
};
|
|
71
|
+
// ===== PROPERTIES =====
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
73
|
+
this._scale = 1.0;
|
|
74
|
+
this._clipViewport = false;
|
|
75
|
+
|
|
76
|
+
// ===== EVENT HANDLERS =====
|
|
77
|
+
|
|
78
|
+
this.onflush = () => {}; // A flush request has finished
|
|
79
|
+
}
|
|
80
80
|
|
|
81
|
-
Display.prototype = {
|
|
82
81
|
// ===== PROPERTIES =====
|
|
83
82
|
|
|
84
|
-
_scale
|
|
85
|
-
get scale() { return this._scale; },
|
|
83
|
+
get scale() { return this._scale; }
|
|
86
84
|
set scale(scale) {
|
|
87
85
|
this._rescale(scale);
|
|
88
|
-
}
|
|
86
|
+
}
|
|
89
87
|
|
|
90
|
-
_clipViewport
|
|
91
|
-
get clipViewport() { return this._clipViewport; },
|
|
88
|
+
get clipViewport() { return this._clipViewport; }
|
|
92
89
|
set clipViewport(viewport) {
|
|
93
90
|
this._clipViewport = viewport;
|
|
94
91
|
// May need to readjust the viewport dimensions
|
|
95
|
-
|
|
92
|
+
const vp = this._viewportLoc;
|
|
96
93
|
this.viewportChangeSize(vp.w, vp.h);
|
|
97
94
|
this.viewportChangePos(0, 0);
|
|
98
|
-
}
|
|
95
|
+
}
|
|
99
96
|
|
|
100
97
|
get width() {
|
|
101
|
-
return this.
|
|
102
|
-
}
|
|
103
|
-
get height() {
|
|
104
|
-
return this._fb_height;
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
logo: null,
|
|
108
|
-
|
|
109
|
-
// ===== EVENT HANDLERS =====
|
|
98
|
+
return this._fbWidth;
|
|
99
|
+
}
|
|
110
100
|
|
|
111
|
-
|
|
101
|
+
get height() {
|
|
102
|
+
return this._fbHeight;
|
|
103
|
+
}
|
|
112
104
|
|
|
113
105
|
// ===== PUBLIC METHODS =====
|
|
114
106
|
|
|
115
|
-
viewportChangePos
|
|
116
|
-
|
|
107
|
+
viewportChangePos(deltaX, deltaY) {
|
|
108
|
+
const vp = this._viewportLoc;
|
|
117
109
|
deltaX = Math.floor(deltaX);
|
|
118
110
|
deltaY = Math.floor(deltaY);
|
|
119
111
|
|
|
@@ -122,23 +114,23 @@ Display.prototype = {
|
|
|
122
114
|
deltaY = -vp.h;
|
|
123
115
|
}
|
|
124
116
|
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
const vx2 = vp.x + vp.w - 1;
|
|
118
|
+
const vy2 = vp.y + vp.h - 1;
|
|
127
119
|
|
|
128
120
|
// Position change
|
|
129
121
|
|
|
130
122
|
if (deltaX < 0 && vp.x + deltaX < 0) {
|
|
131
123
|
deltaX = -vp.x;
|
|
132
124
|
}
|
|
133
|
-
if (vx2 + deltaX >= this.
|
|
134
|
-
deltaX -= vx2 + deltaX - this.
|
|
125
|
+
if (vx2 + deltaX >= this._fbWidth) {
|
|
126
|
+
deltaX -= vx2 + deltaX - this._fbWidth + 1;
|
|
135
127
|
}
|
|
136
128
|
|
|
137
129
|
if (vp.y + deltaY < 0) {
|
|
138
130
|
deltaY = -vp.y;
|
|
139
131
|
}
|
|
140
|
-
if (vy2 + deltaY >= this.
|
|
141
|
-
deltaY -= (vy2 + deltaY - this.
|
|
132
|
+
if (vy2 + deltaY >= this._fbHeight) {
|
|
133
|
+
deltaY -= (vy2 + deltaY - this._fbHeight + 1);
|
|
142
134
|
}
|
|
143
135
|
|
|
144
136
|
if (deltaX === 0 && deltaY === 0) {
|
|
@@ -152,32 +144,35 @@ Display.prototype = {
|
|
|
152
144
|
this._damage(vp.x, vp.y, vp.w, vp.h);
|
|
153
145
|
|
|
154
146
|
this.flip();
|
|
155
|
-
}
|
|
147
|
+
}
|
|
156
148
|
|
|
157
|
-
viewportChangeSize
|
|
149
|
+
viewportChangeSize(width, height) {
|
|
158
150
|
|
|
159
151
|
if (!this._clipViewport ||
|
|
160
152
|
typeof(width) === "undefined" ||
|
|
161
153
|
typeof(height) === "undefined") {
|
|
162
154
|
|
|
163
155
|
Log.Debug("Setting viewport to full display region");
|
|
164
|
-
width = this.
|
|
165
|
-
height = this.
|
|
156
|
+
width = this._fbWidth;
|
|
157
|
+
height = this._fbHeight;
|
|
166
158
|
}
|
|
167
159
|
|
|
168
|
-
|
|
169
|
-
|
|
160
|
+
width = Math.floor(width);
|
|
161
|
+
height = Math.floor(height);
|
|
162
|
+
|
|
163
|
+
if (width > this._fbWidth) {
|
|
164
|
+
width = this._fbWidth;
|
|
170
165
|
}
|
|
171
|
-
if (height > this.
|
|
172
|
-
height = this.
|
|
166
|
+
if (height > this._fbHeight) {
|
|
167
|
+
height = this._fbHeight;
|
|
173
168
|
}
|
|
174
169
|
|
|
175
|
-
|
|
170
|
+
const vp = this._viewportLoc;
|
|
176
171
|
if (vp.w !== width || vp.h !== height) {
|
|
177
172
|
vp.w = width;
|
|
178
173
|
vp.h = height;
|
|
179
174
|
|
|
180
|
-
|
|
175
|
+
const canvas = this._target;
|
|
181
176
|
canvas.width = width;
|
|
182
177
|
canvas.height = height;
|
|
183
178
|
|
|
@@ -190,27 +185,33 @@ Display.prototype = {
|
|
|
190
185
|
// Update the visible size of the target canvas
|
|
191
186
|
this._rescale(this._scale);
|
|
192
187
|
}
|
|
193
|
-
}
|
|
188
|
+
}
|
|
194
189
|
|
|
195
|
-
absX
|
|
196
|
-
|
|
197
|
-
|
|
190
|
+
absX(x) {
|
|
191
|
+
if (this._scale === 0) {
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
return toSigned32bit(x / this._scale + this._viewportLoc.x);
|
|
195
|
+
}
|
|
198
196
|
|
|
199
|
-
absY
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
absY(y) {
|
|
198
|
+
if (this._scale === 0) {
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
return toSigned32bit(y / this._scale + this._viewportLoc.y);
|
|
202
|
+
}
|
|
202
203
|
|
|
203
|
-
resize
|
|
204
|
+
resize(width, height) {
|
|
204
205
|
this._prevDrawStyle = "";
|
|
205
206
|
|
|
206
|
-
this.
|
|
207
|
-
this.
|
|
207
|
+
this._fbWidth = width;
|
|
208
|
+
this._fbHeight = height;
|
|
208
209
|
|
|
209
|
-
|
|
210
|
+
const canvas = this._backbuffer;
|
|
210
211
|
if (canvas.width !== width || canvas.height !== height) {
|
|
211
212
|
|
|
212
213
|
// We have to save the canvas data since changing the size will clear it
|
|
213
|
-
|
|
214
|
+
let saveImg = null;
|
|
214
215
|
if (canvas.width > 0 && canvas.height > 0) {
|
|
215
216
|
saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
|
|
216
217
|
}
|
|
@@ -229,13 +230,13 @@ Display.prototype = {
|
|
|
229
230
|
|
|
230
231
|
// Readjust the viewport as it may be incorrectly sized
|
|
231
232
|
// and positioned
|
|
232
|
-
|
|
233
|
+
const vp = this._viewportLoc;
|
|
233
234
|
this.viewportChangeSize(vp.w, vp.h);
|
|
234
235
|
this.viewportChangePos(0, 0);
|
|
235
|
-
}
|
|
236
|
+
}
|
|
236
237
|
|
|
237
238
|
// Track what parts of the visible canvas that need updating
|
|
238
|
-
_damage
|
|
239
|
+
_damage(x, y, w, h) {
|
|
239
240
|
if (x < this._damageBounds.left) {
|
|
240
241
|
this._damageBounds.left = x;
|
|
241
242
|
}
|
|
@@ -248,25 +249,23 @@ Display.prototype = {
|
|
|
248
249
|
if ((y + h) > this._damageBounds.bottom) {
|
|
249
250
|
this._damageBounds.bottom = y + h;
|
|
250
251
|
}
|
|
251
|
-
}
|
|
252
|
+
}
|
|
252
253
|
|
|
253
254
|
// Update the visible canvas with the contents of the
|
|
254
255
|
// rendering canvas
|
|
255
|
-
flip
|
|
256
|
-
if (this._renderQ.length !== 0 && !
|
|
257
|
-
this.
|
|
256
|
+
flip(fromQueue) {
|
|
257
|
+
if (this._renderQ.length !== 0 && !fromQueue) {
|
|
258
|
+
this._renderQPush({
|
|
258
259
|
'type': 'flip'
|
|
259
260
|
});
|
|
260
261
|
} else {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
w = this._damageBounds.right - x;
|
|
266
|
-
h = this._damageBounds.bottom - y;
|
|
262
|
+
let x = this._damageBounds.left;
|
|
263
|
+
let y = this._damageBounds.top;
|
|
264
|
+
let w = this._damageBounds.right - x;
|
|
265
|
+
let h = this._damageBounds.bottom - y;
|
|
267
266
|
|
|
268
|
-
vx = x - this._viewportLoc.x;
|
|
269
|
-
vy = y - this._viewportLoc.y;
|
|
267
|
+
let vx = x - this._viewportLoc.x;
|
|
268
|
+
let vy = y - this._viewportLoc.y;
|
|
270
269
|
|
|
271
270
|
if (vx < 0) {
|
|
272
271
|
w += vx;
|
|
@@ -298,34 +297,23 @@ Display.prototype = {
|
|
|
298
297
|
this._damageBounds.left = this._damageBounds.top = 65535;
|
|
299
298
|
this._damageBounds.right = this._damageBounds.bottom = 0;
|
|
300
299
|
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
clear: function () {
|
|
304
|
-
if (this._logo) {
|
|
305
|
-
this.resize(this._logo.width, this._logo.height);
|
|
306
|
-
this.imageRect(0, 0, this._logo.type, this._logo.data);
|
|
307
|
-
} else {
|
|
308
|
-
this.resize(240, 20);
|
|
309
|
-
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
|
|
310
|
-
}
|
|
311
|
-
this.flip();
|
|
312
|
-
},
|
|
300
|
+
}
|
|
313
301
|
|
|
314
|
-
pending
|
|
302
|
+
pending() {
|
|
315
303
|
return this._renderQ.length > 0;
|
|
316
|
-
}
|
|
304
|
+
}
|
|
317
305
|
|
|
318
|
-
flush
|
|
306
|
+
flush() {
|
|
319
307
|
if (this._renderQ.length === 0) {
|
|
320
308
|
this.onflush();
|
|
321
309
|
} else {
|
|
322
310
|
this._flushing = true;
|
|
323
311
|
}
|
|
324
|
-
}
|
|
312
|
+
}
|
|
325
313
|
|
|
326
|
-
fillRect
|
|
327
|
-
if (this._renderQ.length !== 0 && !
|
|
328
|
-
this.
|
|
314
|
+
fillRect(x, y, width, height, color, fromQueue) {
|
|
315
|
+
if (this._renderQ.length !== 0 && !fromQueue) {
|
|
316
|
+
this._renderQPush({
|
|
329
317
|
'type': 'fill',
|
|
330
318
|
'x': x,
|
|
331
319
|
'y': y,
|
|
@@ -338,16 +326,16 @@ Display.prototype = {
|
|
|
338
326
|
this._drawCtx.fillRect(x, y, width, height);
|
|
339
327
|
this._damage(x, y, width, height);
|
|
340
328
|
}
|
|
341
|
-
}
|
|
329
|
+
}
|
|
342
330
|
|
|
343
|
-
copyImage
|
|
344
|
-
if (this._renderQ.length !== 0 && !
|
|
345
|
-
this.
|
|
331
|
+
copyImage(oldX, oldY, newX, newY, w, h, fromQueue) {
|
|
332
|
+
if (this._renderQ.length !== 0 && !fromQueue) {
|
|
333
|
+
this._renderQPush({
|
|
346
334
|
'type': 'copy',
|
|
347
|
-
'
|
|
348
|
-
'
|
|
349
|
-
'x':
|
|
350
|
-
'y':
|
|
335
|
+
'oldX': oldX,
|
|
336
|
+
'oldY': oldY,
|
|
337
|
+
'x': newX,
|
|
338
|
+
'y': newY,
|
|
351
339
|
'width': w,
|
|
352
340
|
'height': h,
|
|
353
341
|
});
|
|
@@ -365,84 +353,92 @@ Display.prototype = {
|
|
|
365
353
|
this._drawCtx.imageSmoothingEnabled = false;
|
|
366
354
|
|
|
367
355
|
this._drawCtx.drawImage(this._backbuffer,
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
this._damage(
|
|
356
|
+
oldX, oldY, w, h,
|
|
357
|
+
newX, newY, w, h);
|
|
358
|
+
this._damage(newX, newY, w, h);
|
|
371
359
|
}
|
|
372
|
-
}
|
|
360
|
+
}
|
|
373
361
|
|
|
374
|
-
imageRect
|
|
375
|
-
|
|
362
|
+
imageRect(x, y, width, height, mime, arr) {
|
|
363
|
+
/* The internal logic cannot handle empty images, so bail early */
|
|
364
|
+
if ((width === 0) || (height === 0)) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const img = new Image();
|
|
376
369
|
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
|
377
|
-
|
|
370
|
+
|
|
371
|
+
this._renderQPush({
|
|
378
372
|
'type': 'img',
|
|
379
373
|
'img': img,
|
|
380
374
|
'x': x,
|
|
381
|
-
'y': y
|
|
375
|
+
'y': y,
|
|
376
|
+
'width': width,
|
|
377
|
+
'height': height
|
|
382
378
|
});
|
|
383
|
-
}
|
|
379
|
+
}
|
|
384
380
|
|
|
385
381
|
// start updating a tile
|
|
386
|
-
startTile
|
|
387
|
-
this.
|
|
388
|
-
this.
|
|
382
|
+
startTile(x, y, width, height, color) {
|
|
383
|
+
this._tileX = x;
|
|
384
|
+
this._tileY = y;
|
|
389
385
|
if (width === 16 && height === 16) {
|
|
390
386
|
this._tile = this._tile16x16;
|
|
391
387
|
} else {
|
|
392
388
|
this._tile = this._drawCtx.createImageData(width, height);
|
|
393
389
|
}
|
|
394
390
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
391
|
+
const red = color[2];
|
|
392
|
+
const green = color[1];
|
|
393
|
+
const blue = color[0];
|
|
398
394
|
|
|
399
|
-
|
|
400
|
-
for (
|
|
395
|
+
const data = this._tile.data;
|
|
396
|
+
for (let i = 0; i < width * height * 4; i += 4) {
|
|
401
397
|
data[i] = red;
|
|
402
398
|
data[i + 1] = green;
|
|
403
399
|
data[i + 2] = blue;
|
|
404
400
|
data[i + 3] = 255;
|
|
405
401
|
}
|
|
406
|
-
}
|
|
402
|
+
}
|
|
407
403
|
|
|
408
404
|
// update sub-rectangle of the current tile
|
|
409
|
-
subTile
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
for (
|
|
419
|
-
for (
|
|
420
|
-
|
|
405
|
+
subTile(x, y, w, h, color) {
|
|
406
|
+
const red = color[2];
|
|
407
|
+
const green = color[1];
|
|
408
|
+
const blue = color[0];
|
|
409
|
+
const xend = x + w;
|
|
410
|
+
const yend = y + h;
|
|
411
|
+
|
|
412
|
+
const data = this._tile.data;
|
|
413
|
+
const width = this._tile.width;
|
|
414
|
+
for (let j = y; j < yend; j++) {
|
|
415
|
+
for (let i = x; i < xend; i++) {
|
|
416
|
+
const p = (i + (j * width)) * 4;
|
|
421
417
|
data[p] = red;
|
|
422
418
|
data[p + 1] = green;
|
|
423
419
|
data[p + 2] = blue;
|
|
424
420
|
data[p + 3] = 255;
|
|
425
421
|
}
|
|
426
422
|
}
|
|
427
|
-
}
|
|
423
|
+
}
|
|
428
424
|
|
|
429
425
|
// draw the current tile to the screen
|
|
430
|
-
finishTile
|
|
431
|
-
this._drawCtx.putImageData(this._tile, this.
|
|
432
|
-
this._damage(this.
|
|
426
|
+
finishTile() {
|
|
427
|
+
this._drawCtx.putImageData(this._tile, this._tileX, this._tileY);
|
|
428
|
+
this._damage(this._tileX, this._tileY,
|
|
433
429
|
this._tile.width, this._tile.height);
|
|
434
|
-
}
|
|
430
|
+
}
|
|
435
431
|
|
|
436
|
-
blitImage
|
|
437
|
-
if (this._renderQ.length !== 0 && !
|
|
432
|
+
blitImage(x, y, width, height, arr, offset, fromQueue) {
|
|
433
|
+
if (this._renderQ.length !== 0 && !fromQueue) {
|
|
438
434
|
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
|
439
435
|
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
|
440
436
|
// this probably isn't getting called *nearly* as much
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
this.
|
|
437
|
+
const newArr = new Uint8Array(width * height * 4);
|
|
438
|
+
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
|
439
|
+
this._renderQPush({
|
|
444
440
|
'type': 'blit',
|
|
445
|
-
'data':
|
|
441
|
+
'data': newArr,
|
|
446
442
|
'x': x,
|
|
447
443
|
'y': y,
|
|
448
444
|
'width': width,
|
|
@@ -451,18 +447,18 @@ Display.prototype = {
|
|
|
451
447
|
} else {
|
|
452
448
|
this._bgrxImageData(x, y, width, height, arr, offset);
|
|
453
449
|
}
|
|
454
|
-
}
|
|
450
|
+
}
|
|
455
451
|
|
|
456
|
-
blitRgbImage
|
|
457
|
-
if (this._renderQ.length !== 0 && !
|
|
452
|
+
blitRgbImage(x, y, width, height, arr, offset, fromQueue) {
|
|
453
|
+
if (this._renderQ.length !== 0 && !fromQueue) {
|
|
458
454
|
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
|
459
455
|
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
|
460
456
|
// this probably isn't getting called *nearly* as much
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.
|
|
457
|
+
const newArr = new Uint8Array(width * height * 3);
|
|
458
|
+
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
|
459
|
+
this._renderQPush({
|
|
464
460
|
'type': 'blitRgb',
|
|
465
|
-
'data':
|
|
461
|
+
'data': newArr,
|
|
466
462
|
'x': x,
|
|
467
463
|
'y': y,
|
|
468
464
|
'width': width,
|
|
@@ -471,18 +467,18 @@ Display.prototype = {
|
|
|
471
467
|
} else {
|
|
472
468
|
this._rgbImageData(x, y, width, height, arr, offset);
|
|
473
469
|
}
|
|
474
|
-
}
|
|
470
|
+
}
|
|
475
471
|
|
|
476
|
-
blitRgbxImage
|
|
477
|
-
if (this._renderQ.length !== 0 && !
|
|
472
|
+
blitRgbxImage(x, y, width, height, arr, offset, fromQueue) {
|
|
473
|
+
if (this._renderQ.length !== 0 && !fromQueue) {
|
|
478
474
|
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
|
479
475
|
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
|
480
476
|
// this probably isn't getting called *nearly* as much
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
this.
|
|
477
|
+
const newArr = new Uint8Array(width * height * 4);
|
|
478
|
+
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
|
479
|
+
this._renderQPush({
|
|
484
480
|
'type': 'blitRgbx',
|
|
485
|
-
'data':
|
|
481
|
+
'data': newArr,
|
|
486
482
|
'x': x,
|
|
487
483
|
'y': y,
|
|
488
484
|
'width': width,
|
|
@@ -491,72 +487,67 @@ Display.prototype = {
|
|
|
491
487
|
} else {
|
|
492
488
|
this._rgbxImageData(x, y, width, height, arr, offset);
|
|
493
489
|
}
|
|
494
|
-
}
|
|
490
|
+
}
|
|
495
491
|
|
|
496
|
-
drawImage
|
|
492
|
+
drawImage(img, x, y) {
|
|
497
493
|
this._drawCtx.drawImage(img, x, y);
|
|
498
494
|
this._damage(x, y, img.width, img.height);
|
|
499
|
-
}
|
|
495
|
+
}
|
|
500
496
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
},
|
|
497
|
+
autoscale(containerWidth, containerHeight) {
|
|
498
|
+
let scaleRatio;
|
|
504
499
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
},
|
|
500
|
+
if (containerWidth === 0 || containerHeight === 0) {
|
|
501
|
+
scaleRatio = 0;
|
|
508
502
|
|
|
509
|
-
|
|
510
|
-
this._target.style.cursor = "none";
|
|
511
|
-
},
|
|
503
|
+
} else {
|
|
512
504
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
var fbAspectRatio = vp.w / vp.h;
|
|
505
|
+
const vp = this._viewportLoc;
|
|
506
|
+
const targetAspectRatio = containerWidth / containerHeight;
|
|
507
|
+
const fbAspectRatio = vp.w / vp.h;
|
|
517
508
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
509
|
+
if (fbAspectRatio >= targetAspectRatio) {
|
|
510
|
+
scaleRatio = containerWidth / vp.w;
|
|
511
|
+
} else {
|
|
512
|
+
scaleRatio = containerHeight / vp.h;
|
|
513
|
+
}
|
|
523
514
|
}
|
|
524
515
|
|
|
525
516
|
this._rescale(scaleRatio);
|
|
526
|
-
}
|
|
517
|
+
}
|
|
527
518
|
|
|
528
519
|
// ===== PRIVATE METHODS =====
|
|
529
520
|
|
|
530
|
-
_rescale
|
|
521
|
+
_rescale(factor) {
|
|
531
522
|
this._scale = factor;
|
|
532
|
-
|
|
523
|
+
const vp = this._viewportLoc;
|
|
533
524
|
|
|
534
525
|
// NB(directxman12): If you set the width directly, or set the
|
|
535
526
|
// style width to a number, the canvas is cleared.
|
|
536
527
|
// However, if you set the style width to a string
|
|
537
528
|
// ('NNNpx'), the canvas is scaled without clearing.
|
|
538
|
-
|
|
539
|
-
|
|
529
|
+
const width = factor * vp.w + 'px';
|
|
530
|
+
const height = factor * vp.h + 'px';
|
|
540
531
|
|
|
541
532
|
if ((this._target.style.width !== width) ||
|
|
542
533
|
(this._target.style.height !== height)) {
|
|
543
534
|
this._target.style.width = width;
|
|
544
535
|
this._target.style.height = height;
|
|
545
536
|
}
|
|
546
|
-
}
|
|
537
|
+
}
|
|
547
538
|
|
|
548
|
-
_setFillColor
|
|
549
|
-
|
|
539
|
+
_setFillColor(color) {
|
|
540
|
+
const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
|
|
550
541
|
if (newStyle !== this._prevDrawStyle) {
|
|
551
542
|
this._drawCtx.fillStyle = newStyle;
|
|
552
543
|
this._prevDrawStyle = newStyle;
|
|
553
544
|
}
|
|
554
|
-
}
|
|
545
|
+
}
|
|
555
546
|
|
|
556
|
-
_rgbImageData
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
for (
|
|
547
|
+
_rgbImageData(x, y, width, height, arr, offset) {
|
|
548
|
+
const img = this._drawCtx.createImageData(width, height);
|
|
549
|
+
const data = img.data;
|
|
550
|
+
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
|
|
560
551
|
data[i] = arr[j];
|
|
561
552
|
data[i + 1] = arr[j + 1];
|
|
562
553
|
data[i + 2] = arr[j + 2];
|
|
@@ -564,12 +555,12 @@ Display.prototype = {
|
|
|
564
555
|
}
|
|
565
556
|
this._drawCtx.putImageData(img, x, y);
|
|
566
557
|
this._damage(x, y, img.width, img.height);
|
|
567
|
-
}
|
|
558
|
+
}
|
|
568
559
|
|
|
569
|
-
_bgrxImageData
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
for (
|
|
560
|
+
_bgrxImageData(x, y, width, height, arr, offset) {
|
|
561
|
+
const img = this._drawCtx.createImageData(width, height);
|
|
562
|
+
const data = img.data;
|
|
563
|
+
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
|
|
573
564
|
data[i] = arr[j + 2];
|
|
574
565
|
data[i + 1] = arr[j + 1];
|
|
575
566
|
data[i + 2] = arr[j];
|
|
@@ -577,12 +568,12 @@ Display.prototype = {
|
|
|
577
568
|
}
|
|
578
569
|
this._drawCtx.putImageData(img, x, y);
|
|
579
570
|
this._damage(x, y, img.width, img.height);
|
|
580
|
-
}
|
|
571
|
+
}
|
|
581
572
|
|
|
582
|
-
_rgbxImageData
|
|
573
|
+
_rgbxImageData(x, y, width, height, arr, offset) {
|
|
583
574
|
// NB(directxman12): arr must be an Type Array view
|
|
584
|
-
|
|
585
|
-
if (
|
|
575
|
+
let img;
|
|
576
|
+
if (supportsImageMetadata) {
|
|
586
577
|
img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
|
|
587
578
|
} else {
|
|
588
579
|
img = this._drawCtx.createImageData(width, height);
|
|
@@ -590,34 +581,34 @@ Display.prototype = {
|
|
|
590
581
|
}
|
|
591
582
|
this._drawCtx.putImageData(img, x, y);
|
|
592
583
|
this._damage(x, y, img.width, img.height);
|
|
593
|
-
}
|
|
584
|
+
}
|
|
594
585
|
|
|
595
|
-
|
|
586
|
+
_renderQPush(action) {
|
|
596
587
|
this._renderQ.push(action);
|
|
597
588
|
if (this._renderQ.length === 1) {
|
|
598
589
|
// If this can be rendered immediately it will be, otherwise
|
|
599
590
|
// the scanner will wait for the relevant event
|
|
600
|
-
this.
|
|
591
|
+
this._scanRenderQ();
|
|
601
592
|
}
|
|
602
|
-
}
|
|
593
|
+
}
|
|
603
594
|
|
|
604
|
-
|
|
595
|
+
_resumeRenderQ() {
|
|
605
596
|
// "this" is the object that is ready, not the
|
|
606
597
|
// display object
|
|
607
|
-
this.removeEventListener('load', this.
|
|
608
|
-
this.
|
|
609
|
-
}
|
|
598
|
+
this.removeEventListener('load', this._noVNCDisplay._resumeRenderQ);
|
|
599
|
+
this._noVNCDisplay._scanRenderQ();
|
|
600
|
+
}
|
|
610
601
|
|
|
611
|
-
|
|
612
|
-
|
|
602
|
+
_scanRenderQ() {
|
|
603
|
+
let ready = true;
|
|
613
604
|
while (ready && this._renderQ.length > 0) {
|
|
614
|
-
|
|
605
|
+
const a = this._renderQ[0];
|
|
615
606
|
switch (a.type) {
|
|
616
607
|
case 'flip':
|
|
617
608
|
this.flip(true);
|
|
618
609
|
break;
|
|
619
610
|
case 'copy':
|
|
620
|
-
this.copyImage(a.
|
|
611
|
+
this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true);
|
|
621
612
|
break;
|
|
622
613
|
case 'fill':
|
|
623
614
|
this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
|
|
@@ -632,11 +623,18 @@ Display.prototype = {
|
|
|
632
623
|
this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
|
|
633
624
|
break;
|
|
634
625
|
case 'img':
|
|
635
|
-
|
|
626
|
+
/* IE tends to set "complete" prematurely, so check dimensions */
|
|
627
|
+
if (a.img.complete && (a.img.width !== 0) && (a.img.height !== 0)) {
|
|
628
|
+
if (a.img.width !== a.width || a.img.height !== a.height) {
|
|
629
|
+
Log.Error("Decoded image has incorrect dimensions. Got " +
|
|
630
|
+
a.img.width + "x" + a.img.height + ". Expected " +
|
|
631
|
+
a.width + "x" + a.height + ".");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
636
634
|
this.drawImage(a.img, a.x, a.y);
|
|
637
635
|
} else {
|
|
638
|
-
a.img.
|
|
639
|
-
a.img.addEventListener('load', this.
|
|
636
|
+
a.img._noVNCDisplay = this;
|
|
637
|
+
a.img.addEventListener('load', this._resumeRenderQ);
|
|
640
638
|
// We need to wait for this image to 'load'
|
|
641
639
|
// to keep things in-order
|
|
642
640
|
ready = false;
|
|
@@ -653,46 +651,5 @@ Display.prototype = {
|
|
|
653
651
|
this._flushing = false;
|
|
654
652
|
this.onflush();
|
|
655
653
|
}
|
|
656
|
-
},
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
// Class Methods
|
|
660
|
-
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
|
|
661
|
-
if ((w === 0) || (h === 0)) {
|
|
662
|
-
target.style.cursor = 'none';
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
var cur = []
|
|
667
|
-
var y, x;
|
|
668
|
-
for (y = 0; y < h; y++) {
|
|
669
|
-
for (x = 0; x < w; x++) {
|
|
670
|
-
var idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
|
|
671
|
-
var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
|
|
672
|
-
idx = ((w * y) + x) * 4;
|
|
673
|
-
cur.push(pixels[idx + 2]); // red
|
|
674
|
-
cur.push(pixels[idx + 1]); // green
|
|
675
|
-
cur.push(pixels[idx]); // blue
|
|
676
|
-
cur.push(alpha); // alpha
|
|
677
|
-
}
|
|
678
654
|
}
|
|
679
|
-
|
|
680
|
-
var canvas = document.createElement('canvas');
|
|
681
|
-
var ctx = canvas.getContext('2d');
|
|
682
|
-
|
|
683
|
-
canvas.width = w;
|
|
684
|
-
canvas.height = h;
|
|
685
|
-
|
|
686
|
-
var img;
|
|
687
|
-
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
|
|
688
|
-
img = new ImageData(new Uint8ClampedArray(cur), w, h);
|
|
689
|
-
} else {
|
|
690
|
-
img = ctx.createImageData(w, h);
|
|
691
|
-
img.data.set(new Uint8ClampedArray(cur));
|
|
692
|
-
}
|
|
693
|
-
ctx.clearRect(0, 0, w, h);
|
|
694
|
-
ctx.putImageData(img, 0, 0);
|
|
695
|
-
|
|
696
|
-
var url = canvas.toDataURL();
|
|
697
|
-
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
|
698
|
-
};
|
|
655
|
+
}
|