Slowly but surely...

on Monday, June 25, 2007
... things end up working.

The last blog entry spoke of a successful embedding of xulrunner into a mono winforms app, on windows, and I had hoped that one or two days later I would be blogging about it working on linux as well. Unfortunately, it turned out not to be as simple as that. It actually turned out to be really complicated.

First, while on windows I only had to feed xulrunner with the windows hwnd handle and voilá, it worked, on linux xulrunner uses gtk by default. The xlib xulrunner would be quite nice to use, as it would hopefully be just like windows, feed it a winforms handle and that's that. Unfortunately, the xlib xulrunner code has been bitrotting for quite some time, and would not work at all. :/

The Gtk adventure


Getting gtk to use a winforms handle to draw in was quite an interesting challenge in itself - it was essentially the same as getting gtk to draw in an x window (since winforms handles are essentially xid's). There are a couple of different ways to go about the problem; either use the concept of socket/plug by calling gtk_plug_new on a xid and then adding widgets to that to later pass on to xulrunner to draw in, or using gdk_window_foreign_new to "import" the xid.

gtk_plug_new creates a new GtkWidget, while gdk_window_foreign_new gives you a GdkWindow. The first would seem to be easier to get into xulrunner, as it requires passing in a GtkWidget, but it also requires reparenting the window (because the plug doesn't actually do that, it assumes the xid has a socket attached - which it hasn't, since it comes from winforms).
The second gives you a GdkWindow, which doesn't actually help much when you really want a GtkWidget and simple code. I ended up building a custom widget to wrap around an X handle, following the line of the gtkwin32embed widget that comes with gtk, so I went for the second option. The widget works wonderfully, no need for extra reparenting, no need for top level windows (since the widget is a window), just create it with the xid and you're in business! :)

Winforms application calling the new xulbrowser library to display an
embedded xulrunner engine (mozilla)
From Xulbrowser

Those pesky initializations

What really made me bang my head on the walls was the fact that no matter what I did, the browser seemed to stop in the middle of loading and just stay there indefinitely! I did everything to the code; debugged it, traced it, #ifdef'd it, went step by step checking every call, every event, everything, and nothing, it refused to work! That damned loading icon kept mocking me!

To cut a long story short, after spending hours and hours and days and days on it, I finally found out that the problem was, I wasn't initializing a thing called AppShell. This AppShell (which is a frozen interface, but is not actually a part of the sdk - so you'd think it wasn't all that important, hmm?) initializes gtk on the browser so the browser widget can receive events (you'd think one gtk initialization was enough, sheesh!...)

It also seems to be an absolutely useless interface on windows, since the original code works perfectly fine and it actually catches events without it. So it's a linux only thing, as far as I can tell. Completely undocumented. Unless, of course, someone tells me that it was perfectly obvious that it needed to be there, since gtkmozembed uses it. Of course, gtkmozembed also uses a ton of private calls that are supposedly completely off-limits to us external embedding folks and that would probably earn me a ton of griefing if I actually thought of using them, but hey, I'm supposed to know the difference between an off-limits-never-use-or-we'll-send-the-mob-after-you calls, and absolute-mandatory-linux-only-initialization-calls-on-non-sdk-interfaces... right?

It's a good thing I'm a certified nutcase and I actually have fun solving these little problems. And it's not like I don't have solid walls or anything. :)

So, for your amusement, here's a couple of shots (ok, ok, one here and one above) of a Mono Winforms application running a control with an embedded xulrunner-powered browser inside.

Gratuitous use of Xgl to show off my nice Suse desktop with a Mono Winforms
app embedding a mozilla browser (phew!)
From Xulbrowser

A little browser glue

on Monday, June 11, 2007
It's a real pity I didn't have this to blog about yesterday, it would be a really nice way of celebrating Portugal day (which was yesterday, June 10th :) yay for us!). If it weren't for some pesky bugs yesterday...

Anyway, great blog-worthy news today, got the Mono WebControl -> Gecko thing going, and it's alive and drawing! Check out this screeny of the mozilla engine rendering a webpage inside Mono...




Now where did I put my type?

Today I ran across an interesting problem while trying to export a function from a dll built in vc++ (2003). I've been digging into the mozilla source a lot lately, doing the library to embed mozilla and get our much-needed webcontrol working on Mono, and after a successfull browser window invocation from .net (screenies to come soon) (woohoo), I decided to clean up the code a bit and start doing the functions properly.

The first functions of the library were only returning ints, but for the glue to work properly, I needed to return a pointer to the newly created class in C++, and since cross-platform is good, all the function exports got marked with mozilla's NS_EXPORT_(type). While I had int as a return type, all was well, but when I decided to return void*... *boom*!

Ooops!

Looking at the exported functions of the dll, all those marked with void* or similar (anything with *, really) were not there. Everything was happily compiled... they just weren't exported. :p

Cue 2 hours of googling and mucking about with the export syntax and going absolutely nowhere.

Now note, please, exhibit A - the NS_EXPORT_ typedef:

#define NS_EXPORT_(type) type __declspec(dllexport) __stdcall

Note, also, as exhibit B, an example signature that jchambers gave to me on the #mono channel:

__declspec(dllexport) void* STDCALL myfunc();

Notice anything?

Well, apparently, if you put the type before the __declspec, all functions with pointer return types will not be exported. If you put the type between the declspec and the stdcall, they are exported.

>.<

I ended up doing this:

#ifdef VSTUDIO
#define NS_EXPORT_(type) __declspec(dllexport) type __stdcall
#endif

*sigh*

Of course, I could be doing something really wrong here... if anyone can set me right, feel free! I'll just go back to doing some useful now :)