]> git.proxmox.com Git - mirror_novnc.git/blob - include/canvas.js
Change license to LGPL-3 and add some implementation notes.
[mirror_novnc.git] / include / canvas.js
1 /*
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2010 Joel Martin
4 * Licensed under LGPL-3 (see LICENSE.LGPL-3)
5 *
6 * See README.md for usage and integration instructions.
7 */
8 "use strict";
9
10 /*global window, $, Browser */
11
12 // Everything namespaced inside Canvas
13 var Canvas = {
14
15 prefer_js : false,
16
17 c_x : 0,
18 c_y : 0,
19 c_wx : 0,
20 c_wy : 0,
21 ctx : null,
22
23 prevStyle: "",
24
25 mouseDown: function (e) {
26 var evt = e.event || window.event;
27 e.stop();
28 console.log('mouse ' + evt.which + '/' + evt.button + ' down:' +
29 (evt.clientX - Canvas.c_x) + "," + (evt.clientY - Canvas.c_y));
30 },
31
32 mouseUp: function (e) {
33 var evt = e.event || window.event;
34 e.stop();
35 console.log('mouse ' + evt.which + '/' + evt.button + ' up:' +
36 (evt.clientX - Canvas.c_x) + "," + (evt.clientY - Canvas.c_y));
37 },
38
39 mouseMove: function (e) {
40 var evt = e.event || window.event;
41 console.log('mouse ' + evt.which + '/' + evt.button + ' up:' +
42 (evt.clientX - Canvas.c_x) + "," + (evt.clientY - Canvas.c_y));
43 },
44
45 mouseWheel: function (e) {
46 var evt = e.event || window.event;
47 //e = e ? e : window.event;
48 var wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
49 console.log('mouse scroll by ' + wheelData + ':' +
50 (evt.clientX - Canvas.c_x) + "," + (evt.clientY - Canvas.c_y));
51 },
52
53
54 keyDown: function (e) {
55 e.stop();
56 console.log("keydown: " + e.key + "(" + e.code + ")");
57 },
58
59 keyUp : function (e) {
60 e.stop();
61 console.log("keyup: " + e.key + "(" + e.code + ")");
62 },
63
64 ctxDisable: function (e) {
65 var evt = e.event || window.event;
66 /* Stop propagation if inside canvas area */
67 if ((evt.clientX >= Canvas.c_x) &&
68 (evt.clientY >= Canvas.c_y) &&
69 (evt.clientX < (Canvas.c_x + Canvas.c_wx)) &&
70 (evt.clientY < (Canvas.c_y + Canvas.c_wy))) {
71 e.stop();
72 return false;
73 }
74 },
75
76
77 init: function (id, width, height, keyDown, keyUp,
78 mouseDown, mouseUp, mouseMove, mouseWheel) {
79 console.log(">> Canvas.init");
80
81 Canvas.id = id;
82
83 if (! keyDown) { keyDown = Canvas.keyDown; }
84 if (! keyUp) { keyUp = Canvas.keyUp; }
85 if (! mouseDown) { mouseDown = Canvas.mouseDown; }
86 if (! mouseUp) { mouseUp = Canvas.mouseUp; }
87 if (! mouseMove) { mouseMove = Canvas.mouseMove; }
88 if (! mouseWheel) { mouseWheel = Canvas.mouseWheel; }
89
90 var c = $(Canvas.id);
91 document.addEvent('keydown', keyDown);
92 document.addEvent('keyup', keyUp);
93 c.addEvent('mousedown', mouseDown);
94 c.addEvent('mouseup', mouseUp);
95 c.addEvent('mousemove', mouseMove);
96 c.addEvent('mousewheel', mouseWheel);
97
98 /* Work around right and middle click browser behaviors */
99 document.addEvent('click', Canvas.ctxDisable);
100 document.body.addEvent('contextmenu', Canvas.ctxDisable);
101
102 c.width = width;
103 c.height = height;
104 Canvas.c_x = c.getPosition().x;
105 Canvas.c_y = c.getPosition().y;
106 Canvas.c_wx = c.getSize().x;
107 Canvas.c_wy = c.getSize().y;
108
109 if (! c.getContext) { return; }
110 Canvas.ctx = c.getContext('2d');
111
112 Canvas.prevStyle = "";
113
114 if (Browser.Engine.webkit) {
115 Canvas.prefer_js = true;
116 }
117
118 console.log("<< Canvas.init");
119 },
120
121 clear: function () {
122 Canvas.ctx.clearRect(0, 0, Canvas.c_wx, Canvas.c_wy);
123 var c = $(Canvas.id);
124 c.width = 640;
125 c.height = 20;
126 },
127
128 stop: function () {
129 var c = $(Canvas.id);
130 document.removeEvents('keydown');
131 document.removeEvents('keyup');
132 c.removeEvents('mousedown');
133 c.removeEvents('mouseup');
134 c.removeEvents('mousemove');
135 c.removeEvents('DOMMouseScroll');
136
137 /* Work around right and middle click browser behaviors */
138 document.removeEvents('click');
139 document.body.removeEvents('contextmenu');
140 },
141
142 /*
143 * Tile rendering functions optimized for rendering engines.
144 *
145 * - In Chrome/webkit, Javascript image data array manipulations are
146 * faster than direct Canvas fillStyle, fillRect rendering. In
147 * gecko, Javascript array handling is much slower.
148 */
149 getTile: function(x, y, width, height, color) {
150 var img, data, p, red, green, blue, j, i;
151 img = {'x': x, 'y': y, 'width': width, 'height': height,
152 'data': []};
153 if (Canvas.prefer_js) {
154 data = img.data;
155 red = color[0];
156 green = color[1];
157 blue = color[2];
158 for (j = 0; j < height; j++) {
159 for (i = 0; i < width; i++) {
160 p = (i + (j * width) ) * 4;
161 img.data[p + 0] = red;
162 img.data[p + 1] = green;
163 img.data[p + 2] = blue;
164 //img.data[p + 3] = 255; // Set Alpha
165 }
166 }
167 } else {
168 Canvas.fillRect(x, y, width, height, color);
169 }
170 return img;
171 },
172
173 setTile: function(img, x, y, w, h, color) {
174 var data, p, red, green, blue, width, j, i;
175 if (Canvas.prefer_js) {
176 data = img.data;
177 width = img.width;
178 red = color[0];
179 green = color[1];
180 blue = color[2];
181 for (j = 0; j < h; j++) {
182 for (i = 0; i < w; i++) {
183 p = (x + i + ((y + j) * width) ) * 4;
184 data[p + 0] = red;
185 data[p + 1] = green;
186 data[p + 2] = blue;
187 //img.data[p + 3] = 255; // Set Alpha
188 }
189 }
190 } else {
191 Canvas.fillRect(img.x + x, img.y + y, w, h, color);
192 }
193 },
194
195 putTile: function(img) {
196 if (Canvas.prefer_js) {
197 Canvas.rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
198 //Canvas.ctx.putImageData(img, img.x, img.y);
199 } else {
200 // No-op, under gecko already done by setTile
201 }
202 },
203
204
205 rgbxImage: function(x, y, width, height, arr, offset) {
206 var img, i, j, data;
207 //console.log("rfbxImage: img: " + img + " x: " + x + " y: " + y + " width: " + width + " height: " + height);
208 /* Old firefox and Opera don't support createImageData */
209 img = Canvas.ctx.getImageData(0, 0, width, height);
210 data = img.data;
211 for (i=0; i < (width * height * 4); i=i+4) {
212 j=i+offset;
213 data[i + 0] = arr[j + 0];
214 data[i + 1] = arr[j + 1];
215 data[i + 2] = arr[j + 2];
216 data[i + 3] = 255; // Set Alpha
217 }
218 Canvas.ctx.putImageData(img, x, y);
219
220 },
221
222 fillRect: function(x, y, width, height, color) {
223 var newStyle = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
224 if (newStyle !== Canvas.prevStyle) {
225 Canvas.ctx.fillStyle = newStyle;
226 Canvas.prevStyle = newStyle;
227 }
228 Canvas.ctx.fillRect(x, y, width, height);
229 },
230
231 copyImage: function(old_x, old_y, new_x, new_y, width, height) {
232 Canvas.ctx.drawImage($(Canvas.id), old_x, old_y, width, height,
233 new_x, new_y, width, height);
234 },
235
236 /* Translate DOM key event to keysym value */
237 getKeysym: function(e) {
238 var evt, keysym;
239 evt = e.event || window.event;
240
241 /* Remap modifier and special keys */
242 switch ( evt.keyCode ) {
243 case 8 : keysym = 0xFF08; break; // BACKSPACE
244 case 9 : keysym = 0xFF09; break; // TAB
245 case 13 : keysym = 0xFF0D; break; // ENTER
246 case 27 : keysym = 0xFF1B; break; // ESCAPE
247 case 45 : keysym = 0xFF63; break; // INSERT
248 case 46 : keysym = 0xFFFF; break; // DELETE
249 case 36 : keysym = 0xFF50; break; // HOME
250 case 35 : keysym = 0xFF57; break; // END
251 case 33 : keysym = 0xFF55; break; // PAGE_UP
252 case 34 : keysym = 0xFF56; break; // PAGE_DOWN
253 case 37 : keysym = 0xFF51; break; // LEFT
254 case 38 : keysym = 0xFF52; break; // UP
255 case 39 : keysym = 0xFF53; break; // RIGHT
256 case 40 : keysym = 0xFF54; break; // DOWN
257 case 112 : keysym = 0xFFBE; break; // F1
258 case 113 : keysym = 0xFFBF; break; // F2
259 case 114 : keysym = 0xFFC0; break; // F3
260 case 115 : keysym = 0xFFC1; break; // F4
261 case 116 : keysym = 0xFFC2; break; // F5
262 case 117 : keysym = 0xFFC3; break; // F6
263 case 118 : keysym = 0xFFC4; break; // F7
264 case 119 : keysym = 0xFFC5; break; // F8
265 case 120 : keysym = 0xFFC6; break; // F9
266 case 121 : keysym = 0xFFC7; break; // F10
267 case 122 : keysym = 0xFFC8; break; // F11
268 case 123 : keysym = 0xFFC9; break; // F12
269 case 16 : keysym = 0xFFE1; break; // SHIFT
270 case 17 : keysym = 0xFFE3; break; // CONTROL
271 case 18 : keysym = 0xFFE7; break; // ALT
272 default : keysym = evt.keyCode; break;
273 }
274
275 /* Remap symbols */
276 switch (keysym) {
277 case 186 : keysym = 59; break; // ; (IE)
278 case 187 : keysym = 61; break; // = (IE)
279 case 188 : keysym = 44; break; // , (Mozilla, IE)
280 case 109 : // - (Mozilla)
281 if (Browser.Engine.gecko) {
282 keysym = 45; }
283 break;
284 case 189 : keysym = 45; break; // - (IE)
285 case 190 : keysym = 46; break; // . (Mozilla, IE)
286 case 191 : keysym = 47; break; // / (Mozilla, IE)
287 case 192 : keysym = 96; break; // ` (Mozilla, IE)
288 case 219 : keysym = 91; break; // [ (Mozilla, IE)
289 case 220 : keysym = 92; break; // \ (Mozilla, IE)
290 case 221 : keysym = 93; break; // ] (Mozilla, IE)
291 case 222 : keysym = 39; break; // ' (Mozilla, IE)
292 }
293
294 /* Remap shifted and unshifted keys */
295 if (!!evt.shiftKey) {
296 switch (keysym) {
297 case 48 : keysym = 41 ; break; // ) (shifted 0)
298 case 49 : keysym = 33 ; break; // ! (shifted 1)
299 case 50 : keysym = 64 ; break; // @ (shifted 2)
300 case 51 : keysym = 35 ; break; // # (shifted 3)
301 case 52 : keysym = 36 ; break; // $ (shifted 4)
302 case 53 : keysym = 37 ; break; // % (shifted 5)
303 case 54 : keysym = 94 ; break; // ^ (shifted 6)
304 case 55 : keysym = 38 ; break; // & (shifted 7)
305 case 56 : keysym = 42 ; break; // * (shifted 8)
306 case 57 : keysym = 40 ; break; // ( (shifted 9)
307
308 case 59 : keysym = 58 ; break; // : (shifted `)
309 case 61 : keysym = 43 ; break; // + (shifted ;)
310 case 44 : keysym = 60 ; break; // < (shifted ,)
311 case 45 : keysym = 95 ; break; // _ (shifted -)
312 case 46 : keysym = 62 ; break; // > (shifted .)
313 case 47 : keysym = 63 ; break; // ? (shifted /)
314 case 96 : keysym = 126; break; // ~ (shifted `)
315 case 91 : keysym = 123; break; // { (shifted [)
316 case 92 : keysym = 124; break; // | (shifted \)
317 case 93 : keysym = 125; break; // } (shifted ])
318 case 39 : keysym = 34 ; break; // " (shifted ')
319 }
320 } else if ((keysym >= 65) && (keysym <=90)) {
321 /* Remap unshifted A-Z */
322 keysym += 32;
323 }
324
325 return keysym;
326 }
327
328
329 };
330