]>
git.proxmox.com Git - mirror_novnc.git/blob - core/util/cursor.js
da72723b395485c4665ce2456d98a092b2ce4d90
2 * noVNC: HTML5 VNC client
3 * Copyright 2018 Pierre Ossman for noVNC
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 function Cursor(container
) {
14 this._canvas
= document
.createElement('canvas');
17 this._canvas
.style
.position
= 'fixed';
18 this._canvas
.style
.zIndex
= '65535';
19 this._canvas
.style
.pointerEvents
= 'none';
20 // Can't use "display" because of Firefox bug #1445997
21 this._canvas
.style
.visibility
= 'hidden';
22 document
.body
.appendChild(this._canvas
);
25 this._position
= { x
: 0, y
: 0 };
26 this._hotSpot
= { x
: 0, y
: 0 };
28 this._eventHandlers
= {
29 'mouseover': this._handleMouseOver
.bind(this),
30 'mouseleave': this._handleMouseLeave
.bind(this),
31 'mousemove': this._handleMouseMove
.bind(this),
32 'mouseup': this._handleMouseUp
.bind(this),
33 'touchstart': this._handleTouchStart
.bind(this),
34 'touchmove': this._handleTouchMove
.bind(this),
35 'touchend': this._handleTouchEnd
.bind(this),
40 attach: function (target
) {
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: function (pixels
, mask
, hotx
, hoty
, w
, h
) {
83 if ((w
=== 0) || (h
=== 0)) {
89 for (let y
= 0; y
< h
; y
++) {
90 for (let x
= 0; x
< w
; x
++) {
91 let idx
= y
* Math
.ceil(w
/ 8) + Math
.floor(x
/ 8);
92 let alpha
= (mask
[idx
] << (x
% 8)) & 0x80 ? 255 : 0;
93 idx
= ((w
* y
) + x
) * 4;
94 cur
.push(pixels
[idx
+ 2]); // red
95 cur
.push(pixels
[idx
+ 1]); // green
96 cur
.push(pixels
[idx
]); // blue
97 cur
.push(alpha
); // alpha
101 this._position
.x
= this._position
.x
+ this._hotSpot
.x
- hotx
;
102 this._position
.y
= this._position
.y
+ this._hotSpot
.y
- hoty
;
103 this._hotSpot
.x
= hotx
;
104 this._hotSpot
.y
= hoty
;
106 let ctx
= this._canvas
.getContext('2d');
108 this._canvas
.width
= w
;
109 this._canvas
.height
= h
;
113 // IE doesn't support this
114 img
= new ImageData(new Uint8ClampedArray(cur
), w
, h
);
116 img
= ctx
.createImageData(w
, h
);
117 img
.data
.set(new Uint8ClampedArray(cur
));
119 ctx
.clearRect(0, 0, w
, h
);
120 ctx
.putImageData(img
, 0, 0);
123 this._updatePosition();
125 let url
= this._canvas
.toDataURL();
126 this._target
.style
.cursor
= 'url(' + url
+ ')' + hotx
+ ' ' + hoty
+ ', default';
131 this._target
.style
.cursor
= 'none';
132 this._canvas
.width
= 0;
133 this._canvas
.height
= 0;
134 this._position
.x
= this._position
.x
+ this._hotSpot
.x
;
135 this._position
.y
= this._position
.y
+ this._hotSpot
.y
;
140 _handleMouseOver: function (event
) {
141 // This event could be because we're entering the target, or
142 // moving around amongst its sub elements. Let the move handler
144 this._handleMouseMove(event
);
147 _handleMouseLeave: function (event
) {
151 _handleMouseMove: function (event
) {
152 this._updateVisibility(event
.target
);
154 this._position
.x
= event
.clientX
- this._hotSpot
.x
;
155 this._position
.y
= event
.clientY
- this._hotSpot
.y
;
157 this._updatePosition();
160 _handleMouseUp: function (event
) {
161 // We might get this event because of a drag operation that
162 // moved outside of the target. Check what's under the cursor
163 // now and adjust visibility based on that.
164 let target
= document
.elementFromPoint(event
.clientX
, event
.clientY
);
165 this._updateVisibility(target
);
168 _handleTouchStart: function (event
) {
169 // Just as for mouseover, we let the move handler deal with it
170 this._handleTouchMove(event
);
173 _handleTouchMove: function (event
) {
174 this._updateVisibility(event
.target
);
176 this._position
.x
= event
.changedTouches
[0].clientX
- this._hotSpot
.x
;
177 this._position
.y
= event
.changedTouches
[0].clientY
- this._hotSpot
.y
;
179 this._updatePosition();
182 _handleTouchEnd: function (event
) {
183 // Same principle as for mouseup
184 let target
= document
.elementFromPoint(event
.changedTouches
[0].clientX
,
185 event
.changedTouches
[0].clientY
);
186 this._updateVisibility(target
);
189 _showCursor: function () {
190 if (this._canvas
.style
.visibility
=== 'hidden')
191 this._canvas
.style
.visibility
= '';
194 _hideCursor: function () {
195 if (this._canvas
.style
.visibility
!== 'hidden')
196 this._canvas
.style
.visibility
= 'hidden';
199 // Should we currently display the cursor?
200 // (i.e. are we over the target, or a child of the target without a
201 // different cursor set)
202 _shouldShowCursor: function (target
) {
204 if (target
=== this._target
)
206 // Other part of the DOM?
207 if (!this._target
.contains(target
))
209 // Has the child its own cursor?
210 // FIXME: How can we tell that a sub element has an
211 // explicit "cursor: none;"?
212 if (window
.getComputedStyle(target
).cursor
!== 'none')
217 _updateVisibility: function (target
) {
218 if (this._shouldShowCursor(target
))
224 _updatePosition: function () {
225 this._canvas
.style
.left
= this._position
.x
+ "px";
226 this._canvas
.style
.top
= this._position
.y
+ "px";
230 export default Cursor
;