import { decodeUTF8 } from './util/strings.js';
import { set_defaults, make_properties } from './util/properties.js';
import Display from "./display.js";
-import { Keyboard, Mouse } from "./input/devices.js";
+import Keyboard from "./input/keyboard.js";
+import Mouse from "./input/mouse.js";
import Websock from "./websock.js";
import Base64 from "./base64.js";
import DES from "./des.js";
['JPEG_quality_med', -26 ],
//['JPEG_quality_hi', -23 ],
//['compress_lo', -255 ],
- ['compress_hi', -247 ],
+ ['compress_hi', -254 ],
+ //['compress_max', -247 ],
['DesktopSize', -223 ],
['last_rect', -224 ],
zlib: [] // TIGHT zlib streams
};
- this._fb_Bpp = 4;
- this._fb_depth = 3;
this._fb_width = 0;
this._fb_height = 0;
this._fb_name = "";
'target': 'null', // VNC display rendering Canvas object
'focusContainer': document, // DOM element that captures keyboard input
'encrypt': false, // Use TLS/SSL/wss encryption
- 'true_color': true, // Request true color pixel data
'local_cursor': false, // Request locally rendered cursor
'shared': true, // Request shared mode
'view_only': false, // Disable client mouse/keyboard
}
this._keyboard = new Keyboard({target: this._focusContainer,
- onKeyPress: this._handleKeyPress.bind(this)});
+ onKeyEvent: this._handleKeyEvent.bind(this)});
this._mouse = new Mouse({target: this._target,
onMouseButton: this._handleMouseButton.bind(this),
- onMouseMove: this._handleMouseMove.bind(this),
- notify: this._keyboard.sync.bind(this._keyboard)});
+ onMouseMove: this._handleMouseMove.bind(this)});
this._sock = new Websock();
this._sock.on('message', this._handle_message.bind(this));
this._rfb_password = (password !== undefined) ? password : "";
this._rfb_path = (path !== undefined) ? path : "";
- if (!this._rfb_host || !this._rfb_port) {
+ if (!this._rfb_host) {
return this._fail(
- _("Must set host and port"));
+ _("Must set host"));
}
this._rfb_init_state = '';
return true;
}
- if (this._qemuExtKeyEventSupported) {
- var scancode = XtScancode[code];
+ var scancode = XtScancode[code];
- if (scancode === undefined) {
- Log.Error('Unable to find a xt scancode for code: ' + code);
- // FIXME: not in the spec, but this is what
- // gtk-vnc does
- scancode = 0;
- }
+ if (this._qemuExtKeyEventSupported && scancode) {
+ // 0 is NoSymbol
+ keysym = keysym || 0;
Log.Info("Sending key (" + (down ? "down" : "up") + "): keysym " + keysym + ", scancode " + scancode);
RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
} else {
+ if (!keysym) {
+ return false;
+ }
Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
}
uri = this._encrypt ? 'wss' : 'ws';
}
- uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path;
+ uri += '://' + this._rfb_host;
+ if(this._rfb_port) {
+ uri += ':' + this._rfb_port;
+ }
+ uri += '/' + this._rfb_path;
+
Log.Info("connecting to " + uri);
try {
}
},
- _handleKeyPress: function (keyevent) {
- var down = (keyevent.type == 'keydown');
- this.sendKey(keyevent.keysym, keyevent.code, down);
+ _handleKeyEvent: function (keysym, code, down) {
+ this.sendKey(keysym, code, down);
},
_handleMouseButton: function (x, y, down, bmask) {
if (down) {
this._mouse_buttonMask |= bmask;
} else {
- this._mouse_buttonMask ^= bmask;
+ this._mouse_buttonMask &= ~bmask;
}
if (this._viewportDrag) {
} else {
return this._fail("Authentication failure");
}
- return false;
case 2:
return this._fail("Too many authentication attempts");
default:
// we're past the point where we could backtrack, so it's safe to call this
this._onDesktopName(this, this._fb_name);
- if (this._true_color && this._fb_name === "Intel(r) AMT KVM") {
- Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Disabling true color");
- this._true_color = false;
- }
-
- this._display.set_true_color(this._true_color);
this._display.resize(this._fb_width, this._fb_height);
this._onFBResize(this, this._fb_width, this._fb_height);
if (!this._view_only) { this._keyboard.grab(); }
if (!this._view_only) { this._mouse.grab(); }
- if (this._true_color) {
- this._fb_Bpp = 4;
- this._fb_depth = 3;
- } else {
- this._fb_Bpp = 1;
- this._fb_depth = 1;
- }
-
- RFB.messages.pixelFormat(this._sock, this._fb_Bpp, this._fb_depth, this._true_color);
- RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor, this._true_color);
+ RFB.messages.pixelFormat(this._sock, 4, 3, true);
+ RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor);
RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fb_width, this._fb_height);
this._timing.fbu_rt_start = (new Date()).getTime();
_handle_set_colour_map_msg: function () {
Log.Debug("SetColorMapEntries");
- this._sock.rQskip8(); // Padding
-
- var first_colour = this._sock.rQshift16();
- var num_colours = this._sock.rQshift16();
- if (this._sock.rQwait('SetColorMapEntries', num_colours * 6, 6)) { return false; }
- for (var c = 0; c < num_colours; c++) {
- var red = parseInt(this._sock.rQshift16() / 256, 10);
- var green = parseInt(this._sock.rQshift16() / 256, 10);
- var blue = parseInt(this._sock.rQshift16() / 256, 10);
- this._display.set_colourMap([blue, green, red], first_colour + c);
- }
- Log.Debug("colourMap: " + this._display.get_colourMap());
- Log.Info("Registered " + num_colours + " colourMap entries");
-
- return true;
+ return this._fail("Protocol error", "Unexpected SetColorMapEntries message");
},
_handle_server_cut_text: function () {
['target', 'wo', 'dom'], // VNC display rendering Canvas object
['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input
['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption
- ['true_color', 'rw', 'bool'], // Request true color pixel data
['local_cursor', 'rw', 'bool'], // Request locally rendered cursor
['shared', 'rw', 'bool'], // Request shared mode
['view_only', 'rw', 'bool'], // Disable client mouse/keyboard
// Need to send an updated list of encodings if we are connected
if (this._rfb_connection_state === "connected") {
- RFB.messages.clientEncodings(this._sock, this._encodings, cursor,
- this._true_color);
+ RFB.messages.clientEncodings(this._sock, this._encodings, cursor);
}
};
sock.flush();
},
- clientEncodings: function (sock, encodings, local_cursor, true_color) {
+ clientEncodings: function (sock, encodings, local_cursor) {
var buff = sock._sQ;
var offset = sock._sQlen;
for (i = 0; i < encodings.length; i++) {
if (encodings[i][0] === "Cursor" && !local_cursor) {
Log.Debug("Skipping Cursor pseudo-encoding");
- } else if (encodings[i][0] === "TIGHT" && !true_color) {
- // TODO: remove this when we have tight+non-true-color
- Log.Warn("Skipping tight as it is only supported with true color");
} else {
var enc = encodings[i][1];
buff[j] = enc >> 24;
this._FBU.lines = this._FBU.height;
}
- this._FBU.bytes = this._FBU.width * this._fb_Bpp; // at least a line
+ this._FBU.bytes = this._FBU.width * 4; // at least a line
if (this._sock.rQwait("RAW", this._FBU.bytes)) { return false; }
var cur_y = this._FBU.y + (this._FBU.height - this._FBU.lines);
var curr_height = Math.min(this._FBU.lines,
- Math.floor(this._sock.rQlen() / (this._FBU.width * this._fb_Bpp)));
+ Math.floor(this._sock.rQlen() / (this._FBU.width * 4)));
this._display.blitImage(this._FBU.x, cur_y, this._FBU.width,
curr_height, this._sock.get_rQ(),
this._sock.get_rQi());
- this._sock.rQskipBytes(this._FBU.width * curr_height * this._fb_Bpp);
+ this._sock.rQskipBytes(this._FBU.width * curr_height * 4);
this._FBU.lines -= curr_height;
if (this._FBU.lines > 0) {
- this._FBU.bytes = this._FBU.width * this._fb_Bpp; // At least another line
+ this._FBU.bytes = this._FBU.width * 4; // At least another line
} else {
this._FBU.rects--;
this._FBU.bytes = 0;
RRE: function () {
var color;
if (this._FBU.subrects === 0) {
- this._FBU.bytes = 4 + this._fb_Bpp;
- if (this._sock.rQwait("RRE", 4 + this._fb_Bpp)) { return false; }
+ this._FBU.bytes = 4 + 4;
+ if (this._sock.rQwait("RRE", 4 + 4)) { return false; }
this._FBU.subrects = this._sock.rQshift32();
- color = this._sock.rQshiftBytes(this._fb_Bpp); // Background
+ color = this._sock.rQshiftBytes(4); // Background
this._display.fillRect(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, color);
}
- while (this._FBU.subrects > 0 && this._sock.rQlen() >= (this._fb_Bpp + 8)) {
- color = this._sock.rQshiftBytes(this._fb_Bpp);
+ while (this._FBU.subrects > 0 && this._sock.rQlen() >= (4 + 8)) {
+ color = this._sock.rQshiftBytes(4);
var x = this._sock.rQshift16();
var y = this._sock.rQshift16();
var width = this._sock.rQshift16();
if (this._FBU.subrects > 0) {
var chunk = Math.min(this._rre_chunk_sz, this._FBU.subrects);
- this._FBU.bytes = (this._fb_Bpp + 8) * chunk;
+ this._FBU.bytes = (4 + 8) * chunk;
} else {
this._FBU.rects--;
this._FBU.bytes = 0;
// Figure out how much we are expecting
if (subencoding & 0x01) { // Raw
- this._FBU.bytes += w * h * this._fb_Bpp;
+ this._FBU.bytes += w * h * 4;
} else {
if (subencoding & 0x02) { // Background
- this._FBU.bytes += this._fb_Bpp;
+ this._FBU.bytes += 4;
}
if (subencoding & 0x04) { // Foreground
- this._FBU.bytes += this._fb_Bpp;
+ this._FBU.bytes += 4;
}
if (subencoding & 0x08) { // AnySubrects
this._FBU.bytes++; // Since we aren't shifting it off
if (this._sock.rQwait("hextile subrects header", this._FBU.bytes)) { return false; }
subrects = rQ[rQi + this._FBU.bytes - 1]; // Peek
if (subencoding & 0x10) { // SubrectsColoured
- this._FBU.bytes += subrects * (this._fb_Bpp + 2);
+ this._FBU.bytes += subrects * (4 + 2);
} else {
this._FBU.bytes += subrects * 2;
}
rQi += this._FBU.bytes - 1;
} else {
if (this._FBU.subencoding & 0x02) { // Background
- if (this._fb_Bpp == 1) {
- this._FBU.background = rQ[rQi];
- } else {
- // fb_Bpp is 4
- this._FBU.background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
- }
- rQi += this._fb_Bpp;
+ this._FBU.background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+ rQi += 4;
}
if (this._FBU.subencoding & 0x04) { // Foreground
- if (this._fb_Bpp == 1) {
- this._FBU.foreground = rQ[rQi];
- } else {
- // this._fb_Bpp is 4
- this._FBU.foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
- }
- rQi += this._fb_Bpp;
+ this._FBU.foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+ rQi += 4;
}
this._display.startTile(x, y, w, h, this._FBU.background);
for (var s = 0; s < subrects; s++) {
var color;
if (this._FBU.subencoding & 0x10) { // SubrectsColoured
- if (this._fb_Bpp === 1) {
- color = rQ[rQi];
- } else {
- // _fb_Bpp is 4
- color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
- }
- rQi += this._fb_Bpp;
+ color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
+ rQi += 4;
} else {
color = this._FBU.foreground;
}
},
display_tight: function (isTightPNG) {
- if (this._fb_depth === 1) {
- this._fail("Internal error",
- "Tight protocol handler only implements " +
- "true color mode");
- }
-
this._FBU.bytes = 1; // compression-control byte
if (this._sock.rQwait("TIGHT compression-control", this._FBU.bytes)) { return false; }
var handlePalette = function () {
var numColors = rQ[rQi + 2] + 1;
- var paletteSize = numColors * this._fb_depth;
+ var paletteSize = numColors * 3;
this._FBU.bytes += paletteSize;
if (this._sock.rQwait("TIGHT palette " + cmode, this._FBU.bytes)) { return false; }
var handleCopy = function () {
var raw = false;
- var uncompressedSize = this._FBU.width * this._FBU.height * this._fb_depth;
+ var uncompressedSize = this._FBU.width * this._FBU.height * 3;
if (uncompressedSize < 12) {
raw = true;
cl_header = 0;
}
switch (cmode) {
- // fill use fb_depth because TPIXELs drop the padding byte
+ // fill use depth because TPIXELs drop the padding byte
case "fill": // TPIXEL
- this._FBU.bytes += this._fb_depth;
+ this._FBU.bytes += 3;
break;
case "jpeg": // max clength
this._FBU.bytes += 3;
var w = this._FBU.width;
var h = this._FBU.height;
- var pixelslength = w * h * this._fb_Bpp;
+ var pixelslength = w * h * 4;
var masklength = Math.floor((w + 7) / 8) * h;
this._FBU.bytes = pixelslength + masklength;
QEMUExtendedKeyEvent: function () {
this._FBU.rects--;
- var keyboardEvent = document.createEvent("keyboardEvent");
- if (keyboardEvent.code !== undefined) {
- this._qemuExtKeyEventSupported = true;
- this._keyboard.setQEMUVNCKeyboardHandler();
+ // Old Safari doesn't support creating keyboard events
+ try {
+ var keyboardEvent = document.createEvent("keyboardEvent");
+ if (keyboardEvent.code !== undefined) {
+ this._qemuExtKeyEventSupported = true;
+ }
+ } catch (err) {
}
},