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:
Pierre Ossman 2018-07-20 16:00:43 +02:00
parent e17cae8f32
commit 11309f3243
1 changed files with 150 additions and 146 deletions

View File

@ -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]) {
this._fail("Unsupported encoding (encoding: " +
this._FBU.encoding + ")");
return false;
}
} }
const ret = this._encHandlers[this._FBU.encoding](); if (!this._handleRect()) {
return false;
}
if (!ret) { return ret; } // need more data this._FBU.rects--;
} }
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
}
}
} }