]>
git.proxmox.com Git - mirror_novnc.git/blob - core/util/cursor.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2018 The noVNC Authors
4 * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
7 import { supportsCursorURIs
, isTouchDevice
} from './browser.js';
9 const useFallback
= !supportsCursorURIs() || isTouchDevice
;
11 export default class Cursor
{
12 constructor(container
) {
15 this._canvas
= document
.createElement('canvas');
18 this._canvas
.style
.position
= 'fixed';
19 this._canvas
.style
.zIndex
= '65535';
20 this._canvas
.style
.pointerEvents
= 'none';
21 // Can't use "display" because of Firefox bug #1445997
22 this._canvas
.style
.visibility
= 'hidden';
23 document
.body
.appendChild(this._canvas
);
26 this._position
= { x
: 0, y
: 0 };
27 this._hotSpot
= { x
: 0, y
: 0 };
29 this._eventHandlers
= {
30 'mouseover': this._handleMouseOver
.bind(this),
31 'mouseleave': this._handleMouseLeave
.bind(this),
32 'mousemove': this._handleMouseMove
.bind(this),
33 'mouseup': this._handleMouseUp
.bind(this),
34 'touchstart': this._handleTouchStart
.bind(this),
35 'touchmove': this._handleTouchMove
.bind(this),
36 'touchend': this._handleTouchEnd
.bind(this),
45 this._target
= target
;
48 // FIXME: These don't fire properly except for mouse
49 /// movement in IE. We want to also capture element
50 // movement, size changes, visibility, etc.
51 const options
= { capture
: true, passive
: true };
52 this._target
.addEventListener('mouseover', this._eventHandlers
.mouseover
, options
);
53 this._target
.addEventListener('mouseleave', this._eventHandlers
.mouseleave
, options
);
54 this._target
.addEventListener('mousemove', this._eventHandlers
.mousemove
, options
);
55 this._target
.addEventListener('mouseup', this._eventHandlers
.mouseup
, options
);
57 // There is no "touchleave" so we monitor touchstart globally
58 window
.addEventListener('touchstart', this._eventHandlers
.touchstart
, options
);
59 this._target
.addEventListener('touchmove', this._eventHandlers
.touchmove
, options
);
60 this._target
.addEventListener('touchend', this._eventHandlers
.touchend
, options
);
68 const options
= { capture
: true, passive
: true };
69 this._target
.removeEventListener('mouseover', this._eventHandlers
.mouseover
, options
);
70 this._target
.removeEventListener('mouseleave', this._eventHandlers
.mouseleave
, options
);
71 this._target
.removeEventListener('mousemove', this._eventHandlers
.mousemove
, options
);
72 this._target
.removeEventListener('mouseup', this._eventHandlers
.mouseup
, options
);
74 window
.removeEventListener('touchstart', this._eventHandlers
.touchstart
, options
);
75 this._target
.removeEventListener('touchmove', this._eventHandlers
.touchmove
, options
);
76 this._target
.removeEventListener('touchend', this._eventHandlers
.touchend
, options
);
82 change(rgba
, hotx
, hoty
, w
, h
) {
83 if ((w
=== 0) || (h
=== 0)) {
88 this._position
.x
= this._position
.x
+ this._hotSpot
.x
- hotx
;
89 this._position
.y
= this._position
.y
+ this._hotSpot
.y
- hoty
;
90 this._hotSpot
.x
= hotx
;
91 this._hotSpot
.y
= hoty
;
93 let ctx
= this._canvas
.getContext('2d');
95 this._canvas
.width
= w
;
96 this._canvas
.height
= h
;
100 // IE doesn't support this
101 img
= new ImageData(new Uint8ClampedArray(rgba
), w
, h
);
103 img
= ctx
.createImageData(w
, h
);
104 img
.data
.set(new Uint8ClampedArray(rgba
));
106 ctx
.clearRect(0, 0, w
, h
);
107 ctx
.putImageData(img
, 0, 0);
110 this._updatePosition();
112 let url
= this._canvas
.toDataURL();
113 this._target
.style
.cursor
= 'url(' + url
+ ')' + hotx
+ ' ' + hoty
+ ', default';
118 this._target
.style
.cursor
= 'none';
119 this._canvas
.width
= 0;
120 this._canvas
.height
= 0;
121 this._position
.x
= this._position
.x
+ this._hotSpot
.x
;
122 this._position
.y
= this._position
.y
+ this._hotSpot
.y
;
127 _handleMouseOver(event
) {
128 // This event could be because we're entering the target, or
129 // moving around amongst its sub elements. Let the move handler
131 this._handleMouseMove(event
);
134 _handleMouseLeave(event
) {
138 _handleMouseMove(event
) {
139 this._updateVisibility(event
.target
);
141 this._position
.x
= event
.clientX
- this._hotSpot
.x
;
142 this._position
.y
= event
.clientY
- this._hotSpot
.y
;
144 this._updatePosition();
147 _handleMouseUp(event
) {
148 // We might get this event because of a drag operation that
149 // moved outside of the target. Check what's under the cursor
150 // now and adjust visibility based on that.
151 let target
= document
.elementFromPoint(event
.clientX
, event
.clientY
);
152 this._updateVisibility(target
);
155 _handleTouchStart(event
) {
156 // Just as for mouseover, we let the move handler deal with it
157 this._handleTouchMove(event
);
160 _handleTouchMove(event
) {
161 this._updateVisibility(event
.target
);
163 this._position
.x
= event
.changedTouches
[0].clientX
- this._hotSpot
.x
;
164 this._position
.y
= event
.changedTouches
[0].clientY
- this._hotSpot
.y
;
166 this._updatePosition();
169 _handleTouchEnd(event
) {
170 // Same principle as for mouseup
171 let target
= document
.elementFromPoint(event
.changedTouches
[0].clientX
,
172 event
.changedTouches
[0].clientY
);
173 this._updateVisibility(target
);
177 if (this._canvas
.style
.visibility
=== 'hidden') {
178 this._canvas
.style
.visibility
= '';
183 if (this._canvas
.style
.visibility
!== 'hidden') {
184 this._canvas
.style
.visibility
= 'hidden';
188 // Should we currently display the cursor?
189 // (i.e. are we over the target, or a child of the target without a
190 // different cursor set)
191 _shouldShowCursor(target
) {
193 if (target
=== this._target
) {
196 // Other part of the DOM?
197 if (!this._target
.contains(target
)) {
200 // Has the child its own cursor?
201 // FIXME: How can we tell that a sub element has an
202 // explicit "cursor: none;"?
203 if (window
.getComputedStyle(target
).cursor
!== 'none') {
209 _updateVisibility(target
) {
210 if (this._shouldShowCursor(target
)) {
218 this._canvas
.style
.left
= this._position
.x
+ "px";
219 this._canvas
.style
.top
= this._position
.y
+ "px";