Separate out cursor handling

Make cursor handling more generic in preparation for generic handling
of corner cases.
This commit is contained in:
Pierre Ossman 2018-02-28 16:08:25 +01:00
parent e62b4ccb5e
commit b475eed5fa
4 changed files with 80 additions and 63 deletions

View File

@ -498,18 +498,6 @@ Display.prototype = {
this._damage(x, y, img.width, img.height); this._damage(x, y, img.width, img.height);
}, },
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
},
defaultCursor: function () {
this._target.style.cursor = "default";
},
disableLocalCursor: function () {
this._target.style.cursor = "none";
},
autoscale: function (containerWidth, containerHeight) { autoscale: function (containerWidth, containerHeight) {
var vp = this._viewportLoc; var vp = this._viewportLoc;
var targetAspectRatio = containerWidth / containerHeight; var targetAspectRatio = containerWidth / containerHeight;
@ -655,44 +643,3 @@ Display.prototype = {
} }
}, },
}; };
// Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
if ((w === 0) || (h === 0)) {
target.style.cursor = 'none';
return;
}
var cur = []
var y, x;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
var idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
idx = ((w * y) + x) * 4;
cur.push(pixels[idx + 2]); // red
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx]); // blue
cur.push(alpha); // alpha
}
}
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = w;
canvas.height = h;
var img;
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
img = new ImageData(new Uint8ClampedArray(cur), w, h);
} else {
img = ctx.createImageData(w, h);
img.data.set(new Uint8ClampedArray(cur));
}
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);
var url = canvas.toDataURL();
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
};

View File

@ -17,6 +17,7 @@ import EventTargetMixin from './util/eventtarget.js';
import Display from "./display.js"; import Display from "./display.js";
import Keyboard from "./input/keyboard.js"; import Keyboard from "./input/keyboard.js";
import Mouse from "./input/mouse.js"; import Mouse from "./input/mouse.js";
import Cursor from "./util/cursor.js";
import Websock from "./websock.js"; import Websock from "./websock.js";
import DES from "./des.js"; import DES from "./des.js";
import KeyTable from "./input/keysym.js"; import KeyTable from "./input/keysym.js";
@ -164,6 +165,8 @@ export default function RFB(target, url, options) {
this._canvas.tabIndex = -1; this._canvas.tabIndex = -1;
this._screen.appendChild(this._canvas); this._screen.appendChild(this._canvas);
this._cursor = new Cursor();
// populate encHandlers with bound versions // populate encHandlers with bound versions
this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this); this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this);
this._encHandlers[encodings.encodingCopyRect] = RFB.encodingHandlers.COPYRECT.bind(this); this._encHandlers[encodings.encodingCopyRect] = RFB.encodingHandlers.COPYRECT.bind(this);
@ -413,6 +416,8 @@ RFB.prototype = {
// Make our elements part of the page // Make our elements part of the page
this._target.appendChild(this._screen); this._target.appendChild(this._screen);
this._cursor.attach(this._canvas);
// Monitor size changes of the screen // Monitor size changes of the screen
// FIXME: Use ResizeObserver, or hidden overflow // FIXME: Use ResizeObserver, or hidden overflow
window.addEventListener('resize', this._eventHandlers.windowResize); window.addEventListener('resize', this._eventHandlers.windowResize);
@ -426,6 +431,7 @@ RFB.prototype = {
_disconnect: function () { _disconnect: function () {
Log.Debug(">> RFB.disconnect"); Log.Debug(">> RFB.disconnect");
this._cursor.detach();
this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas); this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas); this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
window.removeEventListener('resize', this._eventHandlers.windowResize); window.removeEventListener('resize', this._eventHandlers.windowResize);
@ -1241,10 +1247,6 @@ RFB.prototype = {
this._timing.fbu_rt_start = (new Date()).getTime(); this._timing.fbu_rt_start = (new Date()).getTime();
this._timing.pixels = 0; this._timing.pixels = 0;
// Cursor will be server side until the server decides to honor
// our request and send over the cursor image
this._display.disableLocalCursor();
this._updateConnectionState('connected'); this._updateConnectionState('connected');
return true; return true;
}, },
@ -2522,9 +2524,9 @@ RFB.encodingHandlers = {
this._FBU.bytes = pixelslength + masklength; this._FBU.bytes = pixelslength + masklength;
if (this._sock.rQwait("cursor encoding", this._FBU.bytes)) { return false; } if (this._sock.rQwait("cursor encoding", this._FBU.bytes)) { return false; }
this._display.changeCursor(this._sock.rQshiftBytes(pixelslength), this._cursor.change(this._sock.rQshiftBytes(pixelslength),
this._sock.rQshiftBytes(masklength), this._sock.rQshiftBytes(masklength),
x, y, w, h); x, y, w, h);
this._FBU.bytes = 0; this._FBU.bytes = 0;
this._FBU.rects--; this._FBU.rects--;

71
core/util/cursor.js Normal file
View File

@ -0,0 +1,71 @@
/*
* noVNC: HTML5 VNC client
* Copyright 2018 Pierre Ossman for noVNC
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
function Cursor(container) {
this._target = null;
}
Cursor.prototype = {
attach: function (target) {
if (this._target) {
this.detach();
}
this._target = target;
this.clear();
},
detach: function () {
this._target = null;
},
change: function (pixels, mask, hotx, hoty, w, h) {
if ((w === 0) || (h === 0)) {
this.clear();
return;
}
let cur = []
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
let idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
let alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
idx = ((w * y) + x) * 4;
cur.push(pixels[idx + 2]); // red
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx]); // blue
cur.push(alpha); // alpha
}
}
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
canvas.width = w;
canvas.height = h;
let img;
try {
// IE doesn't support this
img = new ImageData(new Uint8ClampedArray(cur), w, h);
} catch (ex) {
img = ctx.createImageData(w, h);
img.data.set(new Uint8ClampedArray(cur));
}
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);
let url = this._canvas.toDataURL();
this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
},
clear: function () {
this._target.style.cursor = 'none';
},
};
export default Cursor;

View File

@ -113,9 +113,6 @@ None
| blitRgbImage | (x, y, width, height, arr, offset, from_queue) | Blit RGB encoded image to display | blitRgbImage | (x, y, width, height, arr, offset, from_queue) | Blit RGB encoded image to display
| blitRgbxImage | (x, y, width, height, arr, offset, from_queue) | Blit RGBX encoded image to display | blitRgbxImage | (x, y, width, height, arr, offset, from_queue) | Blit RGBX encoded image to display
| drawImage | (img, x, y) | Draw image and track damage | drawImage | (img, x, y) | Draw image and track damage
| changeCursor | (pixels, mask, hotx, hoty, w, h) | Change cursor appearance
| defaultCursor | () | Restore default cursor appearance
| disableLocalCursor | () | Disable local (client-side) cursor
| autoscale | (containerWidth, containerHeight) | Scale the display | autoscale | (containerWidth, containerHeight) | Scale the display
### 2.3.3 Callbacks ### 2.3.3 Callbacks