Posts mit dem Label OVW werden angezeigt. Alle Posts anzeigen
Posts mit dem Label OVW werden angezeigt. Alle Posts anzeigen

14. Juni 2011

An HTML based Chat Window with WebKit, jQuery, and tricks

Over the weekend I was working on the OVW chat window. The chat window is very similar to the avatar display and also very different. The similarities are:

  • both are display modules
  • both use WebKit
Because they are both display modules, I could take the avatar display module code and strip away the avatar part to keep only nicknames. The chat window needs only nicknames. Later there will be images and more features. But for the start we need only text. The chat module has the same structure as the avatar module. It subscribes to a small number of virtual presence model events and then turns around and paints the display in HTML.


See the ui-layout-* CSS-classes? They are used by jQuery UI Layout. There is a bit of configuration necessary, but only a bit: http://code.google.com/p/ovw/source/browse/trunk/Apollo/bin/Win32/Debug/modules/Chat/Chat.html?r=255#153

As always, styling is very simple. Need bold nicknames? Here they are:
#ChatOut .cNickname { font-weight:bold; }
On the other hand, chat window and avatar display are very different:
  • chat is a dialog where the Chat.html code is in an IFRAME
  • avatar display paints directly, has no dialog frame
Calling the avatar display I can simply call a JavaScript function in WebKit. But in case of a Dialog with IFRAME, things are a bit more complicated. The IFRAME is a HTML window inside the HTML window. The WebView module has only a handle to the outer HTML/JavaScript. There is no direct access to the JavaScript of the inner HTML. I can call a JavaScript function in WebKit, but the function is executed in the context of the dialog wrapper. This is OK, if I want to control the dialog functions. But talking to the inner HTML is more difficult.

I am sure there are way to get to an embedded IFRAME from C++. But I did not want to get too deep into enumerating WebKit objects from C++. I needed a way to call JavaScript in the embedded IFRAME from the parent frame. Luckily we are in the same security domain, so calling JavaScript across frames is no problem. My solution is to call a function in the Dialog, which calls a function in the IFRAME, which evals the code I want to execute in the IFRAME.
function ContentEval(sCode)

{
return document.getElementById('Content').contentWindow.EvalJS(sCode);
}
Not very nice. A bit dirty, but it works. Also not very fast. Anyway, there are not many messages which take this route. Just adding participants and chat lines. I am confident, that we find a better way to do this later.

_happy_wrapping()

UPDATE: a week later, I have direct access to embedded frames. I am searching the frame by it's DOM-ID, then call the function inside the frame. I do GetFrameByPath to find the frame, because the frame may be inside another frame. Frames inside frames will be identified by a "/" separated list of DOM-IDs. That's my so called frame-path: http://code.google.com/p/ovw/source/browse/trunk/Apollo/bin/Win32/Debug/modules/Chat/Chat.html?r=414#200

The GetFrameByPath function is a mess of COM auto-pointers and QueryInterface. The ugly face of C++. Thank FSM someone invented better ways.

7. Juni 2011

More OVW Progress

Another week of evenings and weekend worth of progress on the next weblin generation (OVW). I am now working on browser tracking, support for multiple tabs and the multiple views for the same scene.


1. Firefox Add-on

Unfortunately, the rather simple weblin way of tracking browsers is not sufficient anymore. We want tabs (more about tabs and windows at the OVW blog).

Reliable information about visible tabs is only available inside the browser. So I need real browser addons. I started with Firefox some time ago. The addon keeps track of all tabs, when they open/close, become visible and when they navigate. It sends all the information over a local TCP connection to the client (using a simple remote procedure call protocol). This sounds complicated and it is, because the socket implementation in Firefox has its quirks. I could tell stories. But the beauty of a TCP connection is, that if the browser crashes or disappears, then the client will notice when the TCP connection goes down. It will then leave all rooms automatically, even if the browser addon does not say goodbye.

2. Tracking Browser Coordinates

Unfortunately, I don't get a real window handle (HWND) inside the browser. I need a native component to get a HWND for the tab. Why HWND? because I want to track the coordinates of the tab: resize and move events. I could add a native DLL to the addon, but then the addon is not cross-platform. Since I have a native part anyway (the client), I rather make the platform dependent processing in the client.

I implemented a BrowserInfo-module which finds the tab's HWND in the window list and does GetWindowRect() to get coordinates. The module also arranges the window stacking order with SetWindowPos() so that the avatar display is always directly on top of it's browser. That's good old Win32 coding. While most of the code is cross-platform, this part must be adapted to other operating systems, i.e. MacOS.

3. Model View Controller

I am very happy with the Model-View-Controller architecture. The "view" is only responsible for showing avatars. It can be closed and opened and re-creates everything from the model. The view uses the WebKit and JavaScript heavily.

The virtual presence module manages entering and leaving chat rooms, participants and chat lines. This is the "model". The view gets events from the model when participants enter/leave/chat. The view can always query the model. The model has only data structures. The view creates visible avatars.

And best of all: there can be multiple views for the same model. This means, that 2 browser windows can show the same scene.

_happy_modeling()

3. März 2010

Welcome Neptun

In my spare time I am currently implementing a virtual items management system for the OVW project. During October/November 2009, I programmed a rudimentary item inventory as a Web portal in C#. I am developing on Windows/.NET and run the production server with Linux/mono. (BTW: mono is really great).

Since I had no item server, I implemented a quick web service, which simulated an item server to populate my inventory with dummy items from a dummy web service. This is a view of the inventory as a web page.

The simulator worked directly on the database with lots of SELECTs each time you accessed the inventory page, which shows all items. As we all know, caching helps to take load off the database, but in the case of virtual items, caching is not enough. Virtual items are active. They live. They have timers. They interact, and they change over time. Every single activity invalidates the cache. What virtual items need is an active cache.

That's what I programmed in the last 3 weeks: a specialized cache only for virtual items which can be accessed like a web service via HTTP. The protocol could be SOAP (too heavy) or REST (too unstructured). As you might have guessed, the protocol actually is SRPC.

So, now I have an item server that

  • populates the inventory and
  • serves as an active cache for the database, and
  • has a web service interface.
The thing is called "Neptun". (Yep, I just noticed, that it should be "neptune")

The inventory web page above is for users. Developers can also see items in the item server, but they are much more bare bone there. Neptun has a web user interface. There is a list of item numbers and item properties. Not much for the user, but informative for developers.

Neptun has been developed with lots of unit tests and integration tests (as usual). And here is another proof, that unit-testing is king: when I replaced my dummy item service with the real item server by exchanging just one web service URL, the thing just worked.

_happy_orbiting()

Thanks to Ingo for consulting at the Silpion party.