[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