Separate out cursor handling
Make cursor handling more generic in preparation for generic handling of corner cases.
This commit is contained in:
parent
e62b4ccb5e
commit
b475eed5fa
|
@ -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';
|
|
||||||
};
|
|
||||||
|
|
16
core/rfb.js
16
core/rfb.js
|
@ -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--;
|
||||||
|
|
|
@ -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;
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue