websockify/include/canvas.js

421 lines
14 KiB
JavaScript
Raw Normal View History

2010-05-15 20:28:55 +01:00
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2010 Joel Martin
* Licensed under LGPL-3 (see LICENSE.LGPL-3)
2010-05-15 20:28:55 +01:00
*
* See README.md for usage and integration instructions.
*/
"use strict";
/*jslint white: false, bitwise: false */
/*global window, console, $, Util */
2010-05-15 20:28:55 +01:00
// Everything namespaced inside Canvas
var Canvas = {
prefer_js : false,
true_color : false,
colourMap : [],
c_wx : 0,
c_wy : 0,
ctx : null,
prevStyle: "",
focused : true,
keyPress : null,
mouseButton : null,
mouseMove : null,
onMouseButton: function(e, down) {
var evt, pos, bmask;
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, $(Canvas.id));
bmask = 1 << evt.button;
//console.log('mouse ' + pos.x + "," + pos.y + " down: " + down + " bmask: " + bmask);
if (Canvas.mouseButton) {
Canvas.mouseButton(pos.x, pos.y, down, bmask);
}
Util.stopEvent(e);
return false;
},
onMouseDown: function (e) {
Canvas.onMouseButton(e, 1);
},
onMouseUp: function (e) {
Canvas.onMouseButton(e, 0);
},
onMouseWheel: function (e) {
var evt, pos, bmask, wheelData;
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, $(Canvas.id));
wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40;
if (wheelData > 0) {
bmask = 1 << 3;
} else {
bmask = 1 << 4;
}
//console.log('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y);
if (Canvas.mouseButton) {
Canvas.mouseButton(pos.x, pos.y, 1, bmask);
Canvas.mouseButton(pos.x, pos.y, 0, bmask);
}
Util.stopEvent(e);
return false;
},
onMouseMove: function (e) {
var evt, pos;
evt = (e ? e : window.event);
pos = Util.getEventPosition(e, $(Canvas.id));
//console.log('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y);
if (Canvas.mouseMove) {
Canvas.mouseMove(pos.x, pos.y);
}
},
onKeyDown: function (e) {
//console.log("keydown: " + Canvas.getKeysym(e));
if (! Canvas.focused) {
return true;
}
if (Canvas.keyPress) {
Canvas.keyPress(Canvas.getKeysym(e), 1);
}
Util.stopEvent(e);
return false;
},
onKeyUp : function (e) {
//console.log("keyup: " + e.key + "(" + e.code + ")");
if (! Canvas.focused) {
return true;
}
if (Canvas.keyPress) {
Canvas.keyPress(Canvas.getKeysym(e), 0);
}
Util.stopEvent(e);
return false;
},
onMouseDisable: function (e) {
var evt, pos;
evt = (e ? e : window.event);
pos = Util.getPosition($(Canvas.id));
/* Stop propagation if inside canvas area */
if ((evt.clientX >= pos.x) &&
(evt.clientY >= pos.y) &&
(evt.clientX < (pos.x + Canvas.c_wx)) &&
(evt.clientY < (pos.y + Canvas.c_wy))) {
//console.log("mouse event disabled");
Util.stopEvent(e);
return false;
2010-05-15 20:28:55 +01:00
}
//console.log("mouse event not disabled");
return true;
},
init: function (id, width, height, true_color, keyPress,
mouseButton, mouseMove) {
console.log(">> Canvas.init");
Canvas.id = id;
Canvas.keyPress = keyPress || null;
Canvas.mouseButton = mouseButton || null;
Canvas.mouseMove = mouseMove || null;
var c = $(Canvas.id);
Util.addEvent(document, 'keydown', Canvas.onKeyDown);
Util.addEvent(document, 'keyup', Canvas.onKeyUp);
Util.addEvent(c, 'mousedown', Canvas.onMouseDown);
Util.addEvent(c, 'mouseup', Canvas.onMouseUp);
Util.addEvent(c, 'mousemove', Canvas.onMouseMove);
Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
Canvas.onMouseWheel);
/* Work around right and middle click browser behaviors */
Util.addEvent(document, 'click', Canvas.onMouseDisable);
Util.addEvent(document.body, 'contextmenu', Canvas.onMouseDisable);
Canvas.resize(width, height);
Canvas.c_wx = c.offsetWidth;
Canvas.c_wy = c.offsetHeight;
Canvas.true_color = true_color;
Canvas.colourMap = [];
2010-05-15 20:28:55 +01:00
if (! c.getContext) { return; }
Canvas.ctx = c.getContext('2d');
Canvas.prevStyle = "";
Canvas.focused = true;
if (Util.Engine.webkit) {
Canvas.prefer_js = true;
}
2010-06-14 20:42:34 +01:00
//console.log("<< Canvas.init");
},
clear: function () {
Canvas.ctx.clearRect(0, 0, Canvas.c_wx, Canvas.c_wy);
Canvas.resize(640, 20);
},
resize: function (width, height) {
2010-04-14 18:13:59 +01:00
var c = $(Canvas.id);
c.width = width;
c.height = height;
2010-04-14 18:13:59 +01:00
},
stop: function () {
var c = $(Canvas.id);
Util.removeEvent(document, 'keydown', Canvas.onKeyDown);
Util.removeEvent(document, 'keyup', Canvas.onKeyUp);
Util.removeEvent(c, 'mousedown', Canvas.onMouseDown);
Util.removeEvent(c, 'mouseup', Canvas.onMouseUp);
Util.removeEvent(c, 'mousemove', Canvas.onMouseMove);
Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel',
Canvas.onMouseWheel);
2010-04-13 15:39:29 +01:00
/* Work around right and middle click browser behaviors */
Util.removeEvent(document, 'click', Canvas.onMouseDisable);
Util.removeEvent(document.body, 'contextmenu', Canvas.onMouseDisable);
},
/*
* Tile rendering functions optimized for rendering engines.
*
* - In Chrome/webkit, Javascript image data array manipulations are
* faster than direct Canvas fillStyle, fillRect rendering. In
* gecko, Javascript array handling is much slower.
*/
getTile: function(x, y, width, height, color) {
var img, data, p, rgb, red, green, blue, j, i;
2010-05-15 20:28:55 +01:00
img = {'x': x, 'y': y, 'width': width, 'height': height,
'data': []};
if (Canvas.prefer_js) {
data = img.data;
if (Canvas.true_color) {
rgb = color;
} else {
rgb = Canvas.colourMap[color[0]];
}
red = rgb[0];
green = rgb[1];
blue = rgb[2];
for (j = 0; j < height; j += 1) {
for (i = 0; i < width; i += 1) {
p = (i + (j * width) ) * 4;
data[p + 0] = red;
data[p + 1] = green;
data[p + 2] = blue;
//data[p + 3] = 255; // Set Alpha
}
}
} else {
Canvas.fillRect(x, y, width, height, color);
}
return img;
},
setTile: function(img, x, y, w, h, color) {
var data, p, rgb, red, green, blue, width, j, i;
if (Canvas.prefer_js) {
data = img.data;
2010-05-15 20:28:55 +01:00
width = img.width;
if (Canvas.true_color) {
rgb = color;
} else {
rgb = Canvas.colourMap[color[0]];
}
red = rgb[0];
green = rgb[1];
blue = rgb[2];
for (j = 0; j < h; j += 1) {
for (i = 0; i < w; i += 1) {
p = (x + i + ((y + j) * width) ) * 4;
data[p + 0] = red;
data[p + 1] = green;
data[p + 2] = blue;
//img.data[p + 3] = 255; // Set Alpha
}
}
} else {
Canvas.fillRect(img.x + x, img.y + y, w, h, color);
}
},
putTile: function(img) {
if (Canvas.prefer_js) {
Canvas.rgbxImage(img.x, img.y, img.width, img.height, img.data, 0);
//Canvas.ctx.putImageData(img, img.x, img.y);
} else {
// No-op, under gecko already done by setTile
}
},
rgbxImage: function(x, y, width, height, arr, offset) {
var img, i, j, data;
//console.log("rfbxImage: img: " + img + " x: " + x + " y: " + y + " width: " + width + " height: " + height);
/* Old firefox and Opera don't support createImageData */
2010-05-15 20:28:55 +01:00
img = Canvas.ctx.getImageData(0, 0, width, height);
data = img.data;
for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) {
data[i + 0] = arr[j + 0];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j + 2];
data[i + 3] = 255; // Set Alpha
}
Canvas.ctx.putImageData(img, x, y);
},
cmapImage: function(x, y, width, height, arr, offset) {
var img, i, j, data, rgb, cmap;
img = Canvas.ctx.getImageData(0, 0, width, height);
data = img.data;
cmap = Canvas.colourMap;
//console.log("cmapImage x: " + x + ", y: " + y + "arr.slice(0,20): " + arr.slice(0,20));
for (i=0, j=offset; i < (width * height * 4); i=i+4, j += 1) {
rgb = cmap[arr[j]];
data[i + 0] = rgb[0];
data[i + 1] = rgb[1];
data[i + 2] = rgb[2];
data[i + 3] = 255; // Set Alpha
}
Canvas.ctx.putImageData(img, x, y);
},
blitImage: function(x, y, width, height, arr, offset) {
if (Canvas.true_color) {
Canvas.rgbxImage(x, y, width, height, arr, offset);
} else {
Canvas.cmapImage(x, y, width, height, arr, offset);
}
},
fillRect: function(x, y, width, height, color) {
var rgb, newStyle;
if (Canvas.true_color) {
rgb = color;
} else {
rgb = Canvas.colourMap[color[0]];
}
2010-05-15 20:28:55 +01:00
if (newStyle !== Canvas.prevStyle) {
newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
Canvas.ctx.fillStyle = newStyle;
2010-05-15 20:28:55 +01:00
Canvas.prevStyle = newStyle;
}
2010-04-12 05:11:21 +01:00
Canvas.ctx.fillRect(x, y, width, height);
},
2010-04-07 04:44:12 +01:00
copyImage: function(old_x, old_y, new_x, new_y, width, height) {
Canvas.ctx.drawImage($(Canvas.id), old_x, old_y, width, height,
new_x, new_y, width, height);
},
/* Translate DOM key event to keysym value */
getKeysym: function(e) {
2010-05-15 20:28:55 +01:00
var evt, keysym;
evt = (e ? e : window.event);
/* Remap modifier and special keys */
switch ( evt.keyCode ) {
case 8 : keysym = 0xFF08; break; // BACKSPACE
case 9 : keysym = 0xFF09; break; // TAB
case 13 : keysym = 0xFF0D; break; // ENTER
case 27 : keysym = 0xFF1B; break; // ESCAPE
case 45 : keysym = 0xFF63; break; // INSERT
case 46 : keysym = 0xFFFF; break; // DELETE
case 36 : keysym = 0xFF50; break; // HOME
case 35 : keysym = 0xFF57; break; // END
case 33 : keysym = 0xFF55; break; // PAGE_UP
case 34 : keysym = 0xFF56; break; // PAGE_DOWN
case 37 : keysym = 0xFF51; break; // LEFT
case 38 : keysym = 0xFF52; break; // UP
case 39 : keysym = 0xFF53; break; // RIGHT
case 40 : keysym = 0xFF54; break; // DOWN
case 112 : keysym = 0xFFBE; break; // F1
case 113 : keysym = 0xFFBF; break; // F2
case 114 : keysym = 0xFFC0; break; // F3
case 115 : keysym = 0xFFC1; break; // F4
case 116 : keysym = 0xFFC2; break; // F5
case 117 : keysym = 0xFFC3; break; // F6
case 118 : keysym = 0xFFC4; break; // F7
case 119 : keysym = 0xFFC5; break; // F8
case 120 : keysym = 0xFFC6; break; // F9
case 121 : keysym = 0xFFC7; break; // F10
case 122 : keysym = 0xFFC8; break; // F11
case 123 : keysym = 0xFFC9; break; // F12
case 16 : keysym = 0xFFE1; break; // SHIFT
case 17 : keysym = 0xFFE3; break; // CONTROL
//case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option)
case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command)
default : keysym = evt.keyCode; break;
}
/* Remap symbols */
switch (keysym) {
case 186 : keysym = 59; break; // ; (IE)
case 187 : keysym = 61; break; // = (IE)
case 188 : keysym = 44; break; // , (Mozilla, IE)
2010-04-18 22:19:10 +01:00
case 109 : // - (Mozilla)
if (Util.Engine.gecko) {
2010-05-15 20:28:55 +01:00
keysym = 45; }
break;
case 189 : keysym = 45; break; // - (IE)
case 190 : keysym = 46; break; // . (Mozilla, IE)
case 191 : keysym = 47; break; // / (Mozilla, IE)
case 192 : keysym = 96; break; // ` (Mozilla, IE)
case 219 : keysym = 91; break; // [ (Mozilla, IE)
case 220 : keysym = 92; break; // \ (Mozilla, IE)
case 221 : keysym = 93; break; // ] (Mozilla, IE)
case 222 : keysym = 39; break; // ' (Mozilla, IE)
}
/* Remap shifted and unshifted keys */
if (!!evt.shiftKey) {
switch (keysym) {
case 48 : keysym = 41 ; break; // ) (shifted 0)
case 49 : keysym = 33 ; break; // ! (shifted 1)
case 50 : keysym = 64 ; break; // @ (shifted 2)
case 51 : keysym = 35 ; break; // # (shifted 3)
case 52 : keysym = 36 ; break; // $ (shifted 4)
case 53 : keysym = 37 ; break; // % (shifted 5)
case 54 : keysym = 94 ; break; // ^ (shifted 6)
case 55 : keysym = 38 ; break; // & (shifted 7)
case 56 : keysym = 42 ; break; // * (shifted 8)
case 57 : keysym = 40 ; break; // ( (shifted 9)
case 59 : keysym = 58 ; break; // : (shifted `)
case 61 : keysym = 43 ; break; // + (shifted ;)
case 44 : keysym = 60 ; break; // < (shifted ,)
case 45 : keysym = 95 ; break; // _ (shifted -)
case 46 : keysym = 62 ; break; // > (shifted .)
case 47 : keysym = 63 ; break; // ? (shifted /)
case 96 : keysym = 126; break; // ~ (shifted `)
case 91 : keysym = 123; break; // { (shifted [)
case 92 : keysym = 124; break; // | (shifted \)
case 93 : keysym = 125; break; // } (shifted ])
case 39 : keysym = 34 ; break; // " (shifted ')
}
} else if ((keysym >= 65) && (keysym <=90)) {
/* Remap unshifted A-Z */
keysym += 32;
}
return keysym;
}
};