Thanks to visit codestin.com
Credit goes to csdevteam.blogspot.com

Showing posts with label Delphi. Show all posts
Showing posts with label Delphi. Show all posts

We moved!

Find our new blog here: Cybele Software Blog

Visit our new corporate blog!

Cybele Software’s Development News

We are glad to inform you of our latest Development News:

TN Bridge Host Integration Pack on the new Embarcadero’s Delphi XE
TN Bridge Host Integration Pack is now compatible with the new Embarcadero’s Delphi XE development environment. So now, TN Bridge Host Integration Pack supports Embarcadero’s Delphi 5 through XE, any ActiveX compatible Development Environment and .NET framework and programming languages.

z/Scope for iPad/Android
We are currently extending z/Scope to enable Terminal Emulation access from iPad, tablet devices and smart phones running Apple iOS and Google Android’s OS. We would like to know about your current needs and what features you consider a must for a mobile OS Terminal Emulator. You can send us your comments and suggestions and we’d be happy to evaluate any special need or request you may have.

Web RDP client reaching its final release
Our new Web RDP client it is now getting close to its official release. We are thrilled about the amazing performance it is showing due to the full WebSockets implementation and Video Driver support, among other improvements. We are also planning full Chrome, Firefox and Internet Explorer compatibility and many other enhacements. Stay tunned!

ThinVNC - Digging into the code, Part II

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.  
 Collapse
{"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 purpouses), 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. 
 Collapse
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. 
ProcesWindows function creates or reuses a canvas per window, using the hwnd as part 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.
 Collapse
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.   

VNC got thin! New pure-web remote desktop solution

Cybele Software, Inc. introduces ThinVNC, a new remote desktop solution that boosts and enhances remote technical support and administration tasks, allowing users to remotely access a desktop using just a web browser.

ThinVNC was built from the ground up taking full advantage of AJAX, JSON and HTML5 technologies. By implementing a number of techniques not available in traditional AT&T RFB protocol, it achieves a remarkable performance, rivaling any traditional VNC.

ThinVNC exploits HTML5 canvas to display a remote PC desktop, not requiring any additional plugin, add-on or installation of any kind on the client side. Regardless of the computer or mobile operating system, ThinVNC provides native, secure data transmission using HTTP and SSL.

Users can use the ThinVNC Presentation Manager to invite people to attend online demonstrations or trainings. Running on the web browser, ThinVNC removes the need of a “viewer” download making the attendance of non-tech people smooth and free of any setup issue.

On corporate environments, ThinVNC can be set up as a gateway to proxy other ThinVNC instances. By publishing only one IP, users can remotely get access to desktops on the LAN just by entering the internal IP/PC name in the welcome screen.

ThinVNC Server works on Windows 2000/XP/2003/Vista/Win 7/Windows Server 8. On the client side, ThinVNC is compatible with any Operating System and an HTML5 compliant browser such as Mozilla Firefox, Chrome, Internet Explorer, Opera, Safari, etc.


Recent news (2022):


- ThinVNC has evolved into Thinfinity VNC.

- Full ThinVNC SDK 2.0 can be downloaded, being available on a royalty fee basis. Contact us for pricing and licensing information.
-The old ThinVNC Open Source (version 0.9) must be taken as a technology preview, you can download the latest zip file from Source Forge



ThinVNC - Digging into the code, Part I

Introduction

I found interesting the idea of a pure HTML5 Remote Desktop client for cross-browser cross-platform access to a PC. Despite that there are some AJAX VNC/RFB implementations, I thought in a simpler way to build a remote desktop solution using web standards and simple image processing.

This solution is built using AJAX, HTML5, JSON and simple JPEG/PNG images. The required server-side code is written in Delphi 2010.

The code - Part I

In this first part we'll see the code to do the screen capture. The standard aproach is to capture the whole desktop. However, in this case, we'll capture every window individually, applying clipping regions and saving the individual bitmap for later comparison and difference extracting.

Firstly we need to enumerate all visible top windows:

TWin = class(TObject)
private
  Wnd : Hwnd;
  Rect : TRect;
  Pid : Cardinal;
public
  constructor Create(AWnd:HWND;ARect:TRect;APid:Cardinal);
end;

function EnumWindowsProc(Wnd: HWnd; const obj:TList<TWin>): Bool; export; stdcall;
var ProcessId : Cardinal;
  R,R1 : TRect;
  Win : TWin;
begin
  Result:=True;
  GetWindowThreadProcessId(Wnd,ProcessId);
  if IsWindowVisible(Wnd) and not IsIconic(wnd)then begin
    GetWindowRect(Wnd,R);
    IntersectRect(R1,R,Screen.DesktopRect);
    if not IsRectEmpty(R1) then begin
      win := TWin.Create(Wnd,R,ProcessId);
      obj.Add(win);
    end;
  end;
end;

procedure GetProcessWindowList(WinList:TList<TWin>);
begin
  WinList.Clear;
  EnumWindows(@EnumWindowsProc, Longint(WinList));
end;

We want to keep a list of windows, with their basic attributes and their
bitmaps, so we can compare with the new ones and send the differences to the
client. Here we merge the window list into a list of TWindowMirror:

TWindowMirror = class
private
  FIndex : Integer;
  FRgn : HRGN;
  FHandle : THandle;
  FBoundsRect : TRect;
  FProcessId : Integer;
  FImage : TBitmap;
  FDiffStreamList : TList<TImagePart>;
  ...
  ...
end;

procedure TMirrorManager.RefreshMirrorList(out OneMoved:Boolean);
  procedure GetProcessWindowList(WinList:TList<TWin>);
  begin
    WinList.Clear;
    EnumWindows(@EnumWindowsProc, Longint(WinList));
  end;

var
  wl : TList<TWin>;
  n : Integer;
  wm : TWindowMirror;
begin
  OneMoved:=False;

  wl := TList<TWin>.Create;
  try
    // Enumerates top windows
    GetProcessWindowList(wl);
    try
      for n := wl.Count - 1 downto 0 do begin
        // Looks for a cached window
        wm:=GetWindowMirror(FMirrorList,wl[n].Wnd);
       if assigned(wm) then begin
        if IsIconic(wl[n].Wnd) then
           wm.SetBoundsRect(Rect(0,0,0,0))
       else wm.SetBoundsRect(wl[n].Rect);

          // Returns true when at least one window moved
       OneMoved:=OneMoved or (DateTimeToTimeStamp(Now-wm.FMoved).time<MOVE_TIME);
        end else begin
         // Do not create a TWindowMirror for invisible windows
         if IsIconic(wl[n].Wnd) then Continue;

       wm:=TWindowMirror.Create(Self,wl[n].Wnd,wl[n].Rect, wl[n].pid);
       FMirrorList.Add(wm);
        end;
        // Saves the zIndex
        wm.FIndex:=wl.Count-n;
        // Generates clipping regions
        wm.GenRegions(wl,n);
      end;
    finally
      ClearList(wl);
    end;
    // Sorts the mirror list by zIndex
    FMirrorList.Sort;
  finally
    wl.free;
  end;
end;

Finally we do the capture:

function TWindowMirror.Capture(ANewImage:TBitmap): Boolean;
  function BitBlt(DestDC: HDC; X, Y, Width, Height: Integer; SrcDC: HDC;
                   XSrc, YSrc: Integer; Rop: DWORD): BOOL;
  begin
    // Capture only visible regions
    SelectClipRgn(DestDC,FRgn);
    result:=Windows.BitBlt(DestDC, X, Y, Width, Height, SrcDC,
                           XSrc, YSrc, Rop);
    SelectClipRgn(DestDC,0);
  end;

var
  DC : HDC;
  RasterOp,ExStyle: DWORD;
begin
  RasterOp := SRCCOPY;
  ExStyle:=GetWindowLong(FHandle, GWL_EXSTYLE);
  if (ExStyle and WS_EX_LAYERED) = WS_EX_LAYERED then
  RasterOp := SRCCOPY or CAPTUREBLT;

  DC := GetDCEx(FHandle,0,DCX_WINDOW or DCX_NORESETATTRS or DCX_CACHE);
  try
    Result:=BitBlt(ANewImage.Canvas.Handle,0,0,
    Width(FBoundsRect),Height(FBoundsRect),DC,0,0, RasterOp)
  finally
    ReleaseDC(FHandle,DC);
  end;
end;

Now that we have captured all visible regions we need to get the bitmap
differences against the previous capture. We do this by looping through the
windows, then their visible regions and finally calculating the regions where we
find bitmap differences:

function TWindowMirror.CaptureDifferences(reset:boolean=false): Boolean;
  ....
begin
 ...
 result:=Capture(TmpImage);
 if result then begin
   ...
   ra:=ExtractClippingRegions(Rect(0,0,TmpImage.Width,TmpImage.Height));
   for n := 0 to Length(ra) - 1 do begin
     ra2:=GetDiffRects(FImage,TmpImage,ra[n]);
     for m := 0 to Length(ra2) - 1 do begin
       Jpg := TJpegImage.Create;
       ...
       CopyBmpToJpg(Jpg,TmpImage,ra2[m]);
       FDiffStreamList.Add(TImagePart.Create(rbmp,'jpeg'));
       Jpg.SaveToStream(FDiffStreamList[FDiffStreamList.Count-1].FStream);
       ... 
       Bitblt(FImage.Canvas.Handle,
       ra2[m].Left,ra2[m].Top,Width(ra2[m]),Height(ra2[m]),
       TmpImage.Canvas.handle, rbmp.Left,ra2[m].Top,SRCCOPY);
     end;
   end;
   ...
end;

On the next post we'll focus on the protocol with the client and the client code.

HTML5 remote desktop open source

You can download the full source code from Source Forge.


UPDATE: There's a commercial version with PRO features available here.



 

An HTML5 Remote Desktop implementation

VNC and Remote Desktop software (RealVNCTightVNCUltraVNC, MS Remote Desktop, etc), where around for a long while now. Undoubtedly they work great for most of purposes, I wanted to bring the old VNC / Remote Desktop concept over the web, to allow full web-application integration and full cross-browser cross-platform support out-of-the-box.

ThinVNC is an attempt to this: a pure web remote desktop access application HTML5 based, supported for the majority of web browsers and operating systems.

Despite its name, ThinVNC is not a traditional VNC, as it does not implement the AT&T RFB protocol. Instead, it rests on today's web standards: AJAXJSON and HTML5.



Recent news (2022):


- ThinVNC has evolved into Thinfinity VNC.
- Full ThinVNC SDK 2.0 can be downloaded, being available on a royalty fee basis. Contact us for pricing and licensing information.
-The old ThinVNC Open Source (version 0.9) must be taken as a technology preview, you can download the latest zip file from Source Forge