Click here to Skip to main content
Licence GPL3
First Posted 13 Aug 2010
Views 14,865
Downloads 696
Bookmarked 33 times

An HTML5 Remote Desktop - Part II

By | 13 Aug 2010 | Article
A Remote Desktop software built on AJAX, JSON and HTML5

Introduction

In the first part of this article, I showed how to do the screen capture, window by window and, using clipping regions and bitmap comparison, how to build a list of changed bitmap regions to send to the client. In this second part, we'll see how to send all this information to the client.

The Code (Part II)

The following JSON is an example of a desktop capture:

{"status:":1,"desktopWidth":1280,"desktopHeight":800,
	"cursor":"default","cursorX":241,"cursorY":525,
 "windows": [ { "hwnd":"196724","zidx":1,"desktop":"Default",
	"left":0,"top":0,"width":1280,"height":800},
    { "hwnd":"8521744","zidx":2,"desktop":"Default","left":364,
	"top":59,"width":806,"height":667},
    { "hwnd":"8129930","zidx":3,"desktop":"Default","left":-8,
	"top":-8,"width":1296,"height":776},
    { "hwnd":"1247020","zidx":4,"desktop":"Default","left":244,
	"top":28,"width":1023,"height":728},
    { "hwnd":"8785068","zidx":5,"desktop":"Default","left":-8,
	"top":-8,"width":1296,"height":776,"imgs": [ 
	{ "x":8,"y":8,"w":1280,"h":123,"img": 
	"data:image/jpeg;base64,/9j/4......" }]},
    { "hwnd":"6033806","zidx":6,"desktop":"Default","left":-1,
	"top":479,"width":426,"height":22,"imgs": [ 
	{ "x":1,"y":0,"w":425,"h":22,"img": 
	"data:image/jpeg;base64,/9j/4AAQ......" }]},
    { "hwnd":"196708","zidx":7,"desktop":"Default","left":0,
	"top":760,"width":1280,"height":40,"imgs": [ 
	{ "x":53,"y":0,"w":1227,"h":40,"img": 
	"data:image/jpeg;base64,/9j/4AA......" }]},
    { "hwnd":"131186","zidx":8,"desktop":"Default","left":0,
	"top":760,"width":54,"height":40,"imgs": [ 
	{ "x":0,"y":0,"w":54,"h":40,"img": 
	"data:image/jpeg;base64,/9j/4AAQSkZJ......" }]}]]} 	 

The "windows" array contains all the visible windows on the desktop. Each item contains the window handle (for identification purposes), its bounding rectangle, the desktop to which it belongs, its relative z-order and an array of images. Each image corresponds to changed region on that specific window. If there are no changes, the "imgs" array doesn't exist.

function reload() {
    scale = getScale();
    var url = baseUrl + "json?id=" + sessionStatus.id;
    clearTimeout(jsonTimeout);
    jsonTimeout = setTimeout(onJsonTimeout,jsonTimeoutValue);
    $.getJSON(url, function (obj) {
        try {

            $.each(obj.windows, function (i, win) {
                processWindow(win);
            })

            for (var i = deskDiv.children.length - 1; i >= 0; i--) {

                var found = false;
                var canvas = deskDiv.children[i];

                $.each(obj.windows, function (i, win) {
                    var canvasid = "canvas" + win.hwnd;
                    if (canvas.id == canvasid) {
                        found = true;
                    }
                })
                if (!found) {
                    canvas.style.display = "none";
                    canvas.innerHTML = '';
                    deskDiv.removeChild(canvas);
                }
            }
        }
        catch (err) {
            if (sessionStatus.active) {
                setTimeout(reload, 1);
            }
        }
    });
}

For each "window" item received, the processWindow method is called, creating or reusing a canvas element. All canvas elements belonging to windows that aren't present in this JSON correspond to windows that have already been closed. These are erased on the "for (var i = deskDiv.children.length - 1; i >= 0; i--)" loop.

ProcessWindows function creates or reuses a canvas per window, using the hwnd as part of its id. It also sets the canvas coordinates and its zindex according to the received information, and iterates over the images array to copy them on the canvas surface.

function createCanvas(win) {
    var canvas = document.createElement("canvas");
    
    canvas.visibility = 'visible';
    canvas.display = 'block';
    canvas.style.position = 'absolute';
    canvas.style.left = (win.left-sessionStatus.viewLeft)+'px';
    canvas.style.top = (win.top-sessionStatus.viewTop)+'px';
    canvas.style.zIndex = win.zidx;
    canvas.width = deskDiv.offsetWidth;
    canvas.height = deskDiv.offsetHeight;
    canvas.id = "canvas" + win.hwnd;
    deskDiv.appendChild(canvas);
    return canvas;
}

function processWindow(win) {
    var canvasid = "canvas" + win.hwnd;
    var canvas = document.getElementById(canvasid);
    if (!canvas) {
        canvas = createCanvas(win);
    }

    deskDiv.style.marginLeft = getDeltaX() + 'px';
    deskDiv.style.marginTop = getDeltaY() + 'px';
    if ((win.width == 0) || (win.height == 0)) {
        canvas.style.visibility = "hidden";
        canvas.style.zIndex = -1;
    } else {
        canvas.style.left = (win.left-sessionStatus.viewLeft) + 'px';
        canvas.style.top = (win.top-sessionStatus.viewTop) + 'px';
        canvas.style.clip = 'rect(0px,' + win.width + 'px,' + win.height + 'px,0px)';

        canvas.style.visibility = "visible";
        canvas.style.zIndex = win.zidx;
    }
    
    if (win.imgs != null) {
        var context = canvas.getContext('2d');
        if (!context || !context.drawImage) {
            alert("no hay canvas");
            return;
        };

        $.each(win.imgs, function (i, imgpart) {
            var img = new Image();
            img.id = "imgcanvas";
            img.style.display = "none";
            img.onload = function () {
                context.drawImage(img, imgpart.x , imgpart.y, img.width, img.height);
            }
            img.src = imgpart.img;
        })
    }
};  

The updated source code can always be found here.

History

Part I of this article can be found here. ThinVNC is currently beta software.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

About the Author

Gustavo Ricardi

Chief Technology Officer
Cybele Software, Inc.
United States United States

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionReverse connections? PinmemberRBJensen1:35 23 Aug '10  
AnswerRe: Reverse connections? PinmemberGustavo Ricardi2:23 23 Aug '10  
GeneralRe: Reverse connections? PinmemberRBJensen2:59 23 Aug '10  
GeneralRe: Reverse connections? PinmemberGustavo Ricardi9:02 23 Aug '10  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120517.1 | Last Updated 13 Aug 2010
Article Copyright 2010 by Gustavo Ricardi
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid