[Spice-devel] [spice-html5 3/3] Provide two levels of workaround for IE 10 cursor support.
Jeremy White
jwhite at codeweavers.com
Fri Apr 19 11:13:44 PDT 2013
The first approach is to use a moving image to simulate the cursor.
This does not work well, so a second approach, involving static
cursors is provided. That requires a site administrator to precreate
all the cursors that can be used by a particular application set.
A warning message strongly suggests using a different browser as well.
Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
cursor.js | 6 +-
display.js | 2 +
inputs.js | 9 +++
simulatecursor.js | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++
spice.html | 1 +
spice_auto.html | 1 +
6 files changed, 220 insertions(+), 1 deletion(-)
create mode 100644 simulatecursor.js
diff --git a/cursor.js b/cursor.js
index e3f6e0e..cafdf65 100644
--- a/cursor.js
+++ b/cursor.js
@@ -88,5 +88,9 @@ SpiceCursorConn.prototype.set_cursor = function(cursor)
var pngstr = create_rgba_png(cursor.header.height, cursor.header.width, cursor.data);
var curstr = 'url(data:image/png,' + pngstr + ') ' +
cursor.header.hot_spot_x + ' ' + cursor.header.hot_spot_y + ", default";
- document.getElementById(this.parent.screen_id).style.cursor = curstr;
+ var screen = document.getElementById(this.parent.screen_id);
+ screen.style.cursor = 'auto';
+ screen.style.cursor = curstr;
+ if (window.getComputedStyle(screen, null).cursor == 'auto')
+ SpiceSimulateCursor.simulate_cursor(this, cursor, screen, pngstr);
}
diff --git a/display.js b/display.js
index 9f1e3dd..f9bfe76 100644
--- a/display.js
+++ b/display.js
@@ -721,6 +721,8 @@ function handle_mouseover(e)
function handle_mouseout(e)
{
+ if (this.sc && this.sc.cursor && this.sc.cursor.spice_simulated_cursor)
+ this.sc.cursor.spice_simulated_cursor.style.display = 'none';
this.blur();
}
diff --git a/inputs.js b/inputs.js
index 1131d09..c904eda 100644
--- a/inputs.js
+++ b/inputs.js
@@ -98,6 +98,15 @@ function handle_mousemove(e)
DEBUG > 0 && this.sc.log_info("Discarding mouse motion");
}
}
+
+ if (this.sc && this.sc.cursor && this.sc.cursor.spice_simulated_cursor)
+ {
+ this.sc.cursor.spice_simulated_cursor.style.display = 'block';
+ this.sc.cursor.spice_simulated_cursor.style.left = e.pageX - this.sc.cursor.spice_simulated_cursor.spice_hot_x + 'px';
+ this.sc.cursor.spice_simulated_cursor.style.top = e.pageY - this.sc.cursor.spice_simulated_cursor.spice_hot_y + 'px';
+ e.preventDefault();
+ }
+
}
function handle_mousedown(e)
diff --git a/simulatecursor.js b/simulatecursor.js
new file mode 100644
index 0000000..b1fce06
--- /dev/null
+++ b/simulatecursor.js
@@ -0,0 +1,202 @@
+"use strict";
+/*
+ Copyright (C) 2013 by Jeremy P. White <jwhite at codeweavers.com>
+
+ This file is part of spice-html5.
+
+ spice-html5 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ spice-html5 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*----------------------------------------------------------------------------
+** SpiceSimulateCursor
+** Internet Explorer 10 does not support data uri's in cursor assignment.
+** This file provides a number of gimmicks to compensate. First, if there
+** is a preloaded cursor available, we will use that. Failing that, we will
+** simulate a cursor using an image that is moved around the screen.
+**--------------------------------------------------------------------------*/
+var SpiceSimulateCursor = {
+
+cursors : new Array(),
+unknown_cursors : new Array(),
+warned: false,
+
+add_cursor: function(sha1, value)
+{
+ SpiceSimulateCursor.cursors[sha1] = value;
+},
+
+unknown_cursor: function(sha1, curdata)
+{
+ if (! SpiceSimulateCursor.warned)
+ {
+ SpiceSimulateCursor.warned = true;
+ alert("Internet Explorer does not support dynamic cursors. " +
+ "This page will now simulate cursors with images, " +
+ "which will be imperfect. We recommend using Chrome or Firefox instead. " +
+ "\n\nIf you need to use Internet Explorer, you can create a static cursor " +
+ "file for each cursor your application uses. " +
+ "View the console log for more information on creating static cursors for your environment.");
+ }
+
+ if (! SpiceSimulateCursor.unknown_cursors[sha1])
+ {
+ SpiceSimulateCursor.unknown_cursors[sha1] = curdata;
+ console.log('Unknown cursor. Simulation required. To avoid simulation for this cursor, create and include a custom javascript file, and add the following line:');
+ console.log('SpiceCursorSimulator.add_cursor("' + sha1 + '"), "<your filename here>.cur");');
+ console.log('And then run following command, redirecting output into <your filename here>.cur:');
+ console.log('php -r "echo urldecode(\'' + curdata + '\');"');
+ }
+},
+
+simulate_cursor: function (spicecursor, cursor, screen, pngstr)
+{
+ var cursor_sha = hex_sha1(pngstr + ' ' + cursor.header.hot_spot_x + ' ' + cursor.header.hot_spot_y);
+ if (typeof SpiceSimulateCursor.cursors != 'undefined')
+ if (typeof SpiceSimulateCursor.cursors[cursor_sha] != 'undefined')
+ {
+ var curstr = 'url(' + SpiceSimulateCursor.cursors[cursor_sha] + '), default';
+ screen.style.cursor = curstr;
+ }
+
+ if (window.getComputedStyle(screen, null).cursor == 'auto')
+ {
+ SpiceSimulateCursor.unknown_cursor(cursor_sha,
+ SpiceSimulateCursor.create_icondir(cursor.header.width, cursor.header.height,
+ cursor.data.byteLength, cursor.header.hot_spot_x, cursor.header.hot_spot_y) + pngstr);
+
+ document.getElementById(spicecursor.parent.screen_id).style.cursor = 'none';
+ if (! spicecursor.spice_simulated_cursor)
+ {
+ spicecursor.spice_simulated_cursor = document.createElement('img');
+
+ spicecursor.spice_simulated_cursor.style.position = 'absolute';
+ spicecursor.spice_simulated_cursor.style.display = 'none';
+ spicecursor.spice_simulated_cursor.style.overflow = 'hidden';
+
+ spicecursor.spice_simulated_cursor.spice_screen = document.getElementById(spicecursor.parent.screen_id);
+
+ spicecursor.spice_simulated_cursor.addEventListener('mousemove', SpiceSimulateCursor.handle_sim_mousemove);
+
+ spicecursor.spice_simulated_cursor.spice_screen.appendChild(spicecursor.spice_simulated_cursor);
+ }
+
+ spicecursor.spice_simulated_cursor.src = 'data:image/png,' + pngstr;
+
+ spicecursor.spice_simulated_cursor.spice_hot_x = cursor.header.hot_spot_x;
+ spicecursor.spice_simulated_cursor.spice_hot_y = cursor.header.hot_spot_y;
+
+ spicecursor.spice_simulated_cursor.style.pointerEvents = "none";
+ }
+ else
+ {
+ if (spicecursor.spice_simulated_cursor)
+ {
+ spicecursor.spice_simulated_cursor.spice_screen.removeChild(spicecursor.spice_simulated_cursor);
+ delete spicecursor.spice_simulated_cursor;
+ }
+ }
+},
+
+handle_sim_mousemove: function(e)
+{
+ var retval;
+ var f = SpiceSimulateCursor.duplicate_mouse_event(e, this.spice_screen);
+ return this.spice_screen.dispatchEvent(f);
+},
+
+duplicate_mouse_event: function(e, target)
+{
+ var evt = document.createEvent("mouseevent");
+ evt.initMouseEvent(e.type, true, true, e.view, e.detail,
+ e.screenX, e.screenY, e.clientX, e.clientY,
+ e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget);
+ return evt;
+},
+
+ICONDIR: function ()
+{
+},
+
+ICONDIRENTRY: function(width, height, bytes, hot_x, hot_y)
+{
+ this.width = width;
+ this.height = height;
+ this.bytes = bytes;
+ this.hot_x = hot_x;
+ this.hot_y = hot_y;
+},
+
+
+create_icondir: function (width, height, bytes, hot_x, hot_y)
+{
+ var i;
+ var header = new SpiceSimulateCursor.ICONDIR();
+ var entry = new SpiceSimulateCursor.ICONDIRENTRY(width, height, bytes, hot_x, hot_y);
+
+ var mb = new ArrayBuffer(header.buffer_size() + entry.buffer_size());
+ var at = header.to_buffer(mb);
+ at = entry.to_buffer(mb, at);
+
+ var u8 = new Uint8Array(mb);
+ var str = "";
+ for (i = 0; i < at; i++)
+ {
+ str += "%";
+ if (u8[i] < 16)
+ str += "0";
+ str += u8[i].toString(16);
+ }
+ return str;
+},
+
+};
+
+SpiceSimulateCursor.ICONDIR.prototype =
+{
+ to_buffer: function(a, at)
+ {
+ at = at || 0;
+ var dv = new SpiceDataView(a);
+ dv.setUint16(at, 0, true); at += 2;
+ dv.setUint16(at, 2, true); at += 2;
+ dv.setUint16(at, 1, true); at += 2;
+ return at;
+ },
+ buffer_size: function()
+ {
+ return 6;
+ }
+};
+
+SpiceSimulateCursor.ICONDIRENTRY.prototype =
+{
+ to_buffer: function(a, at)
+ {
+ at = at || 0;
+ var dv = new SpiceDataView(a);
+ dv.setUint8(at, this.width); at++;
+ dv.setUint8(at, this.height); at++;
+ dv.setUint8(at, 0); at++; /* color palette count, unused */
+ dv.setUint8(at, 0); at++; /* reserved */
+ dv.setUint16(at, this.hot_x, true); at += 2;
+ dv.setUint16(at, this.hot_y, true); at += 2;
+ dv.setUint32(at, this.bytes, true); at += 4;
+ dv.setUint32(at, at + 4, true); at += 4; /* Offset to bytes */
+ return at;
+ },
+ buffer_size: function()
+ {
+ return 16;
+ }
+};
diff --git a/spice.html b/spice.html
index f20b585..0f705fd 100644
--- a/spice.html
+++ b/spice.html
@@ -44,6 +44,7 @@
<script src="display.js"></script>
<script src="main.js"></script>
<script src="inputs.js"></script>
+ <script src="simulatecursor.js"></script>
<script src="cursor.js"></script>
<script src="thirdparty/jsbn.js"></script>
<script src="thirdparty/rsa.js"></script>
diff --git a/spice_auto.html b/spice_auto.html
index f51f96c..81ec8cf 100644
--- a/spice_auto.html
+++ b/spice_auto.html
@@ -44,6 +44,7 @@
<script src="display.js"></script>
<script src="main.js"></script>
<script src="inputs.js"></script>
+ <script src="simulatecursor.js"></script>
<script src="cursor.js"></script>
<script src="thirdparty/jsbn.js"></script>
<script src="thirdparty/rsa.js"></script>
--
1.7.10.4
More information about the Spice-devel
mailing list