]>
git.proxmox.com Git - mirror_novnc.git/blob - include/canvas.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2010 Joel Martin
4 * Licensed under LGPL-3 (see LICENSE.LGPL-3)
6 * See README.md for usage and integration instructions.
10 /*jslint white: false, bitwise: false */
11 /*global window, console, $, Util */
13 var Canvas
, Canvas_native
;
16 var pre
, start
= "<script src='", end
= "'><\/script>";
17 if (document
.createElement('canvas').getContext
) {
20 pre
= (typeof VNC_uri_prefix
!== "undefined") ?
21 VNC_uri_prefix
: "include/";
22 document
.write(start
+ pre
+ "excanvas.js" + end
);
23 Canvas_native
= false;
27 // Everything namespaced inside Canvas
47 onMouseButton: function(e
, down
) {
49 evt
= (e
? e
: window
.event
);
50 pos
= Util
.getEventPosition(e
, $(Canvas
.id
));
51 bmask
= 1 << evt
.button
;
52 //console.log('mouse ' + pos.x + "," + pos.y + " down: " + down + " bmask: " + bmask);
53 if (Canvas
.mouseButton
) {
54 Canvas
.mouseButton(pos
.x
, pos
.y
, down
, bmask
);
60 onMouseDown: function (e
) {
61 Canvas
.onMouseButton(e
, 1);
64 onMouseUp: function (e
) {
65 Canvas
.onMouseButton(e
, 0);
68 onMouseWheel: function (e
) {
69 var evt
, pos
, bmask
, wheelData
;
70 evt
= (e
? e
: window
.event
);
71 pos
= Util
.getEventPosition(e
, $(Canvas
.id
));
72 wheelData
= evt
.detail
? evt
.detail
* -1 : evt
.wheelDelta
/ 40;
78 //console.log('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y);
79 if (Canvas
.mouseButton
) {
80 Canvas
.mouseButton(pos
.x
, pos
.y
, 1, bmask
);
81 Canvas
.mouseButton(pos
.x
, pos
.y
, 0, bmask
);
88 onMouseMove: function (e
) {
90 evt
= (e
? e
: window
.event
);
91 pos
= Util
.getEventPosition(e
, $(Canvas
.id
));
92 //console.log('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y);
93 if (Canvas
.mouseMove
) {
94 Canvas
.mouseMove(pos
.x
, pos
.y
);
98 onKeyDown: function (e
) {
99 //console.log("keydown: " + Canvas.getKeysym(e));
100 if (! Canvas
.focused
) {
103 if (Canvas
.keyPress
) {
104 Canvas
.keyPress(Canvas
.getKeysym(e
), 1);
110 onKeyUp : function (e
) {
111 //console.log("keyup: " + Canvas.getKeysym(e));
112 if (! Canvas
.focused
) {
115 if (Canvas
.keyPress
) {
116 Canvas
.keyPress(Canvas
.getKeysym(e
), 0);
122 onMouseDisable: function (e
) {
124 evt
= (e
? e
: window
.event
);
125 pos
= Util
.getPosition($(Canvas
.id
));
126 /* Stop propagation if inside canvas area */
127 if ((evt
.clientX
>= pos
.x
) &&
128 (evt
.clientY
>= pos
.y
) &&
129 (evt
.clientX
< (pos
.x
+ Canvas
.c_wx
)) &&
130 (evt
.clientY
< (pos
.y
+ Canvas
.c_wy
))) {
131 //console.log("mouse event disabled");
135 //console.log("mouse event not disabled");
140 init: function (id
) {
141 var c
, imgTest
, arora
;
142 console
.log(">> Canvas.init");
148 console
.log("Using native canvas");
149 // Use default Canvas functions
151 console
.warn("Using excanvas canvas emulation");
152 //G_vmlCanvasManager.init(c);
153 G_vmlCanvasManager
.initElement(c
);
156 if (! c
.getContext
) { throw("No getContext method"); }
157 Canvas
.ctx
= c
.getContext('2d');
162 * Determine browser Canvas feature support
163 * and select fastest rendering methods
166 Canvas
.has_imageData
= false;
168 imgTest
= Canvas
.ctx
.getImageData(0, 0, 1,1);
169 imgTest
.data
[0] = 123;
170 imgTest
.data
[3] = 255;
171 Canvas
.ctx
.putImageData(imgTest
, 0, 0);
172 tval
= Canvas
.ctx
.getImageData(0, 0, 1, 1).data
[0];
174 Canvas
.has_imageData
= true;
178 if (Canvas
.has_imageData
) {
179 console
.log("Canvas supports imageData");
180 Canvas
.force_canvas
= false;
181 if (Canvas
.ctx
.createImageData
) {
182 // If it's there, it's faster
183 console
.log("Using Canvas createImageData");
184 Canvas
._imageData
= Canvas
._imageDataCreate
;
185 } else if (Canvas
.ctx
.getImageData
) {
186 console
.log("Using Canvas getImageData");
187 Canvas
._imageData
= Canvas
._imageDataGet
;
189 console
.log("Prefering javascript operations");
190 Canvas
.prefer_js
= true;
191 Canvas
._rgbxImage
= Canvas
._rgbxImageData
;
192 Canvas
._cmapImage
= Canvas
._cmapImageData
;
194 console
.log("Canvas lacks imageData, using fillRect (slow)");
195 Canvas
.force_canvas
= true;
196 Canvas
.prefer_js
= false;
197 Canvas
._rgbxImage
= Canvas
._rgbxImageFill
;
198 Canvas
._cmapImage
= Canvas
._cmapImageFill
;
201 Canvas
.colourMap
= [];
202 Canvas
.prevStyle
= "";
203 Canvas
.focused
= true;
205 //console.log("<< Canvas.init");
210 start: function (keyPress
, mouseButton
, mouseMove
) {
212 console
.log(">> Canvas.start");
215 Canvas
.keyPress
= keyPress
|| null;
216 Canvas
.mouseButton
= mouseButton
|| null;
217 Canvas
.mouseMove
= mouseMove
|| null;
219 Util
.addEvent(document
, 'keydown', Canvas
.onKeyDown
);
220 Util
.addEvent(document
, 'keyup', Canvas
.onKeyUp
);
221 Util
.addEvent(c
, 'mousedown', Canvas
.onMouseDown
);
222 Util
.addEvent(c
, 'mouseup', Canvas
.onMouseUp
);
223 Util
.addEvent(c
, 'mousemove', Canvas
.onMouseMove
);
224 Util
.addEvent(c
, (Util
.Engine
.gecko
) ? 'DOMMouseScroll' : 'mousewheel',
225 Canvas
.onMouseWheel
);
227 /* Work around right and middle click browser behaviors */
228 Util
.addEvent(document
, 'click', Canvas
.onMouseDisable
);
229 Util
.addEvent(document
.body
, 'contextmenu', Canvas
.onMouseDisable
);
231 //console.log("<< Canvas.start");
235 Canvas
.resize(640, 20);
236 Canvas
.ctx
.clearRect(0, 0, Canvas
.c_wx
, Canvas
.c_wy
);
239 resize: function (width
, height
, true_color
) {
240 var c
= $(Canvas
.id
);
242 if (typeof true_color
!== "undefined") {
243 Canvas
.true_color
= true_color
;
249 Canvas
.c_wx
= c
.offsetWidth
;
250 Canvas
.c_wy
= c
.offsetHeight
;
254 var c
= $(Canvas
.id
);
255 Util
.removeEvent(document
, 'keydown', Canvas
.onKeyDown
);
256 Util
.removeEvent(document
, 'keyup', Canvas
.onKeyUp
);
257 Util
.removeEvent(c
, 'mousedown', Canvas
.onMouseDown
);
258 Util
.removeEvent(c
, 'mouseup', Canvas
.onMouseUp
);
259 Util
.removeEvent(c
, 'mousemove', Canvas
.onMouseMove
);
260 Util
.removeEvent(c
, (Util
.Engine
.gecko
) ? 'DOMMouseScroll' : 'mousewheel',
261 Canvas
.onMouseWheel
);
263 /* Work around right and middle click browser behaviors */
264 Util
.removeEvent(document
, 'click', Canvas
.onMouseDisable
);
265 Util
.removeEvent(document
.body
, 'contextmenu', Canvas
.onMouseDisable
);
269 * Tile rendering functions optimized for rendering engines.
271 * - In Chrome/webkit, Javascript image data array manipulations are
272 * faster than direct Canvas fillStyle, fillRect rendering. In
273 * gecko, Javascript array handling is much slower.
275 getTile: function(x
, y
, width
, height
, color
) {
276 var img
, data
, p
, rgb
, red
, green
, blue
, j
, i
;
277 img
= {'x': x
, 'y': y
, 'width': width
, 'height': height
,
279 if (Canvas
.prefer_js
) {
281 if (Canvas
.true_color
) {
284 rgb
= Canvas
.colourMap
[color
[0]];
289 for (j
= 0; j
< height
; j
+= 1) {
290 for (i
= 0; i
< width
; i
+= 1) {
291 p
= (i
+ (j
* width
) ) * 4;
295 //data[p + 3] = 255; // Set Alpha
299 Canvas
.fillRect(x
, y
, width
, height
, color
);
304 setSubTile: function(img
, x
, y
, w
, h
, color
) {
305 var data
, p
, rgb
, red
, green
, blue
, width
, j
, i
;
306 if (Canvas
.prefer_js
) {
309 if (Canvas
.true_color
) {
312 rgb
= Canvas
.colourMap
[color
[0]];
317 for (j
= 0; j
< h
; j
+= 1) {
318 for (i
= 0; i
< w
; i
+= 1) {
319 p
= (x
+ i
+ ((y
+ j
) * width
) ) * 4;
323 //img.data[p + 3] = 255; // Set Alpha
327 Canvas
.fillRect(img
.x
+ x
, img
.y
+ y
, w
, h
, color
);
331 putTile: function(img
) {
332 if (Canvas
.prefer_js
) {
333 Canvas
._rgbxImage(img
.x
, img
.y
, img
.width
, img
.height
, img
.data
, 0);
335 // No-op, under gecko already done by setSubTile
339 _imageDataGet: function(width
, height
) {
340 return Canvas
.ctx
.getImageData(0, 0, width
, height
);
342 _imageDataCreate: function(width
, height
) {
343 return Canvas
.ctx
.createImageData(width
, height
);
345 _imageDataRaw: function(width
, height
) {
346 return {'data': [], 'width': width
, 'height': height
};
349 _rgbxImageData: function(x
, y
, width
, height
, arr
, offset
) {
351 img
= Canvas
._imageData(width
, height
);
353 for (i
=0, j
=offset
; i
< (width
* height
* 4); i
=i
+4, j
=j
+4) {
354 data
[i
+ 0] = arr
[j
+ 0];
355 data
[i
+ 1] = arr
[j
+ 1];
356 data
[i
+ 2] = arr
[j
+ 2];
357 data
[i
+ 3] = 255; // Set Alpha
359 Canvas
.ctx
.putImageData(img
, x
, y
);
362 // really slow fallback if we don't have imageData
363 _rgbxImageFill: function(x
, y
, width
, height
, arr
, offset
) {
365 for (i
=0, j
=offset
; i
< (width
* height
); i
+=1, j
+=4) {
366 Canvas
.fillRect(x
+sx
, y
+sy
, 1, 1, [arr
[j
+0], arr
[j
+1], arr
[j
+2]]);
368 if ((sx
% width
) === 0) {
375 _cmapImageData: function(x
, y
, width
, height
, arr
, offset
) {
376 var img
, i
, j
, data
, rgb
, cmap
;
377 img
= Canvas
._imageData(width
, height
);
379 cmap
= Canvas
.colourMap
;
380 for (i
=0, j
=offset
; i
< (width
* height
* 4); i
+=4, j
+=1) {
382 data
[i
+ 0] = rgb
[0];
383 data
[i
+ 1] = rgb
[1];
384 data
[i
+ 2] = rgb
[2];
385 data
[i
+ 3] = 255; // Set Alpha
387 Canvas
.ctx
.putImageData(img
, x
, y
);
390 _cmapImageFill: function(x
, y
, width
, height
, arr
, offset
) {
392 cmap
= Canvas
.colourMap
;
393 console
.log("here1: arr[2]: " + arr
[2] + ", cmap[arr[2]]: " + cmap
[arr
[2]]);
394 for (i
=0, j
=offset
; i
< (width
* height
); i
+=1, j
+=1) {
395 Canvas
.fillRect(x
+sx
, y
+sy
, 1, 1, [arr
[j
]]);
397 if ((sx
% width
) === 0) {
405 blitImage: function(x
, y
, width
, height
, arr
, offset
) {
406 if (Canvas
.true_color
) {
407 Canvas
._rgbxImage(x
, y
, width
, height
, arr
, offset
);
409 Canvas
._cmapImage(x
, y
, width
, height
, arr
, offset
);
413 blitStringImage: function(str
, x
, y
) {
414 var img
= new Image();
415 img
.onload = function () { Canvas
.ctx
.drawImage(img
, x
, y
); };
419 setFillColor: function(color
) {
421 if (Canvas
.true_color
) {
424 rgb
= Canvas
.colourMap
[color
[0]];
426 if (newStyle
!== Canvas
.prevStyle
) {
427 newStyle
= "rgb(" + rgb
[0] + "," + rgb
[1] + "," + rgb
[2] + ")";
428 Canvas
.ctx
.fillStyle
= newStyle
;
429 Canvas
.prevStyle
= newStyle
;
433 fillRect: function(x
, y
, width
, height
, color
) {
434 Canvas
.setFillColor(color
);
435 Canvas
.ctx
.fillRect(x
, y
, width
, height
);
438 copyImage: function(old_x
, old_y
, new_x
, new_y
, width
, height
) {
439 Canvas
.ctx
.drawImage($(Canvas
.id
), old_x
, old_y
, width
, height
,
440 new_x
, new_y
, width
, height
);
443 /* Translate DOM key event to keysym value */
444 getKeysym: function(e
) {
446 evt
= (e
? e
: window
.event
);
448 /* Remap modifier and special keys */
449 switch ( evt
.keyCode
) {
450 case 8 : keysym
= 0xFF08; break; // BACKSPACE
451 case 9 : keysym
= 0xFF09; break; // TAB
452 case 13 : keysym
= 0xFF0D; break; // ENTER
453 case 27 : keysym
= 0xFF1B; break; // ESCAPE
454 case 45 : keysym
= 0xFF63; break; // INSERT
455 case 46 : keysym
= 0xFFFF; break; // DELETE
456 case 36 : keysym
= 0xFF50; break; // HOME
457 case 35 : keysym
= 0xFF57; break; // END
458 case 33 : keysym
= 0xFF55; break; // PAGE_UP
459 case 34 : keysym
= 0xFF56; break; // PAGE_DOWN
460 case 37 : keysym
= 0xFF51; break; // LEFT
461 case 38 : keysym
= 0xFF52; break; // UP
462 case 39 : keysym
= 0xFF53; break; // RIGHT
463 case 40 : keysym
= 0xFF54; break; // DOWN
464 case 112 : keysym
= 0xFFBE; break; // F1
465 case 113 : keysym
= 0xFFBF; break; // F2
466 case 114 : keysym
= 0xFFC0; break; // F3
467 case 115 : keysym
= 0xFFC1; break; // F4
468 case 116 : keysym
= 0xFFC2; break; // F5
469 case 117 : keysym
= 0xFFC3; break; // F6
470 case 118 : keysym
= 0xFFC4; break; // F7
471 case 119 : keysym
= 0xFFC5; break; // F8
472 case 120 : keysym
= 0xFFC6; break; // F9
473 case 121 : keysym
= 0xFFC7; break; // F10
474 case 122 : keysym
= 0xFFC8; break; // F11
475 case 123 : keysym
= 0xFFC9; break; // F12
476 case 16 : keysym
= 0xFFE1; break; // SHIFT
477 case 17 : keysym
= 0xFFE3; break; // CONTROL
478 //case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option)
479 case 18 : keysym
= 0xFFE9; break; // Left ALT (Mac Command)
480 default : keysym
= evt
.keyCode
; break;
485 case 186 : keysym
= 59; break; // ; (IE)
486 case 187 : keysym
= 61; break; // = (IE)
487 case 188 : keysym
= 44; break; // , (Mozilla, IE)
488 case 109 : // - (Mozilla)
489 if (Util
.Engine
.gecko
) {
492 case 189 : keysym
= 45; break; // - (IE)
493 case 190 : keysym
= 46; break; // . (Mozilla, IE)
494 case 191 : keysym
= 47; break; // / (Mozilla, IE)
495 case 192 : keysym
= 96; break; // ` (Mozilla, IE)
496 case 219 : keysym
= 91; break; // [ (Mozilla, IE)
497 case 220 : keysym
= 92; break; // \ (Mozilla, IE)
498 case 221 : keysym
= 93; break; // ] (Mozilla, IE)
499 case 222 : keysym
= 39; break; // ' (Mozilla, IE)
502 /* Remap shifted and unshifted keys */
503 if (!!evt
.shiftKey
) {
505 case 48 : keysym
= 41 ; break; // ) (shifted 0)
506 case 49 : keysym
= 33 ; break; // ! (shifted 1)
507 case 50 : keysym
= 64 ; break; // @ (shifted 2)
508 case 51 : keysym
= 35 ; break; // # (shifted 3)
509 case 52 : keysym
= 36 ; break; // $ (shifted 4)
510 case 53 : keysym
= 37 ; break; // % (shifted 5)
511 case 54 : keysym
= 94 ; break; // ^ (shifted 6)
512 case 55 : keysym
= 38 ; break; // & (shifted 7)
513 case 56 : keysym
= 42 ; break; // * (shifted 8)
514 case 57 : keysym
= 40 ; break; // ( (shifted 9)
516 case 59 : keysym
= 58 ; break; // : (shifted `)
517 case 61 : keysym
= 43 ; break; // + (shifted ;)
518 case 44 : keysym
= 60 ; break; // < (shifted ,)
519 case 45 : keysym
= 95 ; break; // _ (shifted -)
520 case 46 : keysym
= 62 ; break; // > (shifted .)
521 case 47 : keysym
= 63 ; break; // ? (shifted /)
522 case 96 : keysym
= 126; break; // ~ (shifted `)
523 case 91 : keysym
= 123; break; // { (shifted [)
524 case 92 : keysym
= 124; break; // | (shifted \)
525 case 93 : keysym
= 125; break; // } (shifted ])
526 case 39 : keysym
= 34 ; break; // " (shifted ')
528 } else if ((keysym
>= 65) && (keysym
<=90)) {
529 /* Remap unshifted A-Z */