Add Cursor pseudo-encoding support (disabled for now).
To change the appearance of the cursor, we use the CSS cursor style and set the url to a data URI scheme. The image data sent via the cursor pseudo-encoding has to be encoded to a CUR format file before being used in the data URI. During Canvas initialization we try and set a simple cursor to see if the browser has support. Opera is missing support for data URI scheme in cursor URLs. Disabled for now until we have a better way of specifying settings overall (too many settings for control bar now).
This commit is contained in:
@@ -178,7 +178,9 @@ In the following table Jaunty is Ubuntu 9.04 and WinXP is Windows XP.
|
|||||||
is faster than Firefox 3.5, the high variability of web-socket-js
|
is faster than Firefox 3.5, the high variability of web-socket-js
|
||||||
performance results in overall performance being lower. Middle mouse
|
performance results in overall performance being lower. Middle mouse
|
||||||
clicks and keyboard events need some work to work properly under
|
clicks and keyboard events need some work to work properly under
|
||||||
Opera.
|
Opera. Also, Opera does not have support for setting the cursor
|
||||||
|
style url to a data URI scheme, so cursor pseudo-encoding is
|
||||||
|
disabled.
|
||||||
|
|
||||||
|
|
||||||
### Integration
|
### Integration
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
Short Term:
|
Short Term:
|
||||||
|
|
||||||
|
- Proper Javascript namespacing for Canvas and RFB (using function for
|
||||||
|
true local variables and functions).
|
||||||
|
|
||||||
- Timing delta between frames in proxy record log, for playback
|
- Timing delta between frames in proxy record log, for playback
|
||||||
support (for demo and test).
|
support (for demo and test).
|
||||||
|
|
||||||
@@ -14,10 +17,6 @@ Short Term:
|
|||||||
|
|
||||||
Medium Term:
|
Medium Term:
|
||||||
|
|
||||||
- Implement Cursor pseudo-encoding (CSS cursor)
|
|
||||||
http://en.wikipedia.org/wiki/ICO_(file_format)
|
|
||||||
https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
|
|
||||||
|
|
||||||
- Viewport and/or scaling support.
|
- Viewport and/or scaling support.
|
||||||
|
|
||||||
- Status bar buttons:
|
- Status bar buttons:
|
||||||
|
@@ -29,6 +29,11 @@ TLS Protocol:
|
|||||||
Generate self-signed certificate:
|
Generate self-signed certificate:
|
||||||
http://docs.python.org/dev/library/ssl.html#certificates
|
http://docs.python.org/dev/library/ssl.html#certificates
|
||||||
|
|
||||||
|
Cursor appearance/style (for Cursor pseudo-encoding):
|
||||||
|
http://en.wikipedia.org/wiki/ICO_(file_format)
|
||||||
|
http://www.daubnet.com/en/file-format-cur
|
||||||
|
https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
|
||||||
|
|
||||||
|
|
||||||
Related projects:
|
Related projects:
|
||||||
|
|
||||||
|
@@ -27,8 +27,9 @@ var Canvas, Canvas_native;
|
|||||||
// Everything namespaced inside Canvas
|
// Everything namespaced inside Canvas
|
||||||
Canvas = {
|
Canvas = {
|
||||||
|
|
||||||
prefer_js : false,
|
prefer_js : false, // make private
|
||||||
force_canvas : false,
|
force_canvas : false, // make private
|
||||||
|
cursor_uri : true, // make private, create getter
|
||||||
|
|
||||||
true_color : false,
|
true_color : false,
|
||||||
colourMap : [],
|
colourMap : [],
|
||||||
@@ -138,7 +139,7 @@ onMouseDisable: function (e) {
|
|||||||
|
|
||||||
|
|
||||||
init: function (id) {
|
init: function (id) {
|
||||||
var c, imgTest, arora;
|
var c, imgTest, tval, i, curTest, curSave;
|
||||||
Util.Debug(">> Canvas.init");
|
Util.Debug(">> Canvas.init");
|
||||||
|
|
||||||
Canvas.id = id;
|
Canvas.id = id;
|
||||||
@@ -198,6 +199,25 @@ init: function (id) {
|
|||||||
Canvas._cmapImage = Canvas._cmapImageFill;
|
Canvas._cmapImage = Canvas._cmapImageFill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine browser support for setting the cursor via data URI
|
||||||
|
* scheme
|
||||||
|
*/
|
||||||
|
curDat = [];
|
||||||
|
for (i=0; i < 8 * 8 * 4; i++) {
|
||||||
|
curDat.push(255);
|
||||||
|
}
|
||||||
|
curSave = c.style.cursor;
|
||||||
|
Canvas.setCursor(curDat, curDat, 2, 2, 8, 8);
|
||||||
|
if (c.style.cursor) {
|
||||||
|
Util.Info("Data URI scheme cursor supported");
|
||||||
|
} else {
|
||||||
|
Canvas.cursor_uri = false;
|
||||||
|
Util.Warn("Data URI scheme cursor not supported");
|
||||||
|
}
|
||||||
|
c.style.cursor = curSave;
|
||||||
|
|
||||||
|
|
||||||
Canvas.colourMap = [];
|
Canvas.colourMap = [];
|
||||||
Canvas.prevStyle = "";
|
Canvas.prevStyle = "";
|
||||||
Canvas.focused = true;
|
Canvas.focused = true;
|
||||||
@@ -263,6 +283,11 @@ stop: function () {
|
|||||||
/* Work around right and middle click browser behaviors */
|
/* Work around right and middle click browser behaviors */
|
||||||
Util.removeEvent(document, 'click', Canvas.onMouseDisable);
|
Util.removeEvent(document, 'click', Canvas.onMouseDisable);
|
||||||
Util.removeEvent(document.body, 'contextmenu', Canvas.onMouseDisable);
|
Util.removeEvent(document.body, 'contextmenu', Canvas.onMouseDisable);
|
||||||
|
|
||||||
|
// Turn off cursor rendering
|
||||||
|
if (Canvas.cursor_uri) {
|
||||||
|
c.style.cursor = "default";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -530,8 +555,89 @@ getKeysym: function(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return keysym;
|
return keysym;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
|
||||||
|
isCursor: function() {
|
||||||
|
return Canvas.cursor_uri;
|
||||||
|
},
|
||||||
|
|
||||||
|
setCursor: function(pixels, mask, hotx, hoty, w, h) {
|
||||||
|
var cur = [], cmap, IHDRsz, ANDsz, XORsz, url, idx, x, y;
|
||||||
|
//Util.Debug(">> setCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h);
|
||||||
|
|
||||||
|
if (!Canvas.cursor_uri) {
|
||||||
|
Util.Warn("setCursor called but no cursor data URI support");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmap = Canvas.colourMap;
|
||||||
|
IHDRsz = 40;
|
||||||
|
ANDsz = w * h * 4;
|
||||||
|
XORsz = Math.ceil( (w * h) / 8.0 );
|
||||||
|
|
||||||
|
// Main header
|
||||||
|
cur.push16le(0); // Reserved
|
||||||
|
cur.push16le(2); // .CUR type
|
||||||
|
cur.push16le(1); // Number of images, 1 for non-animated ico
|
||||||
|
|
||||||
|
// Cursor #1 header
|
||||||
|
cur.push(w); // width
|
||||||
|
cur.push(h); // height
|
||||||
|
cur.push(0); // colors, 0 -> true-color
|
||||||
|
cur.push(0); // reserved
|
||||||
|
cur.push16le(hotx); // hotspot x coordinate
|
||||||
|
cur.push16le(hoty); // hotspot y coordinate
|
||||||
|
cur.push32le(IHDRsz + XORsz + ANDsz); // cursor data byte size
|
||||||
|
cur.push32le(22); // offset of cursor data in the file
|
||||||
|
|
||||||
|
// Cursor #1 InfoHeader
|
||||||
|
cur.push32le(IHDRsz); // Infoheader size
|
||||||
|
cur.push32le(w); // Cursor width
|
||||||
|
cur.push32le(h*2); // XOR+AND height
|
||||||
|
cur.push16le(1); // number of planes
|
||||||
|
cur.push16le(32); // bits per pixel
|
||||||
|
cur.push32le(0); // Type of compression
|
||||||
|
cur.push32le(XORsz + ANDsz); // Size of Image
|
||||||
|
cur.push32le(0);
|
||||||
|
cur.push32le(0);
|
||||||
|
cur.push32le(0);
|
||||||
|
cur.push32le(0);
|
||||||
|
|
||||||
|
// XOR/color data
|
||||||
|
for (y = h-1; y >= 0; y--) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
idx = y * Math.ceil(w / 8) + Math.floor(x/8);
|
||||||
|
alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
|
||||||
|
|
||||||
|
if (Canvas.true_color) {
|
||||||
|
idx = ((w * y) + x) * 4;
|
||||||
|
cur.push(pixels[idx + 2]); // blue
|
||||||
|
cur.push(pixels[idx + 1]); // green
|
||||||
|
cur.push(pixels[idx + 0]); // red
|
||||||
|
cur.push(alpha); // red
|
||||||
|
} else {
|
||||||
|
idx = (w * y) + x;
|
||||||
|
rgb = cmap[pixels[idx]];
|
||||||
|
cur.push(rgb[2]); // blue
|
||||||
|
cur.push(rgb[1]); // green
|
||||||
|
cur.push(rgb[0]); // red
|
||||||
|
cur.push(alpha); // alpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AND/bitmask data (ignored, just needs to be right size)
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < Math.ceil(w / 8); x++) {
|
||||||
|
cur.push(0x00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
url = "data:image/x-icon;base64," + Base64.encode(cur);
|
||||||
|
$(Canvas.id).style.cursor = "url(" + url + ") " + hotx + " " + hoty + ", default";
|
||||||
|
//Util.Debug("<< setCursor, cur.length: " + cur.length);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -77,6 +77,10 @@ Array.prototype.push16 = function (num) {
|
|||||||
this.push((num >> 8) & 0xFF,
|
this.push((num >> 8) & 0xFF,
|
||||||
(num ) & 0xFF );
|
(num ) & 0xFF );
|
||||||
};
|
};
|
||||||
|
Array.prototype.push16le = function (num) {
|
||||||
|
this.push((num ) & 0xFF,
|
||||||
|
(num >> 8) & 0xFF );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Array.prototype.shift32 = function () {
|
Array.prototype.shift32 = function () {
|
||||||
@@ -97,6 +101,13 @@ Array.prototype.push32 = function (num) {
|
|||||||
(num >> 8) & 0xFF,
|
(num >> 8) & 0xFF,
|
||||||
(num ) & 0xFF );
|
(num ) & 0xFF );
|
||||||
};
|
};
|
||||||
|
Array.prototype.push32le = function (num) {
|
||||||
|
this.push((num ) & 0xFF,
|
||||||
|
(num >> 8) & 0xFF,
|
||||||
|
(num >> 16) & 0xFF,
|
||||||
|
(num >> 24) & 0xFF );
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Array.prototype.shiftStr = function (len) {
|
Array.prototype.shiftStr = function (len) {
|
||||||
var arr = this.splice(0, len);
|
var arr = this.splice(0, len);
|
||||||
|
@@ -85,6 +85,10 @@ encodings : [
|
|||||||
// ['compress_hi', -247, 'set_compress_level']
|
// ['compress_hi', -247, 'set_compress_level']
|
||||||
],
|
],
|
||||||
|
|
||||||
|
encodingCursor :
|
||||||
|
['Cursor', -239, 'set_cursor'],
|
||||||
|
|
||||||
|
|
||||||
setUpdateState: function(externalUpdateState) {
|
setUpdateState: function(externalUpdateState) {
|
||||||
RFB.externalUpdateState = externalUpdateState;
|
RFB.externalUpdateState = externalUpdateState;
|
||||||
},
|
},
|
||||||
@@ -145,6 +149,14 @@ load: function () {
|
|||||||
RFB.updateState('fatal', "No working Canvas");
|
RFB.updateState('fatal', "No working Canvas");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Cursor pseudo-encoding if supported
|
||||||
|
/*
|
||||||
|
if (Canvas.isCursor()) {
|
||||||
|
Util.Debug("Adding Cursor pseudo-encoding to encoding list");
|
||||||
|
RFB.encodings.push(RFB.encodingCursor);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Populate encoding lookup tables
|
// Populate encoding lookup tables
|
||||||
RFB.encHandlers = {};
|
RFB.encHandlers = {};
|
||||||
RFB.encNames = {};
|
RFB.encNames = {};
|
||||||
@@ -1013,10 +1025,40 @@ set_desktopsize : function () {
|
|||||||
RFB.timing.fbu_rt_start = (new Date()).getTime();
|
RFB.timing.fbu_rt_start = (new Date()).getTime();
|
||||||
// Send a new non-incremental request
|
// Send a new non-incremental request
|
||||||
RFB.send_array(RFB.fbUpdateRequest(0));
|
RFB.send_array(RFB.fbUpdateRequest(0));
|
||||||
Util.Debug("<< set_desktopsize");
|
|
||||||
|
|
||||||
RFB.FBU.bytes = 0;
|
RFB.FBU.bytes = 0;
|
||||||
RFB.FBU.rects -= 1;
|
RFB.FBU.rects -= 1;
|
||||||
|
|
||||||
|
Util.Debug("<< set_desktopsize");
|
||||||
|
},
|
||||||
|
|
||||||
|
set_cursor: function () {
|
||||||
|
var x, y, w, h, pixelslength, masklength;
|
||||||
|
//Util.Debug(">> set_cursor");
|
||||||
|
x = RFB.FBU.x; // hotspot-x
|
||||||
|
y = RFB.FBU.y; // hotspot-y
|
||||||
|
w = RFB.FBU.width;
|
||||||
|
h = RFB.FBU.height;
|
||||||
|
|
||||||
|
pixelslength = w * h * RFB.fb_Bpp;
|
||||||
|
masklength = Math.floor((w + 7) / 8) * h;
|
||||||
|
|
||||||
|
if (RFB.RQ.length < (pixelslength + masklength)) {
|
||||||
|
//Util.Debug("waiting for cursor encoding bytes");
|
||||||
|
RFB.FBU.bytes = pixelslength + masklength;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
|
||||||
|
|
||||||
|
Canvas.setCursor(RFB.RQ.shiftBytes(pixelslength),
|
||||||
|
RFB.RQ.shiftBytes(masklength),
|
||||||
|
x, y, w, h);
|
||||||
|
|
||||||
|
RFB.FBU.bytes = 0;
|
||||||
|
RFB.FBU.rects -= 1;
|
||||||
|
|
||||||
|
//Util.Debug("<< set_cursor");
|
||||||
},
|
},
|
||||||
|
|
||||||
set_jpeg_quality : function () {
|
set_jpeg_quality : function () {
|
||||||
|
Reference in New Issue
Block a user