diff options
author | Joel Martin <github@martintribe.org> | 2011-09-13 10:14:53 -0500 |
---|---|---|
committer | Joel Martin <github@martintribe.org> | 2011-09-13 10:14:53 -0500 |
commit | 9192cbe39c51e092a9353af29b2f22383aeedb78 (patch) | |
tree | 54ee60b321f305f86e33fe8af4b481e1203145ee | |
parent | 6ea8bece9b1299f51739e29dfce7c8033af58602 (diff) | |
parent | 54e7cbdf8f50d3a77e70cb25491d9c2b5d40ae46 (diff) | |
download | novnc-9192cbe39c51e092a9353af29b2f22383aeedb78.tar.gz |
Merge branch 'master' into mobile
Conflicts:
include/rfb.js
-rw-r--r-- | include/display.js | 220 | ||||
-rw-r--r-- | include/mobile.css | 30 | ||||
-rw-r--r-- | include/rfb.js | 4 | ||||
-rw-r--r-- | tests/viewport.html | 239 | ||||
-rw-r--r-- | utils/websocket.py | 4 |
5 files changed, 296 insertions, 201 deletions
diff --git a/include/display.js b/include/display.js index d7aa43f..4f37cd1 100644 --- a/include/display.js +++ b/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; }; diff --git a/include/mobile.css b/include/mobile.css index 8ff7a54..86f65ff 100644 --- a/include/mobile.css +++ b/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; +} diff --git a/include/rfb.js b/include/rfb.js index 928500a..04f69de 100644 --- a/include/rfb.js +++ b/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)); diff --git a/tests/viewport.html b/tests/viewport.html index b9225f4..9dcb53f 100644 --- a/tests/viewport.html +++ b/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"); diff --git a/utils/websocket.py b/utils/websocket.py index e906b7e..8194914 100644 --- a/utils/websocket.py +++ b/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: |