Sfoglia il codice sorgente

Merge branch 'master' into mobile

Conflicts:
	include/rfb.js
Joel Martin 14 anni fa
parent
commit
9192cbe39c
5 ha cambiato i file con 296 aggiunte e 201 eliminazioni
  1. 198 22
      include/display.js
  2. 22 8
      include/mobile.css
  3. 3 1
      include/rfb.js
  4. 71 168
      tests/viewport.html
  5. 2 2
      utils/websocket.py

+ 198 - 22
include/display.js

@@ -25,8 +25,12 @@ var that           = {},  // Public API methods
     imageDataCreate, imageDataGet, rgbxImageData, cmapImageData,
     rgbxImageFill, cmapImageFill, setFillColor, rescale, flush,
 
-    c_width        = 0,
-    c_height       = 0,
+    // The full frame buffer (logical canvas) size
+    fb_width        = 0,
+    fb_height       = 0,
+    // The visible "physical canvas" viewport
+    viewport       = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 },
+    cleanRect      = {'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1},
 
     c_prevStyle    = "",
 
@@ -55,11 +59,11 @@ that.get_context = function () { return c_ctx; };
 
 that.set_scale = function(scale) { rescale(scale); };
 
-that.set_width = function (val) { that.resize(val, c_height); };
-that.get_width = function() { return c_width; };
+that.set_width = function (val) { that.resize(val, fb_height); };
+that.get_width = function() { return fb_width; };
 
-that.set_height = function (val) { that.resize(c_width, val); };
-that.get_height = function() { return c_height; };
+that.set_height = function (val) { that.resize(fb_width, val); };
+that.get_height = function() { return fb_height; };
 
 that.set_prefer_js = function(val) {
     if (val && c_forceCanvas) {
@@ -217,7 +221,10 @@ rescale = function(factor) {
         return;
     }
 
-    if (factor > 1.0) {
+
+    if (typeof(factor) === "undefined") {
+        factor = conf.scale;
+    } else if (factor > 1.0) {
         factor = 1.0;
     } else if (factor < 0.1) {
         factor = 0.1;
@@ -234,6 +241,174 @@ rescale = function(factor) {
     c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)";
 };
 
+that.viewportChange = function(deltaX, deltaY, width, height) {
+    var c = conf.target, v = viewport, cr = cleanRect,
+        saveImg = null, saveStyle, x1, y1, vx2, vy2, w, h;
+
+    if (typeof(deltaX) === "undefined") { deltaX = 0; }
+    if (typeof(deltaY) === "undefined") { deltaY = 0; }
+    if (typeof(width) === "undefined") { width = v.w; }
+    if (typeof(height) === "undefined") { height = v.h; }
+
+    // Size change
+
+    if (width > fb_width) { width = fb_width; }
+    if (height > fb_height) { height = fb_height; }
+
+    if ((v.w !== width) || (v.h !== height)) {
+        // Change width
+        if ((width < v.w) && (cr.x2 > v.x + width -1)) {
+            cr.x2 = v.x + width - 1;
+        }
+        v.w = width;
+
+        // Change height
+        if ((height < v.h) && (cr.y2 > v.y + height -1)) {
+            cr.y2 = v.y + height - 1;
+        }
+        v.h = height;
+
+
+        if (v.w > 0 && v.h > 0) {
+            saveImg = c_ctx.getImageData(0, 0,
+                    (c.width < v.w) ? c.width : v.w,
+                    (c.height < v.h) ? c.height : v.h);
+        }
+
+        c.width = v.w;
+        c.height = v.h;
+
+        if (saveImg) {
+            c_ctx.putImageData(saveImg, 0, 0);
+        }
+    }
+
+    vx2 = v.x + v.w - 1;
+    vy2 = v.y + v.h - 1;
+
+
+    // Position change
+
+    if ((deltaX < 0) && ((v.x + deltaX) < 0)) {
+        deltaX = - v.x;
+    }
+    if ((vx2 + deltaX) >= fb_width) {
+        deltaX -= ((vx2 + deltaX) - fb_width + 1);
+    }
+
+    if ((v.y + deltaY) < 0) {
+        deltaY = - v.y;
+    }
+    if ((vy2 + deltaY) >= fb_height) {
+        deltaY -= ((vy2 + deltaY) - fb_height + 1);
+    }
+
+    if ((deltaX === 0) && (deltaY === 0)) {
+        //message("skipping");
+        return;
+    }
+    message("deltaX: " + deltaX + ", deltaY: " + deltaY);
+
+    v.x += deltaX;
+    vx2 += deltaX;
+    v.y += deltaY;
+    vy2 += deltaY;
+
+    // Update the clean rectangle
+    if (v.x > cr.x1) {
+        cr.x1 = v.x;
+    }
+    if (vx2 < cr.x2) {
+        cr.x2 = vx2;
+    }
+    if (v.y > cr.y1) {
+        cr.y1 = v.y;
+    }
+    if (vy2 < cr.y2) {
+        cr.y2 = vy2;
+    }
+
+    if (deltaX < 0) {
+        // Shift viewport left, redraw left section
+        x1 = 0;
+        w = - deltaX;
+    } else {
+        // Shift viewport right, redraw right section
+        x1 = v.w - deltaX;
+        w = deltaX;
+    }
+    if (deltaY < 0) {
+        // Shift viewport up, redraw top section
+        y1 = 0;
+        h = - deltaY;
+    } else {
+        // Shift viewport down, redraw bottom section
+        y1 = v.h - deltaY;
+        h = deltaY;
+    }
+
+    // Copy the valid part of the viewport to the shifted location
+    saveStyle = c_ctx.fillStyle;
+    c_ctx.fillStyle = "rgb(255,255,255)";
+    if (deltaX !== 0) {
+        //that.copyImage(0, 0, -deltaX, 0, v.w, v.h);
+        //that.fillRect(x1, 0, w, v.h, [255,255,255]);
+        c_ctx.drawImage(c, 0, 0, v.w, v.h, -deltaX, 0, v.w, v.h);
+        c_ctx.fillRect(x1, 0, w, v.h);
+    }
+    if (deltaY !== 0) {
+        //that.copyImage(0, 0, 0, -deltaY, v.w, v.h);
+        //that.fillRect(0, y1, v.w, h, [255,255,255]);
+        c_ctx.drawImage(c, 0, 0, v.w, v.h, 0, -deltaY, v.w, v.h);
+        c_ctx.fillRect(0, y1, v.w, h);
+    }
+    c_ctx.fillStyle = saveStyle;
+}
+
+that.getCleanDirtyReset = function() {
+    var v = viewport, c = cleanRect, cleanBox, dirtyBoxes = [],
+        vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1;
+
+
+    // Copy the cleanRect
+    cleanBox = {'x': c.x1, 'y': c.y1,
+                'w': c.x2 - c.x1 + 1, 'h': c.y2 - c.y1 + 1};
+
+    if ((c.x1 >= c.x2) || (c.y1 >= c.y2)) {
+        // Whole viewport is dirty
+        dirtyBoxes.push({'x': v.x, 'y': v.y, 'w': v.w, 'h': v.h});
+    } else {
+        // Redraw dirty regions
+        if (v.x < c.x1) {
+            // left side dirty region
+            dirtyBoxes.push({'x': v.x, 'y': v.y,
+                             'w': c.x1 - v.x + 1, 'h': v.h});
+        }
+        if (vx2 > c.x2) {
+            // right side dirty region
+            dirtyBoxes.push({'x': c.x2 + 1, 'y': v.y,
+                             'w': vx2 - c.x2, 'h': v.h});
+        }
+        if (v.y < c.y1) {
+            // top/middle dirty region
+            dirtyBoxes.push({'x': c.x1, 'y': v.y,
+                             'w': c.x2 - c.x1 + 1, 'h': c.y1 - v.y});
+        }
+        if (vy2 > c.y2) {
+            // bottom/middle dirty region
+            dirtyBoxes.push({'x': c.x1, 'y': c.y2 + 1,
+                             'w': c.x2 - c.x1 + 1, 'h': vy2 - c.y2});
+        }
+    }
+
+    // Reset the cleanRect to the whole viewport
+    cleanRect = {'x1': v.x, 'y1': v.y,
+                 'x2': v.x + v.w - 1, 'y2': v.y + v.h - 1};
+
+    return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes};
+}
+
+
 // Force canvas redraw (for webkit bug #46319 workaround)
 flush = function() {
     var old_val;
@@ -266,27 +441,25 @@ setFillColor = function(color) {
 //
 
 that.resize = function(width, height) {
-    var c = conf.target;
-
     c_prevStyle    = "";
 
-    c.width = width;
-    c.height = height;
-
-    c_width  = c.offsetWidth;
-    c_height = c.offsetHeight;
+    fb_width = width;
+    fb_height = height;
 
     rescale(conf.scale);
+    that.viewportChange();
 };
 
 that.clear = function() {
 
     if (conf.logo) {
         that.resize(conf.logo.width, conf.logo.height);
+        that.viewportChange(0, 0, conf.logo.width, conf.logo.height);
         that.blitStringImage(conf.logo.data, 0, 0);
     } else {
         that.resize(640, 20);
-        c_ctx.clearRect(0, 0, c_width, c_height);
+        that.viewportChange(0, 0, 640, 20);
+        c_ctx.clearRect(0, 0, viewport.w, viewport.h);
     }
 
     // No benefit over default ("source-over") in Chrome and firefox
@@ -295,12 +468,13 @@ that.clear = function() {
 
 that.fillRect = function(x, y, width, height, color) {
     setFillColor(color);
-    c_ctx.fillRect(x, y, width, height);
+    c_ctx.fillRect(x - viewport.x, y - viewport.y, width, height);
 };
 
-that.copyImage = function(old_x, old_y, new_x, new_y, width, height) {
-    c_ctx.drawImage(conf.target, old_x, old_y, width, height,
-                                       new_x, new_y, width, height);
+that.copyImage = function(old_x, old_y, new_x, new_y, w, h) {
+    var x1 = old_x - viewport.x, y1 = old_y - viewport.y,
+        x2 = new_x - viewport.x, y2 = new_y  - viewport.y;
+    c_ctx.drawImage(conf.target, x1, y1, w, h, x2, y2, w, h);
 };
 
 /*
@@ -386,7 +560,7 @@ rgbxImageData = function(x, y, width, height, arr, offset) {
         data[i + 2] = arr[j + 2];
         data[i + 3] = 255; // Set Alpha
     }
-    c_ctx.putImageData(img, x, y);
+    c_ctx.putImageData(img, x - viewport.x, y - viewport.y);
 };
 
 // really slow fallback if we don't have imageData
@@ -414,7 +588,7 @@ cmapImageData = function(x, y, width, height, arr, offset) {
         data[i + 2] = rgb[2];
         data[i + 3] = 255; // Set Alpha
     }
-    c_ctx.putImageData(img, x, y);
+    c_ctx.putImageData(img, x - viewport.x, y - viewport.y);
 };
 
 cmapImageFill = function(x, y, width, height, arr, offset) {
@@ -441,7 +615,9 @@ that.blitImage = function(x, y, width, height, arr, offset) {
 
 that.blitStringImage = function(str, x, y) {
     var img = new Image();
-    img.onload = function () { c_ctx.drawImage(img, x, y); };
+    img.onload = function () {
+        c_ctx.drawImage(img, x - viewport.x, y - viewport.y);
+    };
     img.src = str;
 };
 

+ 22 - 8
include/mobile.css

@@ -1,24 +1,28 @@
-.fullscreen {
-    display: block;
-    position: absolute;
-    top: 0px;
-    left: 0px;
+html,body {
+    margin: 0px;
+    padding: 0px;
     width: 100%;
     height: 100%;
-    z-index: 9999;
-    margin: 0;
-    padding: 0;
 }
 
 .flex-layout {
+    width: 100%;
+    height: 100%;
+
     display: box;
     display: -webkit-box;
     display: -moz-box;
     display: -ms-box;
+
     box-orient: vertical;
     -webkit-box-orient: vertical;
     -moz-box-orient: vertical;
     -ms-box-orient: vertical;
+
+    box-align: stretch;
+    -webkit-box-align: stretch;
+    -moz-box-align: stretch;
+    -ms-box-align: stretch;
 }
 .flex-box {
     box-flex: 1;
@@ -27,3 +31,13 @@
     -ms-box-flex: 1;
 }
 
+.container {
+    margin: 0px;
+    padding: 0px;
+}
+
+.canvas {
+    position: absolute;
+    border-style: dotted;
+    border-width: 1px;
+}

+ 3 - 1
include/rfb.js

@@ -778,6 +778,7 @@ init_msg = function() {
 
         display.set_true_color(conf.true_color);
         display.resize(fb_width, fb_height);
+        display.viewportChange(0, 0, fb_width, fb_height);
         keyboard.grab();
         mouse.grab();
 
@@ -840,7 +841,7 @@ normal_msg = function() {
             blue = parseInt(ws.rQshift16() / 256, 10);
             display.set_colourMap([red, green, blue], first_colour + c);
         }
-        Util.Debug("*** colourMap: " + display.get_colourMap());
+        Util.Debug("colourMap: " + display.get_colourMap());
         Util.Info("Registered " + num_colours + " colourMap entries");
         //Util.Debug("colourMap: " + display.get_colourMap());
         break;
@@ -1308,6 +1309,7 @@ encHandlers.DesktopSize = function set_desktopsize() {
     fb_width = FBU.width;
     fb_height = FBU.height;
     display.resize(fb_width, fb_height);
+    display.viewportChange(0, 0, fb_width, fb_height);
     timing.fbu_rt_start = (new Date()).getTime();
     // Send a new non-incremental request
     ws.send(fbUpdateRequest(0));

+ 71 - 168
tests/viewport.html

@@ -9,29 +9,25 @@
         <meta name=viewport content="width=device-width, initial-scale=1.0, user-scalable=no">
     </head>
     <body>
-        <div class="fullscreen flex-layout">
-
-        <div>
-            Canvas:
-                <input id="move-selector" type="button" value="Move"
-                    onclick="toggleMove();">
-            <br>
-        </div>
-        <div id="container" class="flex-box">
-            <canvas id="canvas"
-                    style="border-style: dotted; border-width: 1px;">
-                Canvas not supported.
-            </canvas>
-            <br>
-        </div>
-        <div>
-            <br>
-            Results:<br>
-            <textarea id="messages" style="font-size: 9;" cols=80 rows=8></textarea>
+        <div class="flex-layout">
+            <div>
+                Canvas:
+                    <input id="move-selector" type="button" value="Move"
+                        onclick="toggleMove();">
+                <br>
+            </div>
+            <div class="container flex-box">
+                <canvas id="canvas" class="canvas">
+                    Canvas not supported.
+                </canvas>
+                <br>
+            </div>
+            <div>
+                <br>
+                Results:<br>
+                <textarea id="messages" style="font-size: 9;" cols=80 rows=8></textarea>
+            </div>
         </div>
-
-        </div>
-
     </body>
 
     <!--
@@ -45,13 +41,10 @@
     <script src="../include/display.js"></script>
     <script>
         var msg_cnt = 0, iterations,
-            fb_width = 800,
-            fb_height = 768,
-            viewport = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 },
-            cleanRect = {},
             penDown = false, doMove = false,
             inMove = false, lastPos = {},
-            canvas, ctx, keyboard, mouse;
+            padW = 0, padH = 0,
+            display, ctx, keyboard, mouse;
 
         var newline = "\n";
         if (Util.Engine.trident) {
@@ -75,14 +68,9 @@
                 if (down && !inMove) {
                     inMove = true;
                     lastPos = {'x': x, 'y': y};
-                    cleanRect = {
-                        'x1': viewport.x,
-                        'y1': viewport.y,
-                        'x2': viewport.x + viewport.w - 1,
-                        'y2': viewport.y + viewport.h - 1};
                 } else if (!down && inMove) {
                     inMove = false;
-                    dirtyRedraw();
+                    //dirtyRedraw();
                 }
                 return;
             }
@@ -101,7 +89,13 @@
             var deltaX, deltaY, x1, y1;
 
             if (inMove) {
-                viewportMove(x, y);
+                //deltaX = x - lastPos.x; // drag viewport
+                deltaX = lastPos.x - x; // drag frame buffer
+                //deltaY = y - lastPos.y; // drag viewport
+                deltaY = lastPos.y - y; // drag frame buffer
+                lastPos = {'x': x, 'y': y};
+
+                display.viewportChange(deltaX, deltaY);
                 return;
             }
 
@@ -111,130 +105,38 @@
             }
         }
 
-        function viewportMove(x, y) {
-            var v = viewport, c = cleanRect,
-                vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1,
-                deltaX, deltaY, w, h;
-
-            //deltaX = x - lastPos.x; // drag viewport
-            deltaX = lastPos.x - x; // drag frame buffer
-            //deltaY = y - lastPos.y; // drag viewport
-            deltaY = lastPos.y - y; // drag frame buffer
-            lastPos = {'x': x, 'y': y};
-
-            if ((deltaX < 0) && ((v.x + deltaX) < 0)) {
-                deltaX = - v.x;
-            }
-            if ((vx2 + deltaX) >= fb_width) {
-                deltaX -= ((vx2 + deltaX) - fb_width + 1);
-            }
-
-            if ((v.y + deltaY) < 0) {
-                deltaY = - v.y;
-            }
-            if ((vy2 + deltaY) >= fb_height) {
-                deltaY -= ((vy2 + deltaY) - fb_height + 1);
-            }
-
-            if ((deltaX === 0) && (deltaY === 0)) {
-                //message("skipping");
+        function dirtyRedraw() {
+            if (inMove) {
+                // Wait for user to stop moving viewport
                 return;
             }
-            message("deltaX: " + deltaX + ", deltaY: " + deltaY);
 
-            v.x += deltaX;
-            vx2 += deltaX;
-            v.y += deltaY;
-            vy2 += deltaY;
+            var d = display.getCleanDirtyReset();
 
-            // Update the clean rectangle
-            if (v.x > c.x1) {
-                c.x1 = v.x;
-            }
-            if (vx2 < c.x2) {
-                c.x2 = vx2;
-            }
-            if (v.y > c.y1) {
-                c.y1 = v.y;
-            }
-            if (vy2 < c.y2) {
-                c.y2 = vy2;
-            }
-
-            if (deltaX < 0) {
-                // Shift viewport left, redraw left section
-                x1 = 0;
-                w = - deltaX;
-            } else {
-                // Shift viewport right, redraw right section
-                x1 = v.w - deltaX;
-                w = deltaX;
-            }
-            if (deltaY < 0) {
-                // Shift viewport up, redraw top section
-                y1 = 0;
-                h = - deltaY;
-            } else {
-                // Shift viewport down, redraw bottom section
-                y1 = v.h - deltaY;
-                h = deltaY;
-            }
-            if (deltaX !== 0) {
-                canvas.copyImage(0, 0, -deltaX, 0, v.w, v.h);
-                canvas.fillRect(x1, 0, w, v.h, [255,255,255]);
-            }
-            if (deltaY !== 0) {
-                canvas.copyImage(0, 0, 0, -deltaY, v.w, v.h);
-                canvas.fillRect(0, y1, v.w, h, [255,255,255]);
+            for (i = 0; i < d.dirtyBoxes.length; i++) {
+                //showBox(d.dirtyBoxes[i], "dirty[" + i + "]: ");
+                drawArea(d.dirtyBoxes[i]);
             }
         }
 
-        function dirtyRedraw() {
-            var v = viewport, c = cleanRect,
-                vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1;
+        function drawArea(b) {
+            var data = [], pixel, x, y;
 
-            if ((c.x1 >= c.x2) || (c.y1 >= c.y2)) {
-                // Nothing clean, redraw everything
-                drawArea(0, 0, v.w, v.h);
-            } else {
-                // Redraw dirty regions
-                if (v.x < c.x1) {
-                    // redraw left side dirty region
-                    drawArea(0, 0, c.x1 - v.x, v.h);
-                }
-                if (vx2 > c.x2) {
-                    // redraw right side dirty region
-                    drawArea(v.w - (vx2 - c.x2), 0, vx2 - c.x2, v.h);
-                }
-                if (v.y < c.y1) {
-                    // redraw top/middle dirty region
-                    drawArea(c.x1 - v.x, 0, c.x2 - c.x1 + 1, c.y1 - v.y);
-                }
-                if (vy2 > c.y2) {
-                    // redraw bottom/middle dirty region
-                    drawArea(c.x1 - v.x, c.y2 - v.y, c.x2 - c.x1 + 1, v.h - (c.y2 - v.y));
-                }
-            }
-        }
-
-        function drawArea(x, y, w, h) {
-            message("draw "+x+","+y+" ("+w+","+h+")");
-            var imgData = ctx.createImageData(w, h),
-                data = imgData.data, pixel, realX, realY;
+            //message("draw "+b.x+","+b.y+" ("+b.w+","+b.h+")");
 
-            for (var i = 0; i < w; i++) {
-                realX = viewport.x + x + i;
-                for (var j = 0; j < h; j++) {
-                    realY = viewport.y + y + j;
-                    pixel = (j * w * 4 + i * 4);
-                    data[pixel + 0] = ((realX * realY) / 13) % 256;
-                    data[pixel + 1] = ((realX * realY) + 392) % 256;
-                    data[pixel + 2] = ((realX + realY) + 256) % 256;
+            for (var i = 0; i < b.w; i++) {
+                x = b.x + i;
+                for (var j = 0; j < b.h; j++) {
+                    y = b.y + j;
+                    pixel = (j * b.w * 4 + i * 4);
+                    data[pixel + 0] = ((x * y) / 13) % 256;
+                    data[pixel + 1] = ((x * y) + 392) % 256;
+                    data[pixel + 2] = ((x + y) + 256) % 256;
                     data[pixel + 3] = 255;
                 }
             }
             //message("i: " + i + ", j: " + j + ", pixel: " + pixel);
-            ctx.putImageData(imgData, x, y);
+            display.blitImage(b.x, b.y, b.w, b.h, data, 0);
         }
 
         function toggleMove() {
@@ -249,36 +151,37 @@
             }
         }
 
-        window.onresize = function() {
-            var v = viewport,
-                cw = $D('container').offsetWidth,
-                ch = $D('container').offsetHeight;
-
-            message("container: " + cw + "," + ch);
+        function detectPad() {
+            var c = $D('canvas'), p = c.parentNode;
+            c.width = 10;
+            c.height = 10;
+            padW = c.offsetWidth - 10;
+            padH = c.offsetHeight - 10;
+            message("padW: " + padW + ", padH: " + padH);
+        }
 
-            if (cw > fb_width) {
-                cw = fb_width;
-            }
-            if (ch > fb_height) {
-                ch = fb_height;
-            }
-            if ((cw !== v.w) || (ch !== v.h)) {
-                v.w = cw;
-                v.h = ch;
-                message("new viewport: " + v.w + "," + v.h);
-                canvas.resize(v.w, v.h);
-                drawArea(0, 0, v.w, v.h);
-            }
-        };
+        function doResize() {
+            var p = $D('canvas').parentNode;
+            message("doResize1: [" + (p.offsetWidth - padW) +
+                    "," + (p.offsetHeight - padH) + "]");
+            display.viewportChange(0, 0,
+                p.offsetWidth - padW, p.offsetHeight - padH);
+        }
 
         window.onload = function() {
-            canvas = new Display({'target' : $D('canvas')});
-            ctx = canvas.get_context();
+            detectPad();
+            display = new Display({'target': $D('canvas')});
+            display.resize(1600, 1024);
+            //display.resize(800, 600);
+            ctx = display.get_context();
             mouse    = new Mouse({'target': $D('canvas'),
                                 'onMouseButton': mouseButton,
                                 'onMouseMove': mouseMove});
 
-            window.onresize();
+            Util.addEvent(window, 'resize', doResize);
+            //doResize();
+            setTimeout(doResize, 1);
+            setInterval(dirtyRedraw, 50);
             mouse.grab();
 
             message("Display initialized");

+ 2 - 2
utils/websocket.py

@@ -295,7 +295,7 @@ Sec-WebSocket-Accept: %s\r
         if has_mask:
             # unmask payload
             f['mask'] = buf[f['hlen']:f['hlen']+4]
-            b = c = ''
+            b = c = s2b('')
             if f['length'] >= 4:
                 mask = numpy.frombuffer(buf, dtype=numpy.dtype('<u4'),
                         offset=f['hlen'], count=1)
@@ -635,7 +635,7 @@ Sec-WebSocket-Accept: %s\r
             # Generate the hash value for the accept header
             accept = b64encode(sha1(s2b(key + self.GUID)).digest())
 
-            response = self.server_handshake_hybi % accept
+            response = self.server_handshake_hybi % b2s(accept)
             if self.base64:
                 response += "Sec-WebSocket-Protocol: base64\r\n"
             else: