[Spice-devel] [PATCH spice-html5 v2] If an agent is attached, enable dynamic resizing of the guest screen.

Jeremy White jwhite at codeweavers.com
Sat Sep 6 08:10:07 PDT 2014


This version shifts the window resizing code to the front end logic.

Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
 cursor.js       |   14 ++++++++++
 display.js      |    5 ++--
 enums.js        |   24 ++++++++++++++++-
 main.js         |   76 +++++++++++++++++++++++++++++++++++++++++++++---------
 resize.js       |   70 ++++++++++++++++++++++++++++++++++++++++++++++++++
 spice.css       |   28 ++++++++++----------
 spice.html      |   12 ++++++++-
 spice_auto.html |   11 +++++++-
 spiceconn.js    |    2 ++
 spicemsg.js     |   77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 289 insertions(+), 30 deletions(-)
 create mode 100644 resize.js

diff --git a/cursor.js b/cursor.js
index cafdf65..71e941d 100644
--- a/cursor.js
+++ b/cursor.js
@@ -80,6 +80,20 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
         return true;
     }
 
+    if (msg.type == SPICE_MSG_CURSOR_RESET)
+    {
+        DEBUG > 1 && console.log("SpiceMsgCursorReset");
+        document.getElementById(this.parent.screen_id).style.cursor = "auto";
+        return true;
+    }
+
+    if (msg.type == SPICE_MSG_CURSOR_INVAL_ALL)
+    {
+        DEBUG > 1 && console.log("SpiceMsgCursorInvalAll");
+        // FIXME - There may be something useful to do here...
+        return true;
+    }
+
     return false;
 }
 
diff --git a/display.js b/display.js
index 6d7dafc..2aa5985 100644
--- a/display.js
+++ b/display.js
@@ -446,8 +446,9 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
             /* This .save() is done entirely to enable SPICE_MSG_DISPLAY_RESET */
             canvas.context.save();
             document.getElementById(this.parent.screen_id).appendChild(canvas);
-            document.getElementById(this.parent.screen_id).setAttribute('width', m.surface.width);
-            document.getElementById(this.parent.screen_id).setAttribute('height', m.surface.height);
+
+            /* We're going to leave width dynamic, but correctly set the height */
+            document.getElementById(this.parent.screen_id).style.height = m.surface.height + "px";
             this.hook_events();
         }
         return true;
diff --git a/enums.js b/enums.js
index 7d41226..d99b38e 100644
--- a/enums.js
+++ b/enums.js
@@ -71,7 +71,12 @@ var SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST  = 111;
 var SPICE_MSG_MAIN_MIGRATE_END          = 112;
 var SPICE_MSG_MAIN_NAME                 = 113;
 var SPICE_MSG_MAIN_UUID                 = 114;
-var SPICE_MSG_END_MAIN                  = 115;
+var SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS = 115;
+var SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS = 116;
+var SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK = 117;
+var SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK = 118;
+var SPICE_MSG_END_MAIN                  = 119;
+
 
 
 var SPICE_MSGC_ACK_SYNC                 = 1;
@@ -300,3 +305,20 @@ var SPICE_CURSOR_TYPE_ALPHA     = 0,
     SPICE_CURSOR_TYPE_COLOR32   = 6;
 
 var SPICE_VIDEO_CODEC_TYPE_MJPEG = 1;
+
+var VD_AGENT_PROTOCOL = 1;
+
+var VD_AGENT_MOUSE_STATE            = 1,
+    VD_AGENT_MONITORS_CONFIG        = 2,
+    VD_AGENT_REPLY                  = 3,
+    VD_AGENT_CLIPBOARD              = 4,
+    VD_AGENT_DISPLAY_CONFIG         = 5,
+    VD_AGENT_ANNOUNCE_CAPABILITIES  = 6,
+    VD_AGENT_CLIPBOARD_GRAB         = 7,
+    VD_AGENT_CLIPBOARD_REQUEST      = 8,
+    VD_AGENT_CLIPBOARD_RELEASE      = 9,
+    VD_AGENT_FILE_XFER_START        =10,
+    VD_AGENT_FILE_XFER_STATUS       =11,
+    VD_AGENT_FILE_XFER_DATA         =12,
+    VD_AGENT_CLIENT_DISCONNECTED    =13,
+    VD_AGENT_MAX_CLIPBOARD          =14;
diff --git a/main.js b/main.js
index 3656a8d..91f1963 100644
--- a/main.js
+++ b/main.js
@@ -40,6 +40,9 @@
 **          onerror     (optional)  If given, a function to receive async
 **                                  errors.  Note that you should also catch
 **                                  errors for ones that occur inline
+**          onagent     (optional)  If given, a function to be called when
+**                                  a VD agent is connected; a good opportunity
+**                                  to request a resize
 **
 **  Throws error if there are troubles.  Requires a modern (by 2012 standards)
 **      browser, including WebSocket and WebSocket.binaryType == arraybuffer
@@ -78,15 +81,11 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
                           " ; ram_hint "                + this.main_init.ram_hint);
         }
 
-        this.mouse_mode = this.main_init.current_mouse_mode;
-        if (this.main_init.current_mouse_mode != SPICE_MOUSE_MODE_CLIENT &&
-            (this.main_init.supported_mouse_modes & SPICE_MOUSE_MODE_CLIENT))
-        {
-            var mode_request = new SpiceMsgcMainMouseModeRequest(SPICE_MOUSE_MODE_CLIENT);
-            var mr = new SpiceMiniData();
-            mr.build_msg(SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST, mode_request);
-            this.send_msg(mr);
-        }
+        this.handle_mouse_mode(this.main_init.current_mouse_mode,
+                               this.main_init.supported_mouse_modes);
+
+        if (this.main_init.agent_connected)
+            this.connect_agent();
 
         var attach = new SpiceMiniData;
         attach.type = SPICE_MSGC_MAIN_ATTACH_CHANNELS;
@@ -99,9 +98,7 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
     {
         var mode = new SpiceMsgMainMouseMode(msg.data);
         DEBUG > 0 && this.log_info("Mouse supported modes " + mode.supported_modes + "; current " + mode.current_mode);
-        this.mouse_mode = mode.current_mode;
-        if (this.inputs)
-            this.inputs.mouse_mode = mode.current_mode;
+        this.handle_mouse_mode(mode.current_mode, mode.supported_modes);
         return true;
     }
 
@@ -144,6 +141,19 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
         return true;
     }
 
+    if (msg.type == SPICE_MSG_MAIN_AGENT_CONNECTED ||
+        msg.type == SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS)
+    {
+        this.connect_agent();
+        return true;
+    }
+
+    if (msg.type == SPICE_MSG_MAIN_AGENT_DISCONNECTED)
+    {
+        this.agent_connected = false;
+        return true;
+    }
+
     return false;
 }
 
@@ -177,3 +187,45 @@ SpiceMainConn.prototype.stop = function(msg)
             this.extra_channels[e].cleanup();
     this.extra_channels = undefined;
 }
+
+SpiceMainConn.prototype.resize_window = function(flags, width, height, depth, x, y)
+{
+    if (this.agent_connected > 0)
+    {
+        var monitors_config = new VDAgentMonitorsConfig(flags, width, height, depth, x, y);
+        var agent_data = new SpiceMsgcMainAgentData(VD_AGENT_MONITORS_CONFIG, monitors_config);
+        var mr = new SpiceMiniData();
+        mr.build_msg(SPICE_MSGC_MAIN_AGENT_DATA, agent_data);
+        this.send_msg(mr);
+    }
+}
+
+SpiceMainConn.prototype.connect_agent = function()
+{
+    this.agent_connected = true;
+
+    var agent_start = new SpiceMsgcMainAgentStart(0);
+    var mr = new SpiceMiniData();
+    mr.build_msg(SPICE_MSGC_MAIN_AGENT_START, agent_start);
+    this.send_msg(mr);
+
+    if (this.onagent !== undefined)
+        this.onagent(this);
+
+}
+
+SpiceMainConn.prototype.handle_mouse_mode = function(current, supported)
+{
+    this.mouse_mode = current;
+    if (current != SPICE_MOUSE_MODE_CLIENT && (supported & SPICE_MOUSE_MODE_CLIENT))
+    {
+        var mode_request = new SpiceMsgcMainMouseModeRequest(SPICE_MOUSE_MODE_CLIENT);
+        var mr = new SpiceMiniData();
+        mr.build_msg(SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST, mode_request);
+        this.send_msg(mr);
+    }
+
+    if (this.inputs)
+        this.inputs.mouse_mode = current;
+}
+
diff --git a/resize.js b/resize.js
new file mode 100644
index 0000000..f5410d3
--- /dev/null
+++ b/resize.js
@@ -0,0 +1,70 @@
+"use strict";
+/*
+   Copyright (C) 2014 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/>.
+*/
+
+/*----------------------------------------------------------------------------
+**  resize.js
+**      This bit of Javascript is a set of logic to help with window
+**  resizing, using the agent channel to request screen resizes.
+**
+**  It's a bit tricky, as we want to wait for resizing to settle down
+**  before sending a size.  Further, while horizontal resizing to use the whole
+**  browser width is fairly easy to arrange with css, resizing an element to use
+**  the whole vertical space (or to force a middle div to consume the bulk of the browser
+**  window size) is tricky, and the consensus seems to be that Javascript is
+**  the only right way to do it.
+**--------------------------------------------------------------------------*/
+function resize_helper(sc)
+{
+    var w = document.getElementById(sc.screen_id).clientWidth;
+    var h = document.getElementById(sc.screen_id).clientHeight;
+
+    var m = document.getElementById(sc.message_id);
+
+    /* Resize vertically; basically we leave a 20 pixel margin
+         at the bottom, and use the position of the message window
+         to figure out how to resize */
+    var hd = window.innerHeight - m.offsetHeight - m.offsetTop - 20;
+
+    /* Xorg requires height be a multiple of 8; round up */
+    h = h + hd;
+    if (h % 8 > 0)
+        h += (8 - (h % 8));
+
+    /* Xorg requires width be a multiple of 8; round up */
+    if (w % 8 > 0)
+        w += (8 - (w % 8));
+
+
+    sc.resize_window(0, w, h, 32, 0, 0);
+    sc.spice_resize_timer = undefined;
+}
+
+function handle_resize(e)
+{
+    var sc = window.spice_connection;
+
+    if (sc && sc.spice_resize_timer)
+    {
+        window.clearTimeout(sc.spice_resize_timer);
+        sc.spice_resize_timer = undefined;
+    }
+
+    sc.spice_resize_timer = window.setTimeout(resize_helper, 200, sc);
+}
diff --git a/spice.css b/spice.css
index 450cf05..5d092ba 100644
--- a/spice.css
+++ b/spice.css
@@ -11,11 +11,9 @@ body
 
 #login
 {
-    width: 790px;
-    margin-top: 20px;
+    width: 95%;
     margin-left: auto;
     margin-right: auto;
-    padding: 2px 10px 2px 20px;
     border: 1px solid #999999;
     background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#24414e));
     background: -moz-linear-gradient(top,  #fff,  #24414e);
@@ -28,7 +26,7 @@ body
 {
     display: inline-block;
     margin-right: 5px;
-    padding-right: 10px;
+    padding: 2px 10px 2px 20px;
     border-right: 1px solid #999999;
     font-size: 20px;
     font-weight: bolder;
@@ -66,16 +64,12 @@ body
 
 #spice-area
 {
-    text-align: center;
-}
-.spice-screen
-{
-    display: inline-block;
-    padding: 10px;
-    min-width: 800px;
-    min-height: 600px;
+    height: 100%;
+    width: 95%;
+    padding: 0;
+    margin-left: auto;
+    margin-right: auto;
     border: solid #222222 1px;
-    background-color: #333333;
     -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
     -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
     box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
@@ -83,6 +77,14 @@ body
     -webkit-border-radius: 10px;
     border-radius: 10px;
 }
+.spice-screen
+{
+    min-height: 600px;
+    height: 100%;
+    margin: 10px;
+    padding: 0;
+    background-color: #333333;
+}
 .spice-message {
     width: 700px;
     height: 50px;
diff --git a/spice.html b/spice.html
index 3b7929c..e830eb3 100644
--- a/spice.html
+++ b/spice.html
@@ -54,6 +54,7 @@
         <script src="thirdparty/rng.js"></script>
         <script src="thirdparty/sha1.js"></script>
         <script src="ticket.js"></script>
+        <script src="resize.js"></script>
         <link rel="stylesheet" type="text/css" href="spice.css" />
 
         <script>
@@ -91,7 +92,7 @@
                 try
                 {
                     sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div", 
-                                message_id: "message-div", password: password, onerror: spice_error });
+                                message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
                 }
                 catch (e)
                 {
@@ -111,6 +112,15 @@
                 document.getElementById('connectButton').onclick = connect;
                 console.log("<< disconnect");
             }
+
+            function agent_connected(sc)
+            {
+                window.addEventListener('resize', handle_resize);
+                window.spice_connection = this;
+
+                resize_helper(this);
+            }
+
         </script>
 
     </head>
diff --git a/spice_auto.html b/spice_auto.html
index 6e1135a..72c5be2 100644
--- a/spice_auto.html
+++ b/spice_auto.html
@@ -54,6 +54,7 @@
         <script src="thirdparty/rng.js"></script>
         <script src="thirdparty/sha1.js"></script>
         <script src="ticket.js"></script>
+        <script src="resize.js"></script>
         <link rel="stylesheet" type="text/css" href="spice.css" />
 
         <script>
@@ -129,7 +130,7 @@
                 try
                 {
                     sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
-                                message_id: "message-div", password: password, onerror: spice_error });
+                                message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
                 }
                 catch (e)
                 {
@@ -148,6 +149,14 @@
                 console.log("<< disconnect");
             }
 
+            function agent_connected(sc)
+            {
+                window.addEventListener('resize', handle_resize);
+                window.spice_connection = this;
+
+                resize_helper(this);
+            }
+
             connect();
         </script>
 
diff --git a/spiceconn.js b/spiceconn.js
index 81bc301..e33227e 100644
--- a/spiceconn.js
+++ b/spiceconn.js
@@ -57,6 +57,8 @@ function SpiceConn(o)
         this.onerror = o.onerror;
     if (o.onsuccess !== undefined)
         this.onsuccess = o.onsuccess;
+    if (o.onagent !== undefined)
+        this.onagent = o.onagent;
 
     this.state = "connecting";
     this.ws.parent = this;
diff --git a/spicemsg.js b/spicemsg.js
index 78371bc..c64f5a3 100644
--- a/spicemsg.js
+++ b/spicemsg.js
@@ -402,6 +402,83 @@ SpiceMsgcMainMouseModeRequest.prototype =
     }
 }
 
+function SpiceMsgcMainAgentStart(num_tokens)
+{
+    this.num_tokens = num_tokens;
+}
+
+SpiceMsgcMainAgentStart.prototype =
+{
+    to_buffer: function(a, at)
+    {
+        at = at || 0;
+        var dv = new SpiceDataView(a);
+        dv.setUint32(at, this.num_tokens, true); at += 4;
+    },
+    buffer_size: function()
+    {
+        return 4;
+    }
+}
+
+function SpiceMsgcMainAgentData(type, data)
+{
+    this.protocol = VD_AGENT_PROTOCOL;
+    this.type = type;
+    this.opaque = 0;
+    this.size = data.buffer_size();
+    this.data = data;
+}
+
+SpiceMsgcMainAgentData.prototype =
+{
+    to_buffer: function(a, at)
+    {
+        at = at || 0;
+        var dv = new SpiceDataView(a);
+        dv.setUint32(at, this.protocol, true); at += 4;
+        dv.setUint32(at, this.type, true); at += 4;
+        dv.setUint64(at, this.opaque, true); at += 8;
+        dv.setUint32(at, this.size, true); at += 4;
+        this.data.to_buffer(a, at);
+    },
+    buffer_size: function()
+    {
+        return 4 + 4 + 8 + 4 + this.data.buffer_size();
+    }
+}
+
+function VDAgentMonitorsConfig(flags, width, height, depth, x, y)
+{
+    this.num_mon = 1;
+    this.flags = flags;
+    this.width = width;
+    this.height = height;
+    this.depth = depth;
+    this.x = x;
+    this.y = y;
+}
+
+VDAgentMonitorsConfig.prototype =
+{
+    to_buffer: function(a, at)
+    {
+        at = at || 0;
+        var dv = new SpiceDataView(a);
+        dv.setUint32(at, this.num_mon, true); at += 4;
+        dv.setUint32(at, this.flags, true); at += 4;
+        dv.setUint32(at, this.height, true); at += 4;
+        dv.setUint32(at, this.width, true); at += 4;
+        dv.setUint32(at, this.depth, true); at += 4;
+        dv.setUint32(at, this.x, true); at += 4;
+        dv.setUint32(at, this.y, true); at += 4;
+    },
+    buffer_size: function()
+    {
+        return 28;
+    }
+}
+
 function SpiceMsgNotify(a, at)
 {
     this.from_buffer(a, at);
-- 
1.7.10.4



More information about the Spice-devel mailing list