Handle pseudo encodings directly
These have very special behaviour compared to normal data encodings, so separate out them and handle them separately.
This commit is contained in:
parent
e17cae8f32
commit
11309f3243
294
core/rfb.js
294
core/rfb.js
|
@ -2,6 +2,7 @@
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
||||||
|
* Copyright (C) 2018 Pierre Ossman for Cendio AB
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -162,12 +163,6 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._encHandlers[encodings.encodingTight] = RFB.encodingHandlers.TIGHT.bind(this, false);
|
this._encHandlers[encodings.encodingTight] = RFB.encodingHandlers.TIGHT.bind(this, false);
|
||||||
this._encHandlers[encodings.encodingTightPNG] = RFB.encodingHandlers.TIGHT.bind(this, true);
|
this._encHandlers[encodings.encodingTightPNG] = RFB.encodingHandlers.TIGHT.bind(this, true);
|
||||||
|
|
||||||
this._encHandlers[encodings.pseudoEncodingDesktopSize] = RFB.encodingHandlers.DesktopSize.bind(this);
|
|
||||||
this._encHandlers[encodings.pseudoEncodingLastRect] = RFB.encodingHandlers.last_rect.bind(this);
|
|
||||||
this._encHandlers[encodings.pseudoEncodingCursor] = RFB.encodingHandlers.Cursor.bind(this);
|
|
||||||
this._encHandlers[encodings.pseudoEncodingQEMUExtendedKeyEvent] = RFB.encodingHandlers.QEMUExtendedKeyEvent.bind(this);
|
|
||||||
this._encHandlers[encodings.pseudoEncodingExtendedDesktopSize] = RFB.encodingHandlers.ExtendedDesktopSize.bind(this);
|
|
||||||
|
|
||||||
// NB: nothing that needs explicit teardown should be done
|
// NB: nothing that needs explicit teardown should be done
|
||||||
// before this point, since this can throw an exception
|
// before this point, since this can throw an exception
|
||||||
try {
|
try {
|
||||||
|
@ -1477,17 +1472,13 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._FBU.height = (hdr[6] << 8) + hdr[7];
|
this._FBU.height = (hdr[6] << 8) + hdr[7];
|
||||||
this._FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
|
this._FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
|
||||||
(hdr[10] << 8) + hdr[11], 10);
|
(hdr[10] << 8) + hdr[11], 10);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._encHandlers[this._FBU.encoding]) {
|
if (!this._handleRect()) {
|
||||||
this._fail("Unsupported encoding (encoding: " +
|
|
||||||
this._FBU.encoding + ")");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const ret = this._encHandlers[this._FBU.encoding]();
|
this._FBU.rects--;
|
||||||
|
|
||||||
if (!ret) { return ret; } // need more data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._display.flip();
|
this._display.flip();
|
||||||
|
@ -1495,6 +1486,151 @@ export default class RFB extends EventTargetMixin {
|
||||||
return true; // We finished this FBU
|
return true; // We finished this FBU
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleRect() {
|
||||||
|
switch (this._FBU.encoding) {
|
||||||
|
case encodings.pseudoEncodingLastRect:
|
||||||
|
this._FBU.rects = 1; // Will be decreased when we return
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case encodings.pseudoEncodingCursor:
|
||||||
|
return this._handleCursor();
|
||||||
|
|
||||||
|
case encodings.pseudoEncodingQEMUExtendedKeyEvent:
|
||||||
|
// Old Safari doesn't support creating keyboard events
|
||||||
|
try {
|
||||||
|
const keyboardEvent = document.createEvent("keyboardEvent");
|
||||||
|
if (keyboardEvent.code !== undefined) {
|
||||||
|
this._qemuExtKeyEventSupported = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case encodings.pseudoEncodingDesktopSize:
|
||||||
|
this._resize(this._FBU.width, this._FBU.height);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case encodings.pseudoEncodingExtendedDesktopSize:
|
||||||
|
return this._handleExtendedDesktopSize();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return this._handleDataRect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleCursor() {
|
||||||
|
const x = this._FBU.x; // hotspot-x
|
||||||
|
const y = this._FBU.y; // hotspot-y
|
||||||
|
const w = this._FBU.width;
|
||||||
|
const h = this._FBU.height;
|
||||||
|
|
||||||
|
const pixelslength = w * h * 4;
|
||||||
|
const masklength = Math.floor((w + 7) / 8) * h;
|
||||||
|
|
||||||
|
this._FBU.bytes = pixelslength + masklength;
|
||||||
|
if (this._sock.rQwait("cursor encoding", this._FBU.bytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cursor.change(this._sock.rQshiftBytes(pixelslength),
|
||||||
|
this._sock.rQshiftBytes(masklength),
|
||||||
|
x, y, w, h);
|
||||||
|
|
||||||
|
this._FBU.bytes = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleExtendedDesktopSize() {
|
||||||
|
this._FBU.bytes = 4;
|
||||||
|
if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const number_of_screens = this._sock.rQpeek8();
|
||||||
|
|
||||||
|
this._FBU.bytes += number_of_screens * 16;
|
||||||
|
if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstUpdate = !this._supportsSetDesktopSize;
|
||||||
|
this._supportsSetDesktopSize = true;
|
||||||
|
|
||||||
|
// Normally we only apply the current resize mode after a
|
||||||
|
// window resize event. However there is no such trigger on the
|
||||||
|
// initial connect. And we don't know if the server supports
|
||||||
|
// resizing until we've gotten here.
|
||||||
|
if (firstUpdate) {
|
||||||
|
this._requestRemoteResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sock.rQskipBytes(1); // number-of-screens
|
||||||
|
this._sock.rQskipBytes(3); // padding
|
||||||
|
|
||||||
|
for (let i = 0; i < number_of_screens; i += 1) {
|
||||||
|
// Save the id and flags of the first screen
|
||||||
|
if (i === 0) {
|
||||||
|
this._screen_id = this._sock.rQshiftBytes(4); // id
|
||||||
|
this._sock.rQskipBytes(2); // x-position
|
||||||
|
this._sock.rQskipBytes(2); // y-position
|
||||||
|
this._sock.rQskipBytes(2); // width
|
||||||
|
this._sock.rQskipBytes(2); // height
|
||||||
|
this._screen_flags = this._sock.rQshiftBytes(4); // flags
|
||||||
|
} else {
|
||||||
|
this._sock.rQskipBytes(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The x-position indicates the reason for the change:
|
||||||
|
*
|
||||||
|
* 0 - server resized on its own
|
||||||
|
* 1 - this client requested the resize
|
||||||
|
* 2 - another client requested the resize
|
||||||
|
*/
|
||||||
|
|
||||||
|
// We need to handle errors when we requested the resize.
|
||||||
|
if (this._FBU.x === 1 && this._FBU.y !== 0) {
|
||||||
|
let msg = "";
|
||||||
|
// The y-position indicates the status code from the server
|
||||||
|
switch (this._FBU.y) {
|
||||||
|
case 1:
|
||||||
|
msg = "Resize is administratively prohibited";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
msg = "Out of resources";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
msg = "Invalid screen layout";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
msg = "Unknown reason";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Log.Warn("Server did not accept the resize request: "
|
||||||
|
+ msg);
|
||||||
|
} else {
|
||||||
|
this._resize(this._FBU.width, this._FBU.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._FBU.bytes = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleDataRect() {
|
||||||
|
let handler = this._encHandlers[this._FBU.encoding];
|
||||||
|
if (!handler) {
|
||||||
|
this._fail("Unsupported encoding (encoding: " +
|
||||||
|
this._FBU.encoding + ")");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler();
|
||||||
|
}
|
||||||
|
|
||||||
_updateContinuousUpdates() {
|
_updateContinuousUpdates() {
|
||||||
if (!this._enabledContinuousUpdates) { return; }
|
if (!this._enabledContinuousUpdates) { return; }
|
||||||
|
|
||||||
|
@ -1883,7 +2019,6 @@ RFB.encodingHandlers = {
|
||||||
if (this._FBU.lines > 0) {
|
if (this._FBU.lines > 0) {
|
||||||
this._FBU.bytes = this._FBU.width * pixelSize; // At least another line
|
this._FBU.bytes = this._FBU.width * pixelSize; // At least another line
|
||||||
} else {
|
} else {
|
||||||
this._FBU.rects--;
|
|
||||||
this._FBU.bytes = 0;
|
this._FBU.bytes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1897,7 +2032,6 @@ RFB.encodingHandlers = {
|
||||||
this._FBU.x, this._FBU.y, this._FBU.width,
|
this._FBU.x, this._FBU.y, this._FBU.width,
|
||||||
this._FBU.height);
|
this._FBU.height);
|
||||||
|
|
||||||
this._FBU.rects--;
|
|
||||||
this._FBU.bytes = 0;
|
this._FBU.bytes = 0;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -1926,7 +2060,6 @@ RFB.encodingHandlers = {
|
||||||
const chunk = Math.min(this._rre_chunk_sz, this._FBU.subrects);
|
const chunk = Math.min(this._rre_chunk_sz, this._FBU.subrects);
|
||||||
this._FBU.bytes = (4 + 8) * chunk;
|
this._FBU.bytes = (4 + 8) * chunk;
|
||||||
} else {
|
} else {
|
||||||
this._FBU.rects--;
|
|
||||||
this._FBU.bytes = 0;
|
this._FBU.bytes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2044,10 +2177,6 @@ RFB.encodingHandlers = {
|
||||||
this._FBU.tiles--;
|
this._FBU.tiles--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._FBU.tiles === 0) {
|
|
||||||
this._FBU.rects--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2350,132 +2479,7 @@ RFB.encodingHandlers = {
|
||||||
|
|
||||||
|
|
||||||
this._FBU.bytes = 0;
|
this._FBU.bytes = 0;
|
||||||
this._FBU.rects--;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
last_rect() {
|
|
||||||
this._FBU.rects = 0;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
ExtendedDesktopSize() {
|
|
||||||
this._FBU.bytes = 1;
|
|
||||||
if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; }
|
|
||||||
|
|
||||||
const firstUpdate = !this._supportsSetDesktopSize;
|
|
||||||
this._supportsSetDesktopSize = true;
|
|
||||||
|
|
||||||
// Normally we only apply the current resize mode after a
|
|
||||||
// window resize event. However there is no such trigger on the
|
|
||||||
// initial connect. And we don't know if the server supports
|
|
||||||
// resizing until we've gotten here.
|
|
||||||
if (firstUpdate) {
|
|
||||||
this._requestRemoteResize();
|
|
||||||
}
|
|
||||||
|
|
||||||
const number_of_screens = this._sock.rQpeek8();
|
|
||||||
|
|
||||||
this._FBU.bytes = 4 + (number_of_screens * 16);
|
|
||||||
if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; }
|
|
||||||
|
|
||||||
this._sock.rQskipBytes(1); // number-of-screens
|
|
||||||
this._sock.rQskipBytes(3); // padding
|
|
||||||
|
|
||||||
for (let i = 0; i < number_of_screens; i += 1) {
|
|
||||||
// Save the id and flags of the first screen
|
|
||||||
if (i === 0) {
|
|
||||||
this._screen_id = this._sock.rQshiftBytes(4); // id
|
|
||||||
this._sock.rQskipBytes(2); // x-position
|
|
||||||
this._sock.rQskipBytes(2); // y-position
|
|
||||||
this._sock.rQskipBytes(2); // width
|
|
||||||
this._sock.rQskipBytes(2); // height
|
|
||||||
this._screen_flags = this._sock.rQshiftBytes(4); // flags
|
|
||||||
} else {
|
|
||||||
this._sock.rQskipBytes(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The x-position indicates the reason for the change:
|
|
||||||
*
|
|
||||||
* 0 - server resized on its own
|
|
||||||
* 1 - this client requested the resize
|
|
||||||
* 2 - another client requested the resize
|
|
||||||
*/
|
|
||||||
|
|
||||||
// We need to handle errors when we requested the resize.
|
|
||||||
if (this._FBU.x === 1 && this._FBU.y !== 0) {
|
|
||||||
let msg = "";
|
|
||||||
// The y-position indicates the status code from the server
|
|
||||||
switch (this._FBU.y) {
|
|
||||||
case 1:
|
|
||||||
msg = "Resize is administratively prohibited";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
msg = "Out of resources";
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
msg = "Invalid screen layout";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
msg = "Unknown reason";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Log.Warn("Server did not accept the resize request: "
|
|
||||||
+ msg);
|
|
||||||
} else {
|
|
||||||
this._resize(this._FBU.width, this._FBU.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._FBU.bytes = 0;
|
|
||||||
this._FBU.rects -= 1;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
DesktopSize() {
|
|
||||||
this._resize(this._FBU.width, this._FBU.height);
|
|
||||||
this._FBU.bytes = 0;
|
|
||||||
this._FBU.rects -= 1;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
Cursor() {
|
|
||||||
Log.Debug(">> set_cursor");
|
|
||||||
const x = this._FBU.x; // hotspot-x
|
|
||||||
const y = this._FBU.y; // hotspot-y
|
|
||||||
const w = this._FBU.width;
|
|
||||||
const h = this._FBU.height;
|
|
||||||
|
|
||||||
const pixelslength = w * h * 4;
|
|
||||||
const masklength = Math.floor((w + 7) / 8) * h;
|
|
||||||
|
|
||||||
this._FBU.bytes = pixelslength + masklength;
|
|
||||||
if (this._sock.rQwait("cursor encoding", this._FBU.bytes)) { return false; }
|
|
||||||
|
|
||||||
this._cursor.change(this._sock.rQshiftBytes(pixelslength),
|
|
||||||
this._sock.rQshiftBytes(masklength),
|
|
||||||
x, y, w, h);
|
|
||||||
|
|
||||||
this._FBU.bytes = 0;
|
|
||||||
this._FBU.rects--;
|
|
||||||
|
|
||||||
Log.Debug("<< set_cursor");
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
QEMUExtendedKeyEvent() {
|
|
||||||
this._FBU.rects--;
|
|
||||||
|
|
||||||
// Old Safari doesn't support creating keyboard events
|
|
||||||
try {
|
|
||||||
const keyboardEvent = document.createEvent("keyboardEvent");
|
|
||||||
if (keyboardEvent.code !== undefined) {
|
|
||||||
this._qemuExtKeyEventSupported = true;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue