浏览代码

Add cut and paste support.

- A textarea below the VNC area represents the state of the current
  VNC clipboard. If there is a server cut event, the textarea will be
  updated. If the user updates the contents of the textarea, the new
  data will be sent as a client paste (cut) event.

- One important change was to detect if the clipboard is focused and
  allow the user to type in the clipboard instead of in the VNC area.
Joel Martin 15 年之前
父节点
当前提交
30059bdf24
共有 3 个文件被更改,包括 92 次插入13 次删除
  1. 0 2
      TODO
  2. 9 0
      vnc.html
  3. 83 11
      vnc.js

+ 0 - 2
TODO

@@ -1,5 +1,3 @@
-- Cut and paste support.
-
 - Better status and error feedback.
 - Better status and error feedback.
 
 
 - Get working in firefox using flash web-socket-js:
 - Get working in firefox using flash web-socket-js:

+ 9 - 0
vnc.html

@@ -16,6 +16,15 @@
                 style="border-style: dotted; border-width: 1px;">
                 style="border-style: dotted; border-width: 1px;">
             Canvas not supported.
             Canvas not supported.
         </canvas>
         </canvas>
+
+        <br><br>
+        VNC Clipboard:
+        <input id='clearButton' type="button" value="Clear"
+            onclick="RFB.clipboardClear();"><br>
+        <textarea id="clipboard" style="font-size:9;" cols=80 rows=5
+            onchange="RFB.clipboardPasteFrom();"
+            onfocus="RFB.clipboardFocus=true;"
+            onblur="RFB.clipboardFocus=false;"></textarea>
     </body>
     </body>
 
 
     <script src="include/mootools.js"></script>
     <script src="include/mootools.js"></script>

+ 83 - 11
vnc.js

@@ -33,6 +33,12 @@ Array.prototype.shiftStr = function (len) {
     return arr.map(function (num) {
     return arr.map(function (num) {
             return String.fromCharCode(num); } ).join('');
             return String.fromCharCode(num); } ).join('');
 }
 }
+Array.prototype.pushStr = function (str) {
+    for (var i=0; i < str.length; i++) {
+        this.push(str.charCodeAt(i));
+    }
+}
+
 Array.prototype.shiftBytes = function (len) {
 Array.prototype.shiftBytes = function (len) {
     return this.splice(0, len);
     return this.splice(0, len);
 }
 }
@@ -74,6 +80,10 @@ d         : [],    // Received data accumulator
 
 
 version   : "RFB 003.003\n",
 version   : "RFB 003.003\n",
 state     : 'ProtocolVersion',
 state     : 'ProtocolVersion',
+cuttext   : 'none', // ServerCutText wait state
+ct_length : 0,
+clipboardFocus: false,
+
 shared    : 1,
 shared    : 1,
 check_rate : 217,
 check_rate : 217,
 req_rate  : 1413,
 req_rate  : 1413,
@@ -233,6 +243,8 @@ normal_msg: function () {
     //console.log(">> normal_msg");
     //console.log(">> normal_msg");
     if (FBU.rects > 0) {
     if (FBU.rects > 0) {
         var msg_type = 0;
         var msg_type = 0;
+    } else if (RFB.cuttext != 'none') {
+        var msg_type = 3;
     } else {
     } else {
         var msg_type = RFB.d.shift8();
         var msg_type = RFB.d.shift8();
     }
     }
@@ -305,9 +317,25 @@ normal_msg: function () {
         break;
         break;
     case 3:  // ServerCutText
     case 3:  // ServerCutText
         console.log("ServerCutText");
         console.log("ServerCutText");
-        RFB.d.shiftBytes(3);  // Padding
-        var length = RFB.d.shift32();
-        RFB.d.shiftBytes(length);
+        console.log("RFB.d:" + RFB.d.slice(0,20));
+        if (RFB.cuttext == 'none') {
+            RFB.cuttext = 'header';
+        }
+        if (RFB.cuttext == 'header') {
+            if (RFB.d.length < 7) {
+                console.log("waiting for ServerCutText header");
+                break;
+            }
+            RFB.d.shiftBytes(3);  // Padding
+            RFB.ct_length = RFB.d.shift32();
+        }
+        RFB.cuttext = 'bytes';
+        if (RFB.d.length < RFB.ct_length) {
+            console.log("waiting for ServerCutText bytes");
+            break;
+        }
+        RFB.clipboardCopyTo(RFB.d.shiftStr(RFB.ct_length));
+        RFB.cuttext = 'none';
         break;
         break;
     default:
     default:
         console.log("Unknown server message type: " + msg_type);
         console.log("Unknown server message type: " + msg_type);
@@ -618,7 +646,17 @@ pointerEvent: function (x, y) {
     return arr;
     return arr;
 },
 },
 
 
-clientCutText: function () {
+clientCutText: function (text) {
+    console.log(">> clientCutText");
+    var arr;
+    arr = [6];     // msg-type
+    arr.push8(0);  // padding
+    arr.push8(0);  // padding
+    arr.push8(0);  // padding
+    arr.push32(text.length);
+    arr.pushStr(text);
+    console.log("<< clientCutText");
+    return arr;
 },
 },
 
 
 
 
@@ -680,20 +718,24 @@ checkEvents: function () {
     }
     }
 },
 },
 
 
-keyDown: function (e) {
-    //console.log(">> keyDown: " + Canvas.getKeysym(e));
+_keyX: function (e, down) {
+    if (RFB.clipboardFocus) {
+        return true;
+    }
     e.stop();
     e.stop();
-    var arr = RFB.keyEvent(Canvas.getKeysym(e), 1);
+    var arr = RFB.keyEvent(Canvas.getKeysym(e), down);
     arr = arr.concat(RFB.fbUpdateRequest(1));
     arr = arr.concat(RFB.fbUpdateRequest(1));
     RFB.send_array(arr);
     RFB.send_array(arr);
 },
 },
 
 
+keyDown: function (e) {
+    //console.log(">> keyDown: " + Canvas.getKeysym(e));
+    RFB._keyX(e, 1);
+},
+
 keyUp: function (e) {
 keyUp: function (e) {
     //console.log(">> keyUp: " + Canvas.getKeysym(e));
     //console.log(">> keyUp: " + Canvas.getKeysym(e));
-    e.stop();
-    var arr = RFB.keyEvent(Canvas.getKeysym(e), 0);
-    arr = arr.concat(RFB.fbUpdateRequest(1));
-    RFB.send_array(arr);
+    RFB._keyX(e, 0);
 },
 },
 
 
 mouseDown: function(e) {
 mouseDown: function(e) {
@@ -729,6 +771,24 @@ mouseMove: function(e) {
     Mouse.arr = Mouse.arr.concat( RFB.pointerEvent(x, y) );
     Mouse.arr = Mouse.arr.concat( RFB.pointerEvent(x, y) );
 },
 },
 
 
+clipboardCopyTo: function (text) {
+    console.log(">> clipboardCopyTo: " + text.substr(0,40) + "...");
+    $('clipboard').value = text;
+    console.log("<< clipboardCopyTo");
+},
+
+clipboardPasteFrom: function () {
+    if (RFB.state != "normal") return;
+    var text = $('clipboard').value;
+    console.log(">> clipboardPasteFrom: " + text.substr(0,40) + "...");
+    RFB.send_array(RFB.clientCutText(text));
+    console.log("<< clipboardPasteFrom");
+},
+
+clipboardClear: function () {
+    $('clipboard').value = '';
+    RFB.clipboardPasteFrom();
+},
 
 
 
 
 /*
 /*
@@ -781,10 +841,22 @@ connect: function () {
         console.log("must set host and port");
         console.log("must set host and port");
         return;
         return;
     }
     }
+
+    /* Reset state */
+    RFB.cuttext  = 'none';
+    RFB.ct_length = 0;
+    FBU.rects    = 0;
+    FBU.subrects = 0;  // RRE and HEXTILE
+    FBU.lines    = 0,  // RAW
+    FBU.tiles    = 0,  // HEXTILE
+    Mouse.buttonmask = 0;
+    Mouse.arr    = [];
+
     if (RFB.ws) {
     if (RFB.ws) {
         RFB.ws.close();
         RFB.ws.close();
     }
     }
     RFB.init_ws();
     RFB.init_ws();
+
     $('connectButton').value = "Disconnect";
     $('connectButton').value = "Disconnect";
     $('connectButton').onclick = RFB.disconnect;
     $('connectButton').onclick = RFB.disconnect;
     console.log("<< connect");
     console.log("<< connect");