]> git.proxmox.com Git - mirror_novnc.git/blobdiff - include/rfb.js
API changes/cleanup.
[mirror_novnc.git] / include / rfb.js
index 3c65a5cbeaf2162b1236f8be0731ca7ffd9d58ef..485adfb59422364d8f799081f62a2987073720cf 100644 (file)
@@ -7,11 +7,11 @@
  */
 
 /*jslint white: false, browser: true, bitwise: false, plusplus: false */
-/*global window, Util, Canvas, Keyboard, Mouse, Websock, Websock_native, Base64, DES, noVNC_logo */
+/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, noVNC_logo */
 
 
 function RFB(conf) {
-    "use strict";
+"use strict";
 
 conf               = conf || {}; // Configuration
 var that           = {},         // Public API interface
@@ -64,7 +64,7 @@ var that           = {},         // Public API interface
     encStats       = {},     // [rectCnt, rectCntTot]
 
     ws             = null,   // Websock object
-    canvas         = null,   // Canvas object
+    display        = null,   // Display object
     keyboard       = null,   // Keyboard input handler object
     mouse          = null,   // Mouse input handler object
     sendTimer      = null,   // Send Queue check timer
@@ -124,8 +124,8 @@ var that           = {},         // Public API interface
 function cdef(v, type, defval, desc) {
     Util.conf_default(conf, that, v, type, defval, desc); }
 
-cdef('target',            'str', null, 'VNC viewport rendering Canvas');
-cdef('focusContainer',    'dom', document, 'Area that traps keyboard input');
+cdef('target',          'dom', null, 'VNC display rendering Canvas object');
+cdef('focusContainer',  'dom', document, 'DOM element that captures keyboard input');
 
 cdef('encrypt',         'bool', false, 'Use TLS/SSL/wss encryption');
 cdef('true_color',      'bool', true,  'Request true color pixel data');
@@ -141,12 +141,25 @@ cdef('disconnectTimeout', 'int', 3,    'Time (s) to wait for disconnection');
 cdef('check_rate',        'int', 217,  'Timing (ms) of send/receive check');
 cdef('fbu_req_rate',      'int', 1413, 'Timing (ms) of frameBufferUpdate requests');
 
-cdef('updateState',
-     'func', function() { Util.Debug("updateState stub"); },
-     'callback: state update');
-cdef('clipboardReceive',
-     'func', function() { Util.Debug("clipboardReceive stub"); },
-     'callback: clipboard contents received');
+// Callback functions
+cdef('onUpdateState',      'func', function() { },
+     'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change ');
+cdef('onPasswordRequired', 'func', function() { },
+     'onPasswordRequired(rfb): VNC password is required ');
+cdef('onClipboard',        'func', function() { },
+     'onClipboard(rfb, text): RFB clipboard contents received');
+cdef('onBell',             'func', function() { },
+     'onBell(rfb): RFB Bell message received ');
+cdef('onFBUReceive',       'func', function() { },
+     'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed ');
+cdef('onFBUComplete',      'func', function() { },
+     'onFBUComplete(rfb, fbu): RFB FBU received and processed ');
+
+// These callback names are deprecated
+cdef('updateState',        'func', function() { },
+     'obsolete, use onUpdateState');
+cdef('clipboardReceive',   'func', function() { },
+     'obsolete, use onClipboard');
 
 
 // Override/add some specific getters/setters
@@ -154,7 +167,7 @@ that.set_local_cursor = function(cursor) {
     if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) {
         conf.local_cursor = false;
     } else {
-        if (canvas.get_cursor_uri()) {
+        if (display.get_cursor_uri()) {
             conf.local_cursor = true;
         } else {
             Util.Warn("Browser does not support local cursor");
@@ -162,8 +175,8 @@ that.set_local_cursor = function(cursor) {
     }
 };
 
-that.get_canvas = function() {
-    return canvas;
+that.get_display = function() {
+    return display;
 };
 that.get_keyboard = function() {
     return keyboard;
@@ -181,7 +194,8 @@ that.get_mouse = function() {
 // Setup routines
 //
 
-// Create the public API interface and initialize
+// Create the public API interface and initialize values that stay
+// constant across connect/disconnect
 function constructor() {
     var i, rmode;
     Util.Debug(">> RFB.constructor");
@@ -192,19 +206,49 @@ function constructor() {
         encNames[encodings[i][1]] = encodings[i][0];
         encStats[encodings[i][1]] = [0, 0];
     }
-    // Initialize canvas, mouse and keyboard
+    // Initialize display, mouse, keyboard, and websock
     try {
-        canvas   = new Canvas({'target': conf.target});
-        keyboard = new Keyboard({'target': conf.focusContainer,
-                                 'keyPress': keyPress});
-        mouse    = new Mouse({'target': conf.target,
-                              'mouseButton': mouseButton,
-                              'mouseMove': mouseMove});
+        display   = new Display({'target': conf.target});
     } catch (exc) {
-        Util.Error("Canvas exception: " + exc);
-        updateState('fatal', "No working Canvas");
+        Util.Error("Display exception: " + exc);
+        updateState('fatal', "No working Display");
     }
-    rmode = canvas.get_render_mode();
+    keyboard = new Keyboard({'target': conf.focusContainer,
+                                'onKeyPress': keyPress});
+    mouse    = new Mouse({'target': conf.target,
+                            'onMouseButton': mouseButton,
+                            'onMouseMove': mouseMove});
+
+    rmode = display.get_render_mode();
+
+    if (typeof noVNC_logo !== 'undefined') {
+        display.set_logo(noVNC_logo);
+    }
+
+    ws = new Websock();
+    ws.on('message', handle_message);
+    ws.on('open', function() {
+        if (rfb_state === "connect") {
+            updateState('ProtocolVersion', "Starting VNC handshake");
+        } else {
+            fail("Got unexpected WebSockets connection");
+        }
+    });
+    ws.on('close', function() {
+        if (rfb_state === 'disconnect') {
+            updateState('disconnected', 'VNC disconnected');
+        } else if (rfb_state === 'ProtocolVersion') {
+            fail('Failed to connect to server');
+        } else if (rfb_state in {'failed':1, 'disconnected':1}) {
+            Util.Error("Received onclose while disconnected");
+        } else  {
+            fail('Server disconnected');
+        }
+    });
+    ws.on('error', function(e) {
+        fail("WebSock error: " + e);
+    });
+
 
     init_vars();
 
@@ -246,36 +290,13 @@ function connect() {
     Util.Debug("<< RFB.connect");
 }
 
+// Initialize variables that are reset before each connection
 init_vars = function() {
     var i;
 
     /* Reset state */
-    ws = new Websock();
     ws.init();
 
-    ws.on('message', handle_message);
-    ws.on('open', function() {
-        if (rfb_state === "connect") {
-            updateState('ProtocolVersion', "Starting VNC handshake");
-        } else {
-            fail("Got unexpected WebSockets connection");
-        }
-    });
-    ws.on('close', function() {
-        if (rfb_state === 'disconnect') {
-            updateState('disconnected', 'VNC disconnected');
-        } else if (rfb_state === 'ProtocolVersion') {
-            fail('Failed to connect to server');
-        } else if (rfb_state in {'failed':1, 'disconnected':1}) {
-            Util.Error("Received onclose while disconnected");
-        } else  {
-            fail('Server disconnected');
-        }
-    });
-    ws.on('error', function(e) {
-        fail("WebSock error: " + e);
-    });
-
     FBU.rects        = 0;
     FBU.subrects     = 0;  // RRE and HEXTILE
     FBU.lines        = 0;  // RAW
@@ -317,25 +338,23 @@ print_stats = function() {
 
 
 /*
- * Running states:
- *   disconnected - idle state
- *   normal       - connected
- *
  * Page states:
  *   loaded       - page load, equivalent to disconnected
- *   connect      - starting initialization
- *   disconnect   - starting disconnect
- *   failed       - abnormal transition to disconnected
+ *   disconnected - idle state
+ *   connect      - starting to connect (to ProtocolVersion)
+ *   normal       - connected
+ *   disconnect   - starting to disconnect
+ *   failed       - abnormal disconnect
  *   fatal        - failed to load page, or fatal error
  *
- * VNC initialization states:
- *   ProtocolVersion
+ * RFB protocol initialization states:
+ *   ProtocolVersion 
  *   Security
  *   Authentication
  *   password     - waiting for password, not part of RFB
  *   SecurityResult
  *   ClientInitialization - not triggered by server message
- *   ServerInitialization
+ *   ServerInitialization (to normal)
  */
 updateState = function(state, statusMsg) {
     var func, cmsg, oldstate = rfb_state;
@@ -362,22 +381,15 @@ updateState = function(state, statusMsg) {
             msgTimer = null;
         }
 
-        if (canvas && canvas.getContext()) {
+        if (display && display.get_context()) {
             keyboard.ungrab();
             mouse.ungrab();
-            canvas.defaultCursor();
-            if (Util.get_logging() !== 'debug') {
-                canvas.clear();
-            }
-
+            display.defaultCursor();
             if ((Util.get_logging() !== 'debug') ||
                 (state === 'loaded')) {
                 // Show noVNC logo on load and when disconnected if
                 // debug is off
-                if (typeof noVNC_logo !== 'undefined' && noVNC_logo) {
-                    canvas.resize(noVNC_logo.width, noVNC_logo.height);
-                    canvas.blitStringImage(noVNC_logo.data, 0, 0);
-                }
+                display.clear();
             }
         }
 
@@ -476,9 +488,11 @@ updateState = function(state, statusMsg) {
 
     if ((oldstate === 'failed') && (state === 'disconnected')) {
         // Leave the failed message
-        conf.updateState(that, state, oldstate);
+        conf.updateState(that, state, oldstate); // Obsolete
+        conf.onUpdateState(that, state, oldstate);
     } else {
-        conf.updateState(that, state, oldstate, statusMsg);
+        conf.updateState(that, state, oldstate, statusMsg); // Obsolete
+        conf.onUpdateState(that, state, oldstate, statusMsg);
     }
 };
 
@@ -681,7 +695,10 @@ init_msg = function() {
                 break;
             case 2:  // VNC authentication
                 if (rfb_password.length === 0) {
+                    // Notify via both callbacks since it is kind of
+                    // a RFB state change and a UI interface issue.
                     updateState('password', "Password Required");
+                    conf.onPasswordRequired(that);
                     return;
                 }
                 if (ws.rQwait("auth challenge", 16)) { return false; }
@@ -759,7 +776,8 @@ init_msg = function() {
         name_length   = ws.rQshift32();
         fb_name = ws.rQshiftStr(name_length);
 
-        canvas.resize(fb_width, fb_height, conf.true_color);
+        display.set_true_color(conf.true_color);
+        display.resize(fb_width, fb_height);
         keyboard.grab();
         mouse.grab();
 
@@ -796,7 +814,7 @@ init_msg = function() {
 normal_msg = function() {
     //Util.Debug(">> normal_msg");
 
-    var ret = true, msg_type, length,
+    var ret = true, msg_type, length, text,
         c, first_colour, num_colours, red, green, blue;
 
     if (FBU.rects > 0) {
@@ -820,13 +838,15 @@ normal_msg = function() {
             //Util.Debug("red after: " + red);
             green = parseInt(ws.rQshift16() / 256, 10);
             blue = parseInt(ws.rQshift16() / 256, 10);
-            canvas.set_colourMap([red, green, blue], first_colour + c);
+            Util.Debug("*** colourMap: " + display.get_colourMap());
+            display.set_colourMap([red, green, blue], first_colour + c);
         }
         Util.Info("Registered " + num_colours + " colourMap entries");
-        //Util.Debug("colourMap: " + canvas.get_colourMap());
+        //Util.Debug("colourMap: " + display.get_colourMap());
         break;
     case 2:  // Bell
-        Util.Warn("Bell (unsupported)");
+        Util.Debug("Bell");
+        conf.onBell(that);
         break;
     case 3:  // ServerCutText
         Util.Debug("ServerCutText");
@@ -835,7 +855,9 @@ normal_msg = function() {
         length = ws.rQshift32();
         if (ws.rQwait("ServerCutText", length, 8)) { return false; }
 
-        conf.clipboardReceive(that, ws.rQshiftStr(length));
+        text = ws.rQshiftStr(length);
+        conf.clipboardReceive(that, text); // Obsolete
+        conf.onClipboard(that, text);
         break;
     default:
         fail("Disconnected: illegal server message type " + msg_type);
@@ -883,6 +905,12 @@ framebufferUpdate = function() {
             FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
                                     (hdr[10] << 8) +  hdr[11], 10);
 
+            conf.onFBUReceive(that,
+                    {'x': FBU.x, 'y': FBU.y,
+                     'width': FBU.width, 'height': FBU.height,
+                     'encoding': FBU.encoding,
+                     'encodingName': encNames[FBU.encoding]});
+
             if (encNames[FBU.encoding]) {
                 // Debug:
                 /*
@@ -943,6 +971,13 @@ framebufferUpdate = function() {
             return ret; // false ret means need more data
         }
     }
+
+    conf.onFBUComplete(that,
+            {'x': FBU.x, 'y': FBU.y,
+                'width': FBU.width, 'height': FBU.height,
+                'encoding': FBU.encoding,
+                'encodingName': encNames[FBU.encoding]});
+
     return true; // We finished this FBU
 };
 
@@ -963,7 +998,7 @@ encHandlers.RAW = function display_raw() {
     cur_y = FBU.y + (FBU.height - FBU.lines);
     cur_height = Math.min(FBU.lines,
                           Math.floor(ws.rQlen()/(FBU.width * fb_Bpp)));
-    canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height,
+    display.blitImage(FBU.x, cur_y, FBU.width, cur_height,
             ws.get_rQ(), ws.get_rQi());
     ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp);
     FBU.lines -= cur_height;
@@ -986,7 +1021,7 @@ encHandlers.COPYRECT = function display_copy_rect() {
     if (ws.rQwait("COPYRECT", 4)) { return false; }
     old_x = ws.rQshift16();
     old_y = ws.rQshift16();
-    canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
+    display.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
     FBU.rects -= 1;
     FBU.bytes = 0;
     return true;
@@ -1000,7 +1035,7 @@ encHandlers.RRE = function display_rre() {
         if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }
         FBU.subrects = ws.rQshift32();
         color = ws.rQshiftBytes(fb_Bpp); // Background
-        canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
+        display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
     }
     while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) {
         color = ws.rQshiftBytes(fb_Bpp);
@@ -1008,7 +1043,7 @@ encHandlers.RRE = function display_rre() {
         y = ws.rQshift16();
         width = ws.rQshift16();
         height = ws.rQshift16();
-        canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
+        display.fillRect(FBU.x + x, FBU.y + y, width, height, color);
         FBU.subrects -= 1;
     }
     //Util.Debug("   display_rre: rects: " + FBU.rects +
@@ -1101,10 +1136,10 @@ encHandlers.HEXTILE = function display_hextile() {
                 /* Weird: ignore blanks after RAW */
                 Util.Debug("     Ignoring blank after RAW");
             } else {
-                canvas.fillRect(x, y, w, h, FBU.background);
+                display.fillRect(x, y, w, h, FBU.background);
             }
         } else if (FBU.subencoding & 0x01) { // Raw
-            canvas.blitImage(x, y, w, h, rQ, rQi);
+            display.blitImage(x, y, w, h, rQ, rQi);
             rQi += FBU.bytes - 1;
         } else {
             if (FBU.subencoding & 0x02) { // Background
@@ -1116,7 +1151,7 @@ encHandlers.HEXTILE = function display_hextile() {
                 rQi += fb_Bpp;
             }
 
-            tile = canvas.getTile(x, y, w, h, FBU.background);
+            tile = display.getTile(x, y, w, h, FBU.background);
             if (FBU.subencoding & 0x08) { // AnySubrects
                 subrects = rQ[rQi];
                 rQi += 1;
@@ -1137,10 +1172,10 @@ encHandlers.HEXTILE = function display_hextile() {
                     sw = (wh >> 4)   + 1;
                     sh = (wh & 0x0f) + 1;
 
-                    canvas.setSubTile(tile, sx, sy, sw, sh, color);
+                    display.setSubTile(tile, sx, sy, sw, sh, color);
                 }
             }
-            canvas.putTile(tile);
+            display.putTile(tile);
         }
         ws.set_rQi(rQi);
         FBU.lastsubencoding = FBU.subencoding;
@@ -1205,7 +1240,7 @@ encHandlers.TIGHT_PNG = function display_tight_png() {
     case "fill":
         ws.rQshift8(); // shift off ctl
         color = ws.rQshiftBytes(fb_depth);
-        canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
+        display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
         break;
     case "jpeg":
     case "png":
@@ -1242,7 +1277,7 @@ extract_data_uri = function(arr) {
 
 scan_tight_imgQ = function() {
     var img, imgQ, ctx;
-    ctx = canvas.getContext();
+    ctx = display.get_context();
     if (rfb_state === 'normal') {
         imgQ = FBU.imgQ;
         while ((imgQ.length > 0) && (imgQ[0][0].complete)) {
@@ -1257,8 +1292,7 @@ encHandlers.DesktopSize = function set_desktopsize() {
     Util.Debug(">> set_desktopsize");
     fb_width = FBU.width;
     fb_height = FBU.height;
-    canvas.clear();
-    canvas.resize(fb_width, fb_height);
+    display.resize(fb_width, fb_height);
     timing.fbu_rt_start = (new Date()).getTime();
     // Send a new non-incremental request
     ws.send(fbUpdateRequest(0));
@@ -1286,7 +1320,7 @@ encHandlers.Cursor = function set_cursor() {
 
     //Util.Debug("   set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
 
-    canvas.changeCursor(ws.rQshiftBytes(pixelslength),
+    display.changeCursor(ws.rQshiftBytes(pixelslength),
                             ws.rQshiftBytes(masklength),
                             x, y, w, h);