monday, 27 july 2009

posted at 19:25 | comments
tags:

The last couple of months have been busy but I've managed to find bits of time here and there to hack on the new AROS hosted port. Last week I really got the guts of the task switching and interrupt code working the way I wanted, which is what I'm here to tell you about today.

Task switching in a typical multitasking system is very simple in concept. Imagine a computer running a single task. There's a big pile of instructions in memory somewhere, and the processor just runs them in sequence. It will keep doing that until something stops it. That something is the most important requirement to make preemptive multitasking work.

What usually happens (again in very simple terms) is that there's an extra bit of circuitry somewhere in the computer that works as a timer. Every now and again (though tens or hundreds of times a seconds), it will prod the CPU. In response, the CPU will stop what its doing and go and run a different bit of code somewhere else in memory. The "prod" is known as an interrupt (or Interrupt Request (IRQ)), and the bit of code that runs is the interrupt handler (or more formally, the Interrupt Service Routine (ISR)). Its the handler's job to arrange for a different task to run.

Something the CPU will do when responding to the interrupt is to save its complete state (known as the context) before it calls the handler. That is, somewhere in memory (typically on the stack) it will save a copy of all its registers, the stack pointer, the program counter and everything else it needs to continue running the program from where it was stopped. This is necessary as the handler will need to use those registers in order to do its work. Many CPUs provide a single instruction to restore the entire CPU state in one go.

To make task switching work, the interrupt handler will take a copy of the context and store it inside the OS task state, which usually contains lots of other info about the running task, such as memory it has allocated, files it has open, etc. Then, the handler chooses another task based on some criteria (this is the scheduler). Finally, it copies the saved context from the state of the task to wherever the CPU needs it, then tells the CPU to reload the context and leave the handler. The handler "returns" to running the newly selected task. This process contiues ad infinitum and you get the illusion that your computer is doing lots of things at the same time.

The existing Unix-hosted version of AROS does fundamentally the same thing, but in a highly convoluted way. The main thing to note is all tasks run inside a single Unix process, which then does some deep magic with Unix signals to make interrupts and task switches are happening. The kind of magic employed is highly OS-specific, and although I don't know exactly why it was done the way it was, I can guess that it was one of:

  • The facilities for user-space task switching weren't available or were incomplete when it was first written (I know this was the case for Linux)
  • Originally AROS was much more tightly integrated with the Linux desktop (eg one AROS window per X11 window, etc)

Times have changed though, and so what I'm trying to do is make a new port that is designed to be much closer structurally to its native cousins. I'm realising this through a number of mechanisms provided by POSIX: threads, signals and the ucontext set of functions (though somewhat ironically these have been removed from the latest versions of POSIX and SUS).

What I do is this. I create a thread to mimic the function of the timer interrupt delivery circuit. It sits in a tight loop, waiting a little while then sending a signal to the "main" thread. This obviously mimics the the interrupt that would exist on a real system, and causes the main thread to stop what its doing and jump to a signal handler.

When a signal is delivered to a Unix process, the kernel saves the current process state (context) onto the stack and then calls a signal handler function. When the handler returns, the kernel reloads the state from the stack and continues from where it was. This sounds like almost exactly what we want, except Unix typically doesn't provide a portable way to get at the saved state on the stack. The existing hosted AROS implementation for Linux uses a bunch of Linux-specific knowledge to dig into the stack and get the data it needs, but thats obviously not portable. These days however, we have the ucontext functions which, while not without their quirks, are far more useful.

The prototypes look like this:

  • int getcontext(ucontext_t *ucp);
  • int setcontext(const ucontext_t *ucp);
  • void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
  • int swapcontext(ucontext_t *oucp, ucontext_t *ucp);

For those who've seen setjmp() and longjmp() before, getcontext() and setcontext() will be quite familiar in function. getcontext() takes a copy of the current process state, including the CPU context, and drops it into the memory pointed to by ucp. setcontext() restores the process state and CPU context from whatever is saved in in ucp, effectively causing a direct jump to the point just after the getcontext(). What this means is that you get the appearance of setcontext() never returning, whereas getcontext() can return multiple times. Interesting times indeed.

makecontext() takes an existing context and modifies it such that when setcontext() is called on it it will jump to func with the arguments specified on the on the stack. You actually need to do a bit of fiddling inside ucp before calling it, to setup an alternate stack for the context to run on and so forth. For the most part this call is not particularly useful except when setting up.

Finally, swapcontext() is an atomic context get-and-set. That is, it does this:

getcontext(oucp);
setcontext(ucp);

except that a later setcontext(oucp) will return to the point after the call to swapcontext().

Armed with this knowledge, we can now take a look at the (slightly simplified) implementation. The task switch "interrupt" handler, is a two-stage process. The first part, which as far as the Unix kernel is concerned is the actual signal handler, looks like this:

ucontext_t irq_ctx;
char irq_stack[SIGSTKSZ];

void irq_trampoline (int signo, siginfo_t *si, void *vctx) {
    getcontext(&irq_ctx);
    irq_ctx.uc_stack.ss_sp = (void *) irq_stack;
    irq_ctx.uc_stack.ss_size = SIGSTKSZ;
    irq_ctx.uc_stack.ss_flags = 0;
    makecontext(&irq_ctx, (void (*)()) irq_handler, 0);

    swapcontext((ucontext_t *) GetIntETask(SysBase->ThisTask)->iet_Context, &irq_ctx);
}   

(irq_stack is initialised during startup as irq_stack = malloc(SIGSTKSZ))

So the signal from the timer thread arrives, and the current task gets interrupted and we arrive here. The getcontext() and makecontext() bit sets up a new context that, when called, will call the actual interrupt handler (ie the scheduler etc) and select a new task.

Its the call to swapcontext() that is most interesting. What this does is save the current context into the current task structure, and switch to the interrupt handler proper. The handler calls into the scheduler to choose another task then calls setcontext() on its saved context to start it up. The subtlety is in the fact that when the saved context is later used to start the task up again, it will return to the point just after the call to swapcontext(), immediately drop off the end of the signal handler and head back to where it was.

You might wonder why the more obvious method of using getcontext() to save the context then calling the scheduler directly isn't used. The problem comes from the fact that when getcontext() "returns", the caller has no way of knowing if it was the initial call to save the context, or if it was as a result of setcontext() being called. Without this knowledge, we're left to this kind of trickery so that the only time we end up after the context being save is when the context is reloaded.

(This is the opposite of setjmp(), which returns zero from its initial call and non-zero after a call to longjmp(). It perhaps makes the code easier to read to just have a call and test to determine what to do next, but its slightly slower and it would also result in the handler being run on the task stack, which means making the handler more complicated to make sure it rewinds correctly when the task is switched back. Or tricks can be played with sigaltstack(), which further complicates things.

The actual implementation is naturally a little more complicated, mostly because it has to deal with so-called "system calls", which is what happens when an application triggers a task switch (eg by calling Wait()). To allow that, each interrupt signal carries a numeric id that allows the trampoline and handler to determine what type of interrupt was requested. Then, when Exec wants to force a task switch, it will trigger the interrupt requesting it, which will make the scheduler with the main task "stopped", as above, but with slightly different semantics. It doesn't add much code though, and the technique is identical.

There's still lots to be done to clean up the scheduler, which so far is a hack job of the hack job already present in the mingw32 port. The next thing to do is continue to work on the boot sequence, which is almost there but is just a tiny bit finicky at the moment (that's a technical term). Next time I think I'll write about the new host module setup which blows hostlib.resource out of the water (if you know what that is)!

monday, 29 june 2009

posted at 22:09 | comments
tags:

My current bus activity is AROS hacking. I've actually been doing at least an hour a day for the last couple of months, so I'm making plenty of progress, but I'm off on a long and exciting tangent so it all seems quite different to what I was doing before.

I started thinking about what it would take to make cairo a "first-class" graphics system, sitting directly on top of the graphics drivers, bypassing graphics.library completely. This isn't a crazy idea - a major part of graphics.library is providing drawing and font rendering primitives, similar conceptually to what cairo does (though cairo is of course far more advanced). My thought is that we make the graphics system at the bottom of the stack for apps do all sorts of crazy compositing and whatever other eyecandy effects, and the whole desktop benefits. Initially it could operate alongside graphics.library, but it'd also probably be reasonable to implement graphics.library functions on top of cairo at some later time.

From there I started looking at the graphics driver API. What we have works well enough (despite the deficiencies that I've complained about in the past), but its not a particularly good fit to the cairo backend API, and from what I understand, not a great match for a modern 2D hardware interface either. So the next thing I started thinging about was to change the graphics drivers to have the exact same interface as the cairo backend API. From there, a driver and/or the hardware could directly accelerate cairo drawing operations. The cairo software fallbacks are pretty heavily tested and optimised (including some tight assembly versions of things where necessary), so I'd expect that even a graphics card or whatever that doesn't offer a lot of function could still go faster than, say, the current SDL driver (which uses the graphics.hidd fallbacks for just about everything currently).

So now I'm looking at drivers. As you know, I work in hosted, so my two examples are the X11 and SDL drivers. Something I hate about the X11 driver is how closely tied to the underlying kernel implementation. I took some steps to deal with this when I wrote the SDL driver with hostlib.resource, but its not perfect, and lately something has changed in the X11 driver to require it to be linked with the kernel once again. Besides that, the X11 driver is ancient, hailing from a time where AROS windows were X11 windows, and it retains a lot of that structure even though its no longer the way the world works. Also, it relies on the X11 "backing store" feature, which is usually disabled and will shortly be removed from Xorg. In short, the thing needs a rewrite.

So yay, rewriting one, maybe two, graphics drivers. Down a level to figure out what's going on the core, and sure enough, more work required there. In the last few years the structure of an AROS kernel has changed to be a minimal kernel.resource which implements the absolute minimum required to initialise the memory and task-switching hardware and hand control to exec.library. The loader (typically GRUB) can optionally get whatever modules (libraries, resources, devices, etc) into memory and make them available to exec when it starts. This is the basic idea behind the so-called "modular kernel", which has been implemented in the x86_64, Efika, SAM (both PPC), and more recently, mingw32 ports. The only ports that don't do this are the first two - Linux hosted and i386-pc.

The mingw32 port is particularly interesting. Its a hosted port to Windows, and in essence uses the OS threading system to implement a minimal virtual machine, all within kernel.resource. It has a small bootloader that loads an ELF kernel, making it so that stock AROS i386 code can be used even on Windows which doesn't use ELF itself. The other thing it does is neatly split modules into host-side and AROS-side parts. The AROS parts are handled as normal modules, but in their initialisation they call into hostlib.resource (which is now contained within kernel.resource) to load and link the host-side part. These are standard shared libraries (ie DLLs) which can bring in any library dependencies they need, neatly avoiding the problem contained within the X11 and SDL drivers in that its kinda painful to find the needed libraries at runtime. This way, you just find what you need at link time.

And so, after all this, I'm doing a new port of AROS to Linux, based on the structure used for the mingw32 port. I'm improving on it a bit though. There's still too much arch-specific code in exec.library (like thread context manipulation) which I'm hiding inside kernel.resource. I'm also adding a host.resource which will provide ways for modules to hook into the system main loop inside kernel.resource to do things like "virtual" hardware and the like (ie faking interrupts and such). The mingw32 port did this via special architecture-specific calls in kernel.resource, but I want to try to make kernel.resource have a standard interface across all ports, so they can all run an exec.library that is substantially the same.

So that's some kind of plan. I'm currently at the point where the kernel.resource boots and gets exec.library online. The next thing I need to do is reimplement my task switching and interrupt core which I never tested. If you feel like googling something, it turns out that ucontext_t is not particularly easy to copy or cache on Linux due to the Linux people messing up the way they store the floating point state. I need to rewrite it based on the wonderful context_demo.c example, which never requires an explicit context copy and should do much better. After that I should be able to hook DOS up and get something interesting happening.

I'll keep working and maybe let you know some more in another month or two :)

sunday, 28 june 2009

posted at 20:42 | comments
tags:

Yeah, its been a while. I'm still here, and I've done heaps of stuff since last time, but I just haven't gotten around to writing about it yet. I'll get there.

What I'm here for tonight is to tell you about something new. I know there's people out there blogging about AROS. I'm subscribed to a few of them myself. I'm sure I haven't got all of them though. So I'm putting together a planet to list them all:

If you're trying to follow what's going on with AROS, it'll be good for you to subscribe to this planet, as you'll find out everything that's going on. If you're blogging about AROS, it'll be good for you to be on this planet, as you make sure that everyone is reading your stuff and you benefit from other people's popularity.

If you write about AROS, email me (rob@cataclysm.cx) or ping me on IRC (fce2 on irc.freenode.org). Let me know the location of your RSS or Atom feed, and I'll add you. Its cool if you have non-AROS stuff in there, this is about AROS people as well as AROS itself.

If this gets big and popular, I'll see what I can do to get a better URL. How does planet.aros.org sound? :)

Oh, and I need to do something to pretty it up a bit. If you feel like doing something there, drop me a line.

sunday, 3 may 2009

posted at 22:49 | comments

Long ago I wrote a SDL driver for AROS hosted. Back then I wrote about it being slow because of deficiencies in the driver interface that require flushing the output to the screen for every pixel plotted by software fallbacks. Go and read that first.

I never did finish my implementation originally, but in the last week I've resurrected the branch and completed it. Its taken adding an UpdateRect method to the bitmap class and then modifying graphics.library to call it after ever operation. If its running a tight loop to paint a rectangle or something, it will call this once when its finished to push its output.

To test, I removed all the "accelerated" methods in the SDL bitmap class, leaving only GetPixel and PutPixel. Back when I first writing sdl.hidd this was all I had implemented, and it worked fine, but was slow enough that you could watch the individual pixels appear on the screen. With the UpdateRect stuff its now very usable. Its not blinding fast, but its snappy enough to be comfortable.

And the best thing is that no changes are required to existing graphics drivers. For those, the call to UpdateRect will just use the baseclass version, which is a no-op. I've confirmed this is actually the case with the X11 driver, so yay.

I'm not sure what's next for my hacking. I'm really just studying graphics.library and graphics.hidd at the moment, trying to get my head around how it all fits together. Something big is coming, I'm just not sure what it looks like yet :)

tuesday, 28 april 2009

posted at 08:39 | comments
tags:

It would appear I'm back in the AROS game for a little while. I got a nice email asking for some help with fat.handler so I decided that I'd look into it. In the last 18 months a few things have been broken in things that I care about which were causing my particular configuration to fail to build, so I had to get into the code to fix them. While doing this I started to remember that I actually quite like hacking on AROS and miss it. That and my brain seems ready for a challenge again.

Of course this time around, I'd like to avoid the frustrations that contributed to me quitting last time. So this is my plan:

  • I will only work on things that interest me
  • I will not work for money
  • I will not take on significant commitments (ie "sure, I can take a look at that bug" is ok, but "sure, I'll write you a browser" is not)
  • I will not get involved in any political stuff like arguments about project governance, goals (backward compatibility) or anything else

The last point is key. There was a few times previously that I had to do things the wrong way just so that backwards compatibility would be maintained, a goal that I never agreed with. This time, I won't be arguing about it, I'll just be doing what I want to do. Its a light fork, if you like.

I've got a new repository set up over at repo.or.cz. "cake" is what I'm calling my mini-project for now. I'll be committing everything I do there, as well as keeping the AROS mainline there (manually updated as necessary). I will commit things to the AROS Subversion repository as appropriate, but when I do something that causes significant breakage then it will live here. In true open source fashion, anyone who wants my stuff can get it from me and build their own, or if demand gets high, maybe I'll provide some builds or something. We'll see.

So here we go, the brave new world. I'm great at changing my mind, so we'll see how long this lasts :)

friday, 23 may 2008

posted at 12:10 | comments
tags:

I'm in the process of putting a heap of code thats just sitting around on my laptop into git repositories. To make my life easier I've moved all my AROS stuff into a subdirectory. So if you're looking for one of the AROS repositories or you've cloned from me, you'll need to change paths. As usual, cgit lights the way.

monday, 28 april 2008

posted at 22:17 | comments

There's no easy or amusing way to say it, so I'm just going to say it. My involvement in AROS, including Traveller and the nightly builds, ends right now.

Over the last few weeks I've been doing a few different things. I played lots of Morrowind, started work on a couple of brand new projects, played lots of the new Advance Wars game that I got for my birthday, read the new Ben Elton book, and a few other things. I've enjoyed every part of it. I've been doing lots of different things, stretching my brain in different ways, and not been beholden to anyone. Since I'm happier, work has been much better, home has been much happier, everything just seems good.

The whole time though there's been a tiny nagging voice in the back of my head. Thats the one that has been telling me that I need to get on with Traveller. Only a couple of months to go. I hate that voice. I've tried a number of times to get into it, but I've only added about twenty lines of code to the loader in that time.

The fact is that I'm just over it all. Every part of AROS that I was interested in I've done enough work on to learn as much as I'm interested in. I wrote a filesystem. I wrote a graphics driver. I ported some minor apps. I hacked on some libraries. Its the same for Traveller. I got it to a point where you could browse the web. Everything else is just a bonus - in these areas, there's not really much left to take my interest.

I've been over this before. This is a major part of my reason for planning to leave after Traveller. But I really started thinking about why its so difficult for me to get motivated. The question I eventually got to was "would I be trying to finish this if there wasn't a nice prize in it for me?". And the only answer I had to that was "no".

That was a rather enlightening moment. I'm a little bit ashamed of myself actually, but not surprised. I've known since forever that money is not really a motivator for me, it never has been. I think I just got a bit dazzled by the possibilities; large amounts of spare cash don't come my way too often and there's at least one neat gadget that I've been hankering for.

So all in all, I have no compelling reason to continue. I realise I've made commitments, and I hate breaking them, but I've made other commitments in my work and personal lives, and I can't do them all, so I have to choose. Once I really looked at it seemed to be a fairly straightforward choice.

It shouldn't take long to remove myself. I've already managed to offload nightly build duties, as there were some issues and recent updates that I've been rather tardy in sorting out and so someone else offered to take the build on. I'll email TeamAROS shortly to let them know that I'm ditching the bounty. I guess I'll spend a little time during the week responding to email and that should about take care of it.

For anyone who wants to take on WebKit and make a browser, feel free to take the code I've already done. Its all available via my git repository under free licenses. Do contact me if you need help; while I'm not working on stuff and won't be paying a lot of attention the current goings on, I'm quite happy to offer support and advice on specific issues.

Finally, thanks to all the nice people in and around the AROS community. I've had a great time getting to know you and working with you. I've no doubt that we'll see each other around the internets from time to time, and I'll drop into #aros when I can too.

This blog isn't closing up shop, of course. Once I'm back from playing games and reading I'll likely be back writing about whatever I end up working on next. Current interests are DS hacking, binary decompilation and RPGs. By the time I write something they may not be interesting anymore in which case you'll get to read about something else :)

sunday, 9 march 2008

posted at 09:24 | comments
tags:

I started writing a long post about what I'm working on right now, but its really quite disjointed because I realised I don't actually have a point to make. So here's the short version of what I've been doing this week.

To be really useful, WebKit needs to be a shared library. On AROS, we can't support this in the normal way because of issues with global variables. The solution involves large-scale changes to the program loader and execution code.

AROS executables are actually ELF relocatable objects rather than executable objects. This is done so we can relocate programs on the fly without needing a MMU. To implement ELF shared libraries properly though, we need the extra information provided by ELF executables as they contain (among other things) the dependency list.

What I'm doing is to make AROS executables be ELF shared objects, containing both the relocation information and the dependency list, as well as other stuff. This requires a new loader for this object type, but I'm taking the opportunity to merge the existing ELF loaders since there's a lot of overlap of functionality.

Once shared object "executables" are available, I can begin implementing the library side of things. These are essentially the same thing, except that they will be position-independent, and so the loader will have to deal with setting up the GOT and PLT. The tricky bit arranging for each instance of the library to find its GOT. I'm still wrapping my head around that.

Once thats done, we'll be able to Unix-style .so libraries in addition to our standard ones. Not long after, we'll have a properly-sharable WebKit.zcc, with pretty things like libcairo.so and so forth.

I'll post more as I have time, progress and proper brain to describe it.

sunday, 2 march 2008

posted at 12:21 | comments

I have a little treat for the adventurous today.

AROSWebKit-preview-r30597-20080302.zip [8.1M]

Its mostly unusable, but many many people have requested a demo. Its still quite difficult to build it from the source, so here it is.

This will crash your system. No support of any kind is offered, but feedback is welcome. Send some mail or nab me in IRC :)

monday, 25 february 2008

posted at 15:34 | comments

A week later:

The major new things compared to my last post are the addition of the page title (and progress bar), the URL entry bar, and scrollbars. The last one is the thing thats been killing me for the last week, and I'm happy to finally have it over and done with.

What you don't see is that most of the details of integrating WebKit with Zune so that it can request and control native UI widgets. At its core, WebKit is a layout engine. It takes a UI description (in the form of HTML, CSS, etc), creates a bunch of objects, positions them relative to each other and then draws them. Sometimes (eg for a HTML form) rather than handling an object internally, it instead asks the host UI system to handle creating and drawing the object instead. When it does this, however, it expects to have full control over where the object is placed.

Zune allows a custom class to provide a function that will be used to position the widgets rather than using its own internal algorithms. This I have written. All it does is loops over the list of native widgets, asked WebKit what their dimensions are, and then tells Zune how it should draw them. Its the easy bit in all of this.

A typical Zune object is rendered in three parts. Upon receiving a request to render an object, Zune first asks the object about its dimensions, and receives back the minimum and maximum possible sizes it can use, and its ideal size. The object's parent object sets an appropriate size within the ranges and positions it in relation to itself, and then asks the object to do the same for its children, if it has any (most simple widgets do not). Finally, once the object knows its position and everything else is done, it is asked to draw itself in that space. This rendering process happens based on some external trigger, such as the window being opened or resized.

The complication arises from the order that things are done in this process, and when the process is triggered. Once its size is determined, a Zune object is asked to layout its children, if it has any, via MUIM_Layout. Once done, MUIM_Show is called to tell the object it is about to be displayed. Finally MUIM_Draw is called and the object is drawn.

Lets think about what really needs to happen to render a page, and how Zune conspires against us. I'll start by describing the obvious implementation of this mess, which is what I had before this week. In the beginning, we have a pristine WebKit_View object, with no drawing in it and no child widgets. Lets assume though, that WebKit has already loaded a page internally, because the initial page load has a couple of extra twists and this description is already complicated enough.

At the moment the application window appears (or the view is added to the UI in some other way), the magic begins. The view is asked for its dimensions, which are typically "as much as you can give me". Next, the view is asked to lay itself out via MUIM_Layout. This is actually a private method, and not one we're supposed to override, so we let that go through to the view's superclass, Group. It gets its list of sub-widgets, finds it empty, and so does nothing.

Next, MUIM_Show is called on the view. This is the first time the view knows the exact dimensions it has been given by the window, and so we tell WebKit the new dimensions and ask it to layout the page based on this size. Once thats done, the window calls MUIM_Draw, which sets up a cairo context over the view area of the window and tells WebKit to draw into it.

The cake is a lie.

If WebKit, during its layout phase, determines that it needs native UI widgets (form elements, scrollbars, etc), it asks the Zune to create them and add them to the view. Unfortunately, at this point the Zune object layout has already been done (we're in MUIM_Show, which runs after MUIM_Layout), so the new widgets have not been asked about their size, have not been placed on the page, etc. MUIM_Draw fires, the view asks WebKit to draw the page and then calls the supermethod to draw the widgets. These unitialised widgets all get drawn with no dimensions at the top-left of the view. This is not what's wanted.

At this point some way of forcing the entire layout process to run again is necessary. This is harder than it should be. You can't just call MUIM_Layout, even if it weren't a private method, because the new widgets have not yet been queried for their sizings. There appears to be no standard way of forcing the layout process to run. In the end I've abused a feature of the Group class to do what I want. The usual way you'd add widgets to a group is to call MUIM_Group_InitChange on the group, followed by one or more calls to OM_ADDMEMBER or OM_REMMEMBER. Once done, a call to MUIM_Group_ExitChange "commits" the changes by making the whole window relayout and redraw from scratch. To force the layout to happen, I simply call InitChange followed by ExitChange with no widgets added in between.

(Coincidentally, I used to use these methods when adding the widgets to the group in the first place, but stopped because it was causing a redraw every time. Now I simply use OM_ADDMEMBER and OM_REMMEMBER and assume that the layout and draw will be done elsewhere, which is correct conceptually).

The one chink in this method is that ExitChange eventually causes all three stages of the render process to run - sizing, layout and draw. We're already inside the layout section, and so we don't want everything to run again. Specifically, we don't want this secondary render process to cause WebKit to do another layout, and we don't want it to draw either, as that will be handled by the original render process. Some flags in the view object to record and detect this reentrancy are all that's required. So the final process becomes:

  • Render process triggered
  • (internal) setup widget dimensions
  • (MUIM_Layout) widget layout (ignored)
  • (MUIM_Show) WebKit layout
  • (MUIM_Show) force second render process
    • (internal) setup widget dimensions
    • (MUIM_Layout) widget layout
    • (MUIM_Show) WebKit layout (ignored)
    • (MUIM_Show) force second render process (ignored)
    • (MUIM_Draw) draw everything (ignored)
  • (MUIM_Draw) draw everything

Do you see what we did there? We just bent the Zune render process to our will by turning it inside out :) There's a couple of other warts thrown in to the mix to deal with a couple of edge cases, but thats basically it. You can read the dirty details in webkit_view.cpp.

Now I have no idea if this is compatible with real MUI. MUIM_Layout is actually private in MUI, but public in Zune, so I wouldn't be able to override it there, but the override could probably be done well enough in the custom layout function. I'm not overly concerned if its not compatible; I'm not developing for MUI after all, but I am curious.

This all points to what I believe is a fairly major design flaw in MUI, that being that the stages of the render process are fairly tightly coupled. There should be a direct a way to force a single object to relayout itself from scratch, and doing it without triggering a redraw. There should be a way to get dimensions recalculated. I suppose its not unreasonable that these things can't be done directly as its probably not often that an attempt is made to bolt an entirely seperate layout engine to it. I suppose it is a testament to MUI's flexability that I can twist it like this at all.

Next up is to get the scrollbars hooked up to the page. After that is the RenderTheme implementation which gives all the other widgets necessary to view pages with forms. A little input handling after that and then we'll have something usable on our hands!

monday, 18 february 2008

posted at 11:03 | comments

A couple of hours work on yesterday's effort, and we see this:

Had I known just how close I was, I probably wouldn't have even bothered posting yesterday.

The wonky text was because a stupid assumption I made in cairo's font code, which I've now fixed. The text still looks crap, mostly because of issues with the renderer, but I've been pointed at TTEngine this morning which looks much more like what I want and would let me remove some of the hacks I've had to do in cairo. I'll be looking at this further this week.

There's still a hell of a lot to do, so don't get too excited. At least now I have a way to see whether or not my changes are actually doing something or not.

I'll be posting many more screenshots as work progresses, but I won't be blogging them all. Things are moving just too fast for that. If you want to follow the screenshots, watch my screenshots set on Flickr or just subscribe to its feed.

sunday, 17 february 2008

posted at 09:46 | comments

Current progress:

This is WebKit on AROS rendering a trivial page containing a H1, a H2, a DIV with CSS styles set to force to 100x100 with a green background, and a IMG of a pirate, though thats not working yet.

The text alignment appears to be screwy because my code in cairo is not correctly calculating the baseline on tall glyphs. It works as expected from my cairo tests though, so I'll need to dig a lot deeper to figure this out. Likely I just missed some mundane detail; font metrics are actually quite difficult and I'm not help by the fact that the bullet interface doesn't provide away to get the metrics for the font as a whole, meaning I have to generate them in a rather horrible way.

There's also an issue where if a line of text is wrapped (eg if I resize that window to be really narrow), only the last line is rendered. I still haven't looked into that yet. Oh and of course there's a bunch of internal stuff that really isn't correct but won't noticeably affect the outcome just yet.

All in all, not bad progress so far. Its only going to get more difficult as I really get into the details, I think. Not to mention the many many shortcomings in services provided by AROS, which are going to need to be addressed if this thing is to look nice and not be insanely slow. I'll write more about that lot later.

monday, 11 february 2008

posted at 09:07 | comments
tags:

AROS work has slowed down over the last week. There's been a lot of email to reply to (I won't mention the topic :P), and I've doing some web work on the side, but I've still had a little time to work on cairo, which I'm now calling finished, at least for the moment.

The big thing I was trying to get going was the shared library stuff; ie having a shared cairo.library. I got this working, but programs are crashing because cairo has globals, a fact that I'd overlooked. It only has a couple, but they're rather important. Once again, I'm not willing to make the large changes required to remove the globals because I want to keep the changes to upstream to an absolute minimum. Once AROS has proper support for global data, then this code can be resurrected.

So cairo works, but is noticably slow. That mostly comes from it doing all its rendering in software and then blitting the results into the AROS graphics subsystem. Working slowly is good enough for me at this stage.

Fonts work, with the following issues:

  • Non-scale transformations (rotate, shear, etc) don't work as the necessary API is not exposed via bullet. Scaling work, buts only vertically - again, missing API. Basically the only cairo API that is of any use for glyph transformation is cairo_set_font_size().
  • The font tests don't pass. The first reason for this is that font sizing on AROS is not the same as on other systems. As far as I can tell the bullet interface to FreeType is recalculating the metrics to better match the traditional Amiga way of talking about metrics, with the downside that it makes the glyphs smaller than they should be. Additionally, there's no way to get the display device DPI under AROS, making it quite impossible to have FreeType adjust the scale appropriately.
  • The other reason the tests don't pass is that spacing between glyphs is wrong. A typical line of test rendered on Linux will have pretty much even spacing between each glyph. The same text rendered on AROS has uneven widths. I haven't been able to determine the cause of this yet.

The font problems shouldn't be an issue for WebKit as it does its own font work, though it will still hit the underlying font system so its likely the same issues will appear in other contexts. Again, I'll just do the best I can.

So this afternoon its back to WebKit! There's been many many changes there in the last month, so the first step will be to just get my stuff building again.

friday, 1 february 2008

posted at 22:28 | comments
tags:

As you know, I've been at linux.conf.au this week. There's a lot of cool stuff happening in the Linux world, and a few of those things really made me ache to grab the code and get hacking on them. But even more than the technology, the best thing about Linux is the community. Even when there's disagreement (and there's plenty) the feeling is wonderful because everyone is working hard on the same thing: making computers awesome.

A shortlist of things I'd like to work on:

  • Martin Krafft's netconf network configuration framework. His design is elegant and this is something that Linux badly needs.
  • Rusty Russell's lguest hypervisor which is just beautiful in its simplicity. I've already done some real hacking on this in the tutorial and its very pleasant to work on. I had a chat with Rusty about adding support for gdb stubs (because I like that kind of thing) and it looks like it could be added quite easily. That sort of gratification is hard to come by. Plus I'm feeling happy because I won the prize in the tutorial for the most progress made (four targets reached in two hours). Its some kind of Brazilian liquor called Chico Mineiro that I'm looking forward to trying at the next gaming night.
  • cairo is still outstanding and from its requirements have come some major redesigns of the 2D graphics core in X and below. By the time the wizards are done with it cairo (and others) will be able to get better performance out of 2D graphics hardware than any other platform (Windows included). This stuff is harder to get into but is by no means impossible.
  • The GNOME crew have got some fascinating stuff coming down the pipe that I'd really enjoy working on. Its mostly integrating different types of application to better support social interactions (ie convenient sharing your stuff), which is something I've always had an interest in.
  • I've been gifted an OLPC XO-1. In the immediate future I've decided to let Francesca at it and document her progress, as a kind of observation project. The thing about these machines is that the are purpose-built for sharing and working with others, and the interface breaks all the rules and thus gives heaps of scope for trying new things. Whether she gets sick of it and hands it back or I buy one for myself so that we can play with them together, there's lots I'd like to do with it.

So there, lots of stuff I could do that I'd thoroughly enjoy, that would produce real stuff that would be used on real computers by lots of real people, and that would keep this community buzz alive for me.

On the other side, there's AROS. Now I like AROS because its technically interesting and there's lots of stuff to fix, but previously I didn't really have anything better to do. I still like AROS, but I've found myself this week doing a lot of soul-searching, trying to decide if AROS hacking is really the best use of my time. As I look at what's happening this at LCA this week, its increasingly apparent that AROS, when held up against just about everything else, is insignificant.

I don't have any delusions about AROS ever becoming a mainstream system, and thats fine, because it doesn't need to be to still be considered successful. In order to be successful, it needs a clear plan and goal moving forward (so we can actually measure our progress), and it needs a strong community of developers around it committed to that goal.

As it stands, we have none of that. The community, such that it is, is fractured, which is unsurprising since its a part of the Amiga community and we all know just how much infighting there is and has always been there. In terms of goals, there basically are none. There are those that would argue that "AmigaOS 3.1 compatibility" is the goal, which I'd answer by either saying we're already there since most source from the 3.1 era will compile and work with no or only minor tweaks, or that the goal is irrelevant since there's nothing from the 3.1 era you'd want anyway.

If we are to be a clone, then we're still a long way away - AROS can't even run on real Amiga computers! We're incompatible in a number of ways, but those ways are only important for binary compatibility, which we don't have. On the other hand, if you have the source, perfect compatibility is not really an issue as you can modify the application for the differences. But like I said above, there's nothing from the old days thats worth bothering with.

In the absence of real goals, I set my own personal goal for my work on AROS, which is to get it to a point where I could run it as the primary OS I use day-to-day on my laptop. That's a huge task, as my laptop is something close to an extension of my brain. AROS would need to at least be able to do the following to supplant Linux there:

  • Web browser
  • SSH client
  • Fully xterm-compliant console
  • Stable and fast filesystem
  • X server (for remote applications)
  • Perl
  • Proper support for my laptop - wireless, idling, suspend, etc
  • Some way to run certain Windows apps (like VMWare, qemu, etc)

It should be clear that there's more to it than just this list - a massive amount of work needs to happen under the hood to support all this.

As you can see, my aims are very forward looking, and make no provision for backward compatibility. This is causing some problems as I try to progress things. An example is my recent work on cairo. AROS graphics APIs are broken in the way the handle certain things related to alpha channels. Unfortunately this can't be changed without breaking backward compatibility. As such, I've implement a particular fix in four different ways over the last two weeks. The first three have introduce compatibility issues and I've had to remove them. I'm hopeful that the current one will not introduce any further issues, but I hoped that last time. Even if it does stick, I still needed a pretty nasty and performance-degrading hack in cairo to finally get what I wanted.

Obviously, this is frustrating. Perhaps it wouldn't be so bad if everyone was at least trying to move forward, just breaking as little as possible in the process (something I agree with), but there is an entire camp that appears to want backward compatibility at the expense of all else.

If I haven't been clear yet, I don't think that this is a bad goal. I have no issue with people wanting things that are different to what I want. The problem that I have in this case is that I don't see that the two positions can ever be reconciled as they're fundamentally opposed.

So I'm frustrated anyway, and then I go to a conference and hear and see amazing things by focused and motivated hackers, and I get even more frustrated because I want what they have. I want to work with these people on code that matters with the confidence that we're all moving in the same direction. This is why I'm starting to wonder if AROS is such a great place for me to be.

I've had some discussions in #aros about this, and the idea of forking the project often comes up. I've considered this in the past, but I've so far resisted for a few reasons. From the practical side its a pain because I'd have to setup the website, repository, etc and do admin stuff, write a lot of email, write a plan and other project governance stuff. Socially it always sucks to split a community. I'm starting to think that if I want AROS to move forward, I may not have much option.

The important thing that would have to happen before a fork is to very clearly determine what I want not-AROS to be. I think "modern" and "Amiga-like", or perhaps "Amiga-inspired" are probably the simplest ways to describe where I think things should go, but we have to specifically define those terms. "Modern" is pretty straightforward: the goal should be that if I put not-AROS on my computer, it will make efficient and effective use of my multiple processors, my piles of RAM, my wireless controller, my USB devices, etc. I should be able to use my computer to do any task that I can do currently in Linux or Windows. That of course requires a lot of applications to be written, but there should be nothing inherent in the system that prohibits those applications being made.

"Amiga-inspired" is a little more difficult to define. I've asked a few of the AROS crowd, and nobody seems to really be able to quantify it, which I find surprising since they're usually the advocates for it and came from Amiga in the old days. Perhaps its one of those cases where its difficult to define what you know because its been obvious for so long.

I don't have an Amiga heritage, coming from Unix as I have, so perhaps I can do better. Since I have no issue with changing the internals, we should start by looking at the Amiga from a user perspective. The major thing is that the system is designed to be GUI-driven, and as such the primary interface is a light and functional GUI. Unix of course is the other way around, where the command line reigns supreme.

The next major thing is the fast booting time. An Amiga system was typically ready for use within seconds of starting. Interestingly, if you measure the boot time as being from the time when the bootloader first hands control to the system to the time when the primary interface can be used, Linux actually only takes a few seconds too. The standard Unix boot sequence generally readies all system services before giving control to the user, whereas the Amiga was more likely to load things it needed on-demand. This made sense given the small amounts of memory available to the system, but that does not mean that its not a good model even for a modern system (though more options exist given the available resources, like starting services in anticipation of their use).

Much of this is enabled by the extremely light microkernel architecture. There's so little structure that system processes actually run much closer to the metal than they wood on other systems. I'm not sure how sustainable this would be as more features and system services are added, but neither have I had much chance to think about in detail. I see no particular reason why it couldn't be kept light if it was always being considered at every stage of development.

So to summarise, not-AROS would:

  • Boot fast
  • Assume a GUI (but see below)
  • Not keep stuff around that isn't needed
  • Keep the microkernel vibe
  • Let you do what you want without getting in your way

A word about the GUI. I'm a command line junkie. I type fast but am really uncoordinated when it comes to using the mouse. So my personal requirement (and I get to have them if its my project) is that everything you can do in the GUI you can do via the command line, and vice-versa. That requirement is fairly straightforward to achieve by seperating function from form - guts in a library, with UI of any type that calls it. Remotely controlling GUI applications is also something that Amiga has a history of with AREXX ports and the like.

And so then we get to backward compatibility. The fact is, I don't care. My not-AROS would not be an Amiga clone. It would try to follow roughly those points above but would be happy to break the rules when they don't work. It would aggressively steal from other systems past and current, both in ideas and in code. Additionally, once implemented, I would not be afraid to gut the internals and redo it if it became clear that we did it wrong the first time.

So there's high-level goals. They're deliberately nonspecific, which is what you want at that level. For the actual development cycle, I'd probably aim for regular releases (depending on available developers) each focusing on one or two specific areas. There'd be no nightly builds. You either get the source and build it yourself, or you wait for a release. I have ideas already about what I'd work on and change and in what order, but I'm not going to write about that here because the tasks are actually somewhat irrelevant.

From where I sit right now, AROS is in an untenable position. In my opinion it cannot get to where I think it could by continuing to be managed the way it is.

What will I do? For now, I'm committed (and still enjoying) my work on WebKit and cairo. I will complete the Traveller bounty. At that time, I'll consider my options, which will be three:

  • Abandon AROS development altogether and go and work on Linux stuff, and enjoy myself, but always wonder what might have been.
  • Continue work on AROS and likely continue beating my head against the wall until I finally explode.
  • Fork AROS and see what happens with the high likelihood that it will go nowhere and wast a lot of my time, with the guarantee that a good amount of my time will be sent managing the project rather than writing code.

What would be great would be if the AROS crowd managed to make a hard decision one way or the other before I have to decide properly. It won't happen, but it still would be very nice.

So thats it. Thats about the sum total of my thinking this week. If you're going to add a comment, please make a good argument for or against what I've said. This is actually a serious post, and I'm not interested in hearing from the fanboys this time around. If you post "I agree!" or "don't ruin AROS for everyone!", expect to have your comment deleted. And if you are going to disagree, make sure your have a pretty solid argument to back up your position because you'll be wasting your time if you don't - I've agonised over this stuff this week and I'm quite sure of my own position.

tuesday, 29 january 2008

posted at 10:12 | comments
tags:

I'm at linux.conf.au this week and because I'm so well practiced at listening to people talk while doing something unrelated on the laptop (thanks dayjob), I've got a hell of a lot of code done, making up for the nothing I did over the weekend.

Yesterday I finally got text rendering happening via cairo:

There's not really a lot to say about it. The hardest part has been converting the glyph metrics that come back from the bullet glyph engine into cairo's glyph metrics, as they haven't a slightly different view of the world.

The code is still rather messy and incomplete. I still have to handle the font matrix which will allow arbitrary scalings, rotations, etc. Smarter font selection is needed as well as using the algorithmic emboldening/shearing stuff to provide fonts that don't exist. At least its all downhill from here.

tuesday, 22 january 2008

posted at 13:29 | comments
tags:

Things got a little slow in the last week. I spent last week tweaking bits of graphics.library and graphics.hidd to force the alpha channel to be set when calling ReadPixelArray() on a surface that has no alpha (so it can be fed directly to a cairo surface with alpha). Each attempt worked, but also introduced subtle incompatibilities into the Cybergraphics API. I still think its important to have (along with software alpha compositing, which is an entirely seperate issue), but it can't be done comfortably via the current API, so for now I just post-process the returned pixel data and force the alpha channel on before handing it to cairo. I don't like it, but it will do, and it makes it possible to use any system bitmap as a source. So now you can use cairo to take a snapshot of the entire screen with this simple code:

    struct Screen *screen = LockPubScreen(NULL);
    cairo_surface_t *surface = cairo_aros_surface_create(&screen->RastPort, 0, 0, screen->Width, screen->Height);
    cairo_surface_write_to_png(surface, "snapshot.png");

I've now turned my attention to the font backend. Its taken me a while to even begin to understand it, because I know basically nothing about font rendering, but I think I'm at least starting to see what's going on. I began an implementation based on the graphics.library functions for font rendering, but it really felt wrong as the interface really doesn't seem to support much - very few facilities for controlling rendering options, limitation to ASCII, etc. It seemed that there must be something more powerful available, as its clear from just loading up AROS that we support TrueType fonts and non-ASCII characters.

After a lot of digging, I found out about the existence of the bullet.library interface for outline fonts, and our implementation of it in freetype2.library. From there, to Google, where I discovered that there's next to no documentation out there for it. I did find reference to a document in the AmigaOS 3.1 development kit, and a quick ask-around in #aros gained me a copy of BulletLibrary, which I offer here for reference.

The interface is complicated, but appears to have most of the features I need to map to cairo font functions. I have no idea how it will go, and I imagine our implementation is deficient, but I will write some tests this afternoon and see what I can do with it, then start hooking it up to cairo.

friday, 18 january 2008

posted at 20:06 | comments
tags:

As far as cairo is concerned, its backend buffer get/set methods are only required to store and retrieve pixel data in the format requested by the cairo core. It does not have to do fancy conversions. It does not have to do alpha stuff. Presumably you'd want it be convertible to the host graphics system, but cairo itself doesn't care about that.

wednesday, 16 january 2008

posted at 12:21 | comments
tags:

Cairo is working! So far I have RGB and ARGB surfaces working, and so still have alpha-only surfaces and fonts to do, but that is enough to make the majority of the test suite work. I actually had the basics working on Thursday, but the colours were all messed up, and it took five days to track down all the issues and fix them. I won't go into the process, because its peppered with dead ends and misunderstandings, but here's what I've learnt:

  • CyberGraphics is a big-endian interface. That is to say, when you request ARGB, you will always get the same byte ordering on little and big-endian machines. This is different to cairo, where specifying ARGB will get you the ordering of the local machine. What this means is that on little-endian machines when converting from AROS bitmaps to cairo surfaces, I have to request BGRA format from ReadPixelArray() but then tell cairo its ARGB, and vice-versa.
  • When AROS converts from a bitmap with no alpha channel (eg RGB) to one with alpha (eg ARGB24), it will set the alpha in the target bitmap to 0 (fully-transparent). When feeding the target into cairo, which knows about alpha, it basically does nothing as it sees that all the pixels are fully transparent. I've already done a rather naive fix in AROS for one case, but there's still a case where the graphics library, realising that a conversion from a non-alpha format to a 32-bit with-alpha format is requested, rewrites the target format to be 32-bit no-alpha (eg 0RGB), thus leaving the alpha set to 0 again. I'm working on a more generic fix.
  • WritePixelArray() has no support for software alpha compositing. That is, when using it to blit a 32-bit with-alpha bitmap to another bitmap without alpha, the alpha component is ignored rather than computed in software. Ironically, alpha compositing code exists for WritePixelArrayAlpha(), so I'll also be looking at factoring this code out into a generic function and having both calls use it.

Once I get this sorted, I have a very cute piece of eyecandy in the works to demonstrate to you all just how powerful cairo is, and just how easy it is to use. Hopefully I'll have something to show in a few days, then I'll get back onto the font support.

monday, 14 january 2008

posted at 12:23 | comments

A year ago today I made my first commit to the AROS Subversion repository. It feels like I've been doing this forever, not only a year. I've been digging back through the history to see what I've been up to over the last year.

Here's a list of things I've done that I think are worth noting:

  • 21 January: tap.device network driver for Linux hosted AROS
  • 4 March: DOS packets and initial FAT filesystem support (minimal read-only)
  • 27 April: FAT write support
  • 7 May: Fast bitmap scaling, made Wanderer startup faster and made FFE usable in hosted and boosted it from 8 to 20FPS in native
  • 16 May: FAT notifications
  • 20 May: PA_CALL and PA_FASTCALL taskswitch and lock-free message ports for speed
  • 8 June: GetDeviceProc() and ErrorRerport() rewrite and internal DOS refactoring
  • 17 June: Pipe() DOS function
  • 21 September: hostlib.resource for calling host libraries from inside AROS
  • 2 October: Converted X11 driver to use hostlib and moved it out of ROM
  • 3 October: SDL driver
  • November (and ongoing): C/POSIX library improvements
  • 17 November: Math library upgrade
  • 3 December: thread.library
  • 13 December: ELF loader support for large objects

There's also a pile of tweaks and fixes that don't feature in this list. According to git, I've made 269 commits to the core AROS repository, adding 23182 lines and removing 12741 lines.

In addition to this, I've got plenty of work-in-progress stuff that hasn't (or won't) hit the AROS repository:

And of course, the Traveller-related work:

2008 should be a bit more focused for me, as most of the first part of the year will be working on getting Traveller out the door, and then on a few big supporting things like SysV shared object support. I don't think it'll be any less interesting as a result :)

Thanks to everyone who has helped and guided me through the many many mistakes I've made, particularly the freaks in #aros. The major reason I'm still here and wanting to work is that is fun, nothing more. Cheers lads :)

wednesday, 9 january 2008

posted at 09:17 | comments

With the help of WebKit developers I finally sorted out the crasher that plagued me over Christmas, and now I see WebKit making network requests, receiving data and calling into the graphics code to get it on screen. The next step is to begin implementing this graphics code.

As far as I can tell I need support for both blitting objects (like images) to the screen, but also need drawing primitives, both simple stuff like lines, circles and rectangles as well as complicated things like Bézier curves and arbitrary paths. It needs to be able to apply a transformation matrix to both paths and images. It needs compositing support. It also needs to be able to operate on screens of arbitrary size and depth.

AROS (and the standard Amiga API) can't support this. Some of it exists, just not enough. graphics.library has basic drawing primitives but not advanced stuff like splines and such. Its primitives don't operate reliably on TrueColor screens, which is what pretty much everything is these days. CyberGraphics provides access to higher-depth modes, but only really for blitting. And we have no support for affine transforms, compositing, or other advanced features.

To Commodore's credit, its pretty clear that they were moving in this direction. They had these concepts on the market in a time where they were barely even considered elsewhere. I'm quite sure that were they still around today we'd have these features available. Sadly, we don't, so we must find another way.

I've studied the problem in some depth, and I've decided to port the cairo graphics library to AROS. Their description sums it up well enough:

The cairo API provides operations similar to the drawing operators of PostScript and PDF. Operations in cairo including stroking and filling cubic Bézier splines, transforming and compositing translucent images, and antialiased text rendering. All drawing operations can be transformed by any affine transformation (scale, rotation, shear, etc.)

A port will be a good thing for everyone. WebKit already has a cairo graphics target, so I'd get my rendering for free. The library is extremely portable, with a great target abstraction. Indeed, I already have the thing compiling and the AROS backend stubbed.

More controversially, I think cairo could actually become the core 2D graphics system for AROS. graphics.library could be trivially implemented on top of it for compatibility, so there's nothing to worry about there. We'd implement a cairo backend that talks to a stripped-down version of our graphics HIDD layer (as much of their functionality would no longer be necessary). Once it place it would give easy support for eyecandy like real transparent windows or something like Exposé. Combine that with the plan to get 3D via Gallium, and AROS could become the shiniest thing out there.

My port will be a proper AROS-style shared library, cairo.library. Cairo's code is clean enough that I think I can do this without requiring the API to change and while still making it possible to contribute all the changes upstream without adversely affecting them.

Port repositories: cairo and pixman. These will be combined in the final library.

monday, 7 january 2008

posted at 15:41 | comments

Christmas and New Year festivities are over, and I enjoyed them thoroughly. I spent some awesome time with both sides of my family, played some cricket and soccer, played some Wii, ate way too much several times, and scored a nice pile of DVDs and t-shirts. In the long drives between various parties and dinners I've had a lot of time to ponder a WebKit problem, which I document here :)

WebCore has some functions that arrange for a simple timer to be implemented. Its very basic; there's three functions: one to set a function to call when the timer goes off, one to set an absolute time that the timer should go off, and one to disable the currently set timer. This simple interface is used by the higher-level Timer class which can be instantiated multiple times. It handles coordinating the current timers and making sure the system timer is requested at the proper interval.

I did a first implementation of this using timer.device directly, but it really didn't feel right. The interface has no provisions for initialising or finalising the timer, so I hacked it such that the first call would open the timer device if it wasn't already open. I ignored the finalisation for the time being, and started looking at how to arrange triggering the timer.

We're back to the old problem that AROS basically does not have any provisions for signals/interrupts that preempt the running process in the process context (actually, task exceptions can, but they're too low-level for our purposes and don't work properly under AROS anyway). When timer.device fires, it pushes a message onto the IO request port, which either raises a signal (MP_SIGNAL port) or calls a function directly from the scheduler context (MP_SOFTINT port). There's also MP_CALL and MP_FASTCALL ports; these are the same as MP_SOFTINT for our purposes.

Having a soft interrupt that calls the timer callback doesn't work, as it would cause us to do large amounts of work inside the scheduler which is bad for system performance. Having a signal requires the main process to Wait() for that signal and then call the timer callback. The main loop is controlled by the application and by Zune, both things we have no control over.

I confirmed via #webkit that the timer callback is indeed supposed to be called from UI main loop. Studying the MUI docs and the Zune code, it seems that it is possible to have the Zune main loop setup a timer and trigger the callback itself using MUIM_Application_AddInputHandler. This is perfect for our needs, as it removes any need for initialisation and finalisation in the shared timer code itself.

The only thing that has to be arranged then is for the shared code to get hold of the application object to setup the timer. The application object is created and controlled by the application, of course, but there is only ever supposed to be one of them per application, and I can't think of a good reason why there should ever be more than one. Its easy to get hold of this object from any Zune object inside the application, via the _app() macro, with the slight quirk that its only available when the object is actually attached to the application object. We can detect that well enough though and defer calls into WebKit until we're attached, so all that remains is to grab the application object, stow a pointer to it in a global variable, and then have the shared timer code use that variable.

This all took me a few hours to work out, and then I happily went off to do Christmas things. Over the next couple of days, the nagging seed of doubt that I had in the beginning grew into some kind of spooky pirahna flower thing. This morning while hanging clothes out to dry I finally understood the issue. Its all to do with how global variables work, and its has much greater implications for this project than just getting hold of the Zune application object.

Lets think about what happens when you load a program into memory. Forgetting about the details of the loader doing relocations, setting up space for variables, etc and the program startup making shared libraries available, effectively you just have the system allocating a chunk of memory, loading the program from disk into that memory, and then running the code within it. Space for and initial values for global variables are all held within that chunk of memory, and only the program code knows where they are and what they're for. Nothing else on the system can reasonably access them so there's nothing to worry about.

A shared library is essentially the same as this, except that it is only ever loaded into memory once. When a second program requests it, the systems checks if the library is already in memory, and if it is arranges for the program to use it. This is where things can get complicated. The big chunk of memory contains some things that are sharable because they can be considered read-only - things like program code, const data, and so on. Regular global variables are generally not sharable, as you generally don't want changes made by one process to be seen by another.

In systems that have a MMU, the usual way that this is dealt with is to make a copy of the global data somewhere else in memory, and then map it into the process address space at the appropriate location. That is, process share the read-only parts of the shared library, but have their own copies of the writable areas. (In practice its quite a bit more complicated, but this is the general idea).

AROS, like AmigaOS before it, has all processes, libraries and anything else coexisting in the same memory space. Shared libraries pretty much don't use global data. There is no support for MMUs so the kind of copying and remapping descibed above is impossible. If per-process data is required, then various techniques are employed explicitly by the shared library author - per-opener library bases, data access arbitration using semaphores, and so on. That works fine, because the author is fully aware of these limitations when he designs and implements the library.

Its worth noting that this problem is not isolated to AROS, but to every system where a MMU is not available. uClinux has had the same issue in the past and dealt with it in a couple of different ways.

Now lets look at what I'm trying to do. My goal is and has always been to make WebKit a shared library (actually a Zune custom class, though as far as the OS is concerned its the same thing). WebKit and its dependencies all make use of global variables as necessary, and assume that their globals are isolated to a single process, which is a reasonable assumption given that basically every system out there that WebKit currently runs on works this way. For AROS though, this is a huge problem.

The cheap way out is to just ignore the whole mess by producing a static libWebKit.a and requiring any applications to link it statically. This is essentially what I'm doing now. It works well enough, but currently the (non-debug) library weighs in at a touch under 18MB, and thats with barely any AROS-specifics implemented. For every WebKit-using application you have running, thats at least 18MB of duplicated code that you have to hold in memory. There's also all the usual issues with static linkage: greater disk usage, no ability to upgrade just the library and have all its users get the update, and so on.

The least favourable option would be to rewrite all the parts of WebKit and its dependencies that use global variables and either find a way to remove them or otherwise move them into a per-process context. This is horrendously difficult to do and would pretty much remove any hope of contributing the code back to its upstream sources, which I consider an imperative for this project. So lets say no more about it.

The only other option is to add support to the OS to do the appropriate remapping stuff. This is no small undertaking either, but I think as time goes on, its a very good thing for us to have. I haven't investigated it in depth, but in addition to actually implementing the stuff in the loader, its also necessary to make some changes to the way modules are held in memory and shared between users.

Currently a module can exist in memory and be used as-is by multiple users without too much effort. Because there's no global data, sharing a module is as simple as incrementing a use count, so that the module isn't purged from memory ahead of time.

When sharing an object with global data, in the absence of a MMU, its necessary to allocate new global data for each opener and do its relocations each time. This requires keeping a record of the required relocations. There's also the issue of constructing the global offset table and the procedure linkage tables, and making sure the pointer to the GOT is carried around the application appropriately. Work that will be usefel here is Staf Verhaegen's current project on library bases and preserving the %ebx register. Of course this will all have to integrate nicely with that.

Then there's also the matter of detecting when to use all this new stuff over the standard loading and linking code. I think I can make that as simple as requiring all code to be shared in this way be position-independent (ie compiled with -fPIC). Code compiled in this way is incompatible with the standard load method anyway, and for this type of shared object its far simpler to implement this whole mess if PIC is enabled. If it is, then detecting which type to use should be as simple as looking for the presence of the .got section in the object.

Thats about as far as my thinking on the matter has come. The shared timer stuff that originally provoked all this is working happily, but if WebKit is ever to be a shared object on AROS, all this will need to be revisited. Because its such a huge undertaking I'm going to leave it until after the WebKit and Traveller are in some kind of usable state. At that time I'll look at handing off care of the web browser to someone else for a little while and work on this stuff instead.

tuesday, 1 january 2008

posted at 22:53 | comments

Hi. I have lots to tell you, but haven't had time to write it all down yet. But I wanted to share this, the very first web request ever done by WebKit on AROS:

GET / HTTP/1.1
Host: 192.168.0.1:8080
Accept-Encoding: deflate, gzip
User-Agent: WebKit AROS
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

I'll post more details sometime in the next couple of days. Happy new year :)

wednesday, 19 december 2007

posted at 22:06 | comments

This is week is insanely busy, as is typical of the week before Christmas, so I've had very little time to think about code in the last couple of days. I therefore opted for something that wouldn't steal too much of my brain, and began stubbing the Zune View class.

The interface will be typical Zune stuff. To get a web renderer into your app, you'll include a WebKitViewObject in your widget tree, and go from there.

The launcher is just a fairly standard Zune application setup. It will get a little more code before the end, mostly adding basic navigation buttons and location bar, but the basic structure won't change. This will serve as both a test program and an example of how to use WebKit in your own applications.

tuesday, 18 december 2007

posted at 11:17 | comments

Now that I've (apparently) fixed the loader, my mammoth WebKit test binary loads and runs, and so I've begun implementing the stub functions with earnest. To start my method has been to run the program until it crashes, find out where the crash happened, which is usually a NULL pointer dereference, and then provide a basic implementation of the class that that thing is supposed to be pointing to.

The current problem is a crash that occurs inside a regular method call, for no apparent reason. The offending method, in its entirety:

void DocumentLoader::setFrame(Frame* frame)
{
    if (m_frame == frame)
        return;
    ASSERT(frame && !m_frame);
    m_frame = frame;
    attachToFrame();
}

Good old printf() tracing shows that the crash occurs after m_frame = frame but before attachToFrame(). That is, that method is never called. This is highly unusual, and tedious to debug, because it means we have no choice but to drop down to assembly code, which I can muddle through well enough but can't really wrap my brain around.

Disassembling the last two lines of the method, we get this:

    mov    0x8(%ebp),%edx
    mov    0xc(%ebp),%eax
    mov    %eax,0xc(%edx)

    mov    0x8(%ebp),%eax
    mov    (%eax),%eax
    add    $0x8,%eax
    mov    (%eax),%eax
    sub    $0xc,%esp
    pushl  0x8(%ebp)
    call   *%eax
    add    $0x10,%esp

The pointer to the current object, this, is on the stack, 8 bytes in, as is the frame pointer, 12 bytes in. So we see the value of this being dereferenced through the stack and stored in %edx, and then the same for the frame pointer, being stored it in %eax. Then the location 12 bytes into the object proper is computed (which is where m_frame is stored), and %eax (the location of the frame object) is stored in it. Thus, m_frame = frame.

The next chunk, predictably, is the call to attachToFrame(). The important thing about this method is that its what C++ calls a virtual method. It wasn't until Friday that it was actually explained to me what that meant, and I found it hilarious. Consider:

    Object *o = new Object;
    o->method();

    o = new SubObject;
    o->method();

(where SubObject is a subclass of Object).

Now, if method() is a virtual function, this will do what you'd expect from most other OO languages: the first call will call Object::method(), the second calling SubObject::method(). If its not virtual, then both calls will go to Object::method, because its taken from the type of the pointer, not the type of the object itself.

I don't know if this was considered counterintuitive when it was first designed, but its certainly not the way most OO languages work these days. Usually you have to be explicit when you want to call a superclass version.

In any case, the code generated is different. In the simple non-virtual case, the call can be done via an absolute address, as the compiler can know exactly where the method() function is for the type. The virtual case is more complicated as the object itself needs to be interrogated to find out where its function is.

To do this, a table for each class that the object inherits from is placed inside the object, containing pointers to the functions that the object wants to use for its virtual methods. A virtual method call might then be rendered in C as:

    o->_vtbl_Object.method();

That is, go through the table of implementations of methods defined in the Object class to find the method, and call it.

So, getting back to our disassembly. attachToFrame() is a virtual method. The code gets this from the stack, 8 bytes in, and puts it in %eax. Then it dereferences the pointer to find the actual memory location of the object. It then adds 8 to that to get the location of the virtual method table, and dereferences that to get a pointer to the attachToFrame() function, which goes into %eax.

Then it does the usual function call setup, making room on the stack for the arguments and return address, and then calls the function at the location in %eax. It is here that the crash occurs, because %eax has 0 in it.

I was floored when I first saw this. I checked a number of times in different places, finally checking the constructor itself. And sure enough, the virtual table contains all zeroes. To me this smelt suspiciously like a relocation problem - if the the ELF loader is not correctly doing the relocations for virtual tables, then they'll point to garbage memory, causing a crash.

I'm not entirely sure how this can be, and haven't figured it out yet. I need to check the place where virtual table is normally initialised, but I don't know where that is! I can theorise by thinking about the structure of an object and the virtual table internally.

The first critical thing is that the virtual table inside the object is a pointer. That is, when the memory for the object is allocated space is not allocated for the virtual table too. A pointer needs to be to point to a valid virtual table. There's two ways this could be done: setting a pointer to some known static data that contains the data for this class, or allocating some more memory and copying the pointers from same known static data.

The former seems the more likely to me. The extra allocation and copy seems unnecessary as the table for the object will not change during the lifetime of the object. There are seperate tables for each class the object inherits from, so there's no need for a group of tables to be mixed into a single one.

So given that as a theory, we should be able to find some code somewhere around the constructor that sets up the virtual table pointer. It'll probably be the first thing after the memory allocation is done. This code might not exist in the binary itself though but may be part of a language support library (libgcc or similar). Regardless, the thing that will need to be there is the virtual table location.

I'm expecting to find that the location of the virtual table is not being relocated properly by the ELF loader. Basically, I trust GCC to produce correct code than I trust our loader to do the right thing. The problem could also be within our linker, collect-aros, but its so simple that I'm happy to rule it out initially.

Stuart, get back to work!

Update 3pm: Found it. I missed one section header table index conversion when I was updating the loader for large numbers of sections. Stupid, but it never hurts to exercise my brain on the really low level stuff.

thursday, 13 december 2007

posted at 22:04 | comments
  • mood: hobbitish

I just now have the extensions to the ELF loader implemented such that my gargantuan WebKit test binary loads. It took me a lot of reading and experimenting to figure out what was going on but I got it.

In my last post I talked about how files with large numbers of section headers store the count of headers elsewhere in the file. I'd taken care of that just fine. The other important thing that I missed is that every entry in the symbol table has a section header index that points to the section that the symbol is relative to. Of course this is a 16-bit field also, and has the same problem as the header count does.

The solution to this one is even more crazy. Basically there's an entire extra section in the file that is just an array of 32-bit values. If a symbol refers to a section with an index that is too large, you basically go fishing into that array to find the index instead. This of course means that I have to have that array loaded and available before I start doing symbol table work.

Finally, something that confused me until I put together some nice macros to deal with it was that there's a "hole" in the range of possible section header index numbers. What used to be the top 256 values (0xff00 to 0xffff) are reserved as special control codes, markers and other such things. Now that the header number is fudged into 32 bits, we get the situation where the header at index 65279 (0xfeff) corresponds to section 65279, but the header at index 65280 actually corresponds to section 65536 (0x10000). So basically, anywhere that a section number is found in any of the ELF structures, it has to be massaged into a header array index number taking the hole into account. This caused no end of issues, particularly since my file has hundreds of effectively unused sections - it was hard to even see when it was going wrong!

So now ArosLauncher loads and runs and I get some debug output before it crashes:

UNIMPLEMENTED:
(../../../WebCore/platform/aros/TemporaryLinkStubs.cpp:42 void WebCore::setFocusRingColorChangeFunction(void (*)()))
UNIMPLEMENTED:
(../../../WebCore/platform/aros/SharedTimerAros.cpp:10 void WebCore::setSharedTimerFiredFunction(void (*)()))

Before I get back into WebKit though I need to cleanup this code and commit it. I still need to merge the other two ELF loaders. As far as I can tell from a cursory glance the elf64 version is basically the same but using 64-bit definitions, macros, etc as defined by the ELF specs. The other, elf_aros, I'm not entirely sure about but its certainly much simpler. Its possible it just hasn't been looked at for a long time (the changelog certainly appears to show that). I'll continue to try to figure out what its for, but my feeling is that it can probably go, and elf64 and elf can be comfortably merged with a little restructuring of the code.

One thing that has become apparent is that our loader is incredibly slow and rather naive. As we start implementing more features (debug support, memory protection, shared objects) I don't think its going to cope well with its current structure. And its certainly got its work cut out for it - I've been reading Ulrich Drepper's paper "How To Write Shared Libraries", and it goes into a lot of detail about the kind of pain the dynamic linker has to go through to make things work. The glibc loader is something I'll have to study, I think.

wednesday, 12 december 2007

posted at 13:56 | comments
  • mood: elvish

I wrote a simple launcher for WebKit that creates a WebCore::Page, attaches it to a WebCore::Frame, then tries to load the Google homepage with it. Unsurprisingly, when I ran it it crashed, as most of my factory methods just return NULL. I fired up the debugger and figured out where the crash was coming from, and found it was in FrameLoaderClient::createDocumentLoader, one of my factory methods. Curiously, this function calls notImplemented(), and so should have printed something to the console. A little poking revealed that I had been done a release build, not a debug build, so I recompiled with --debug.

The resulting binary was almost three times the size, up around 300MB, which makes sense because its now carrying almost the entire source code for debugging as well. I had to start AROS with -m 512, to give it enough memory to actually be able to load the thing. I started AROS, opened a shell, started ArosLauncher, and then the amazing fireworks began.

On my debug console, I got a line of output:

[LoadSeg] Failed to load 'ArosLauncher'

Thats a problem - LoadSeg() is the program loader/linker. More exciting though was the line after line of pure binary appearing in my AROS shell. Do something like cat /bin/ls to see what I mean.

My first thought was that the awesome size of the binary was trampling something in memory, but a bit of poking around revealed the answer. When you type a command into the shell, it tries to load it as an executable file. If that fails, it checks if the file has the script flag enabled. If it does, it calls C:Execute with the file as an argument. Execute is the script runner, and it simply feeds the contents of the file into the shell's input buffer to be executed as though the commands were being typed.

Execute doesn't have any smarts to determine if what its being passed is really a script; that would be a useful feature for it to have. The real issue though is that the ArosLauncher binary had the script flag. I never set it, it shouldn't be.

Closer inspection revealed that the hosted filesystem driver, which maps Unix file permissions to AROS file permissions, was setting the script flag for every file without exception. That was perhaps a reasonable choice at the time it was written, as Unix does not have a script flag or anything similar it wouldn't have been immediately obvious what to map it too and it was never used in AROS anyway until recently (the shell gained support for testing for it and calling Execute a couple of weeks ago). Clearly though its not write, so I had to do something. I modified the permissions mapping code in emul.handler to map the AROS script flag to the Unix "sticky" (t) permission bit. I also implemented FSA_SET_PROTECT at the same time, so now typing protect +s file in AROS acheives the same as chmod +t file in Unix, and vice-versa.

So with that fix in hand, ArosLauncher was rerun and the far simpler error was returned:

ArosLauncher: file is not executable

So the next step was to dig into LoadSeg() and find out why it couldn't load the file.

A tiny bit of background: Any program, library or other "executable" thing under AROS (and most Unix systems) is stored in a format called ELF. It is split into a number of "sections". Each one contains some information. It might be program code, data, symbol names, debugging info, there's lots of different types. Its up to the OS loader/linker to pull all these together into a runnable program.

So, with the ELF specs in hand I started stepping through the loader code, and quickly found the problem. When you compile something with debugging information, it adds many extra sections to the binary object, containing what amounts to the entire source code for the program, so the debugger can give you the proper context and so on. Because it includes all of WebKit, ICU, cURL, libxml and SQLite, it has a lot of sections. Somewhere in the order of 75000 in fact.

The field in the ELF header that stores the count of sections is a 16-bit field, which means it can count up to ~65000. Clearly there are too many sections in the file to fit. In this case, the number of headers is marked as 0, and the loader should try to load the first header. In there is the real count, in a 32-bit field that normally is used for something else (the header size) but is borrowed just for this special case.

So I implemented this, and it works - it finds the headers correctly and does the relocations as it should. Its still not at the point where it will run ArosLauncher. It would appear that there's a symbol type that the AROS loader doesn't know about and is interpreting as being invalid, rather than handling/ignoring it. I'm not sure what's appropriate yet; I'll take more of a look on my bus ride home today.

More todo items: There are three ELF loaders in AROS currently, elf, elf64 and elf_aros. elf is the main one that I'm working on, elf64 is a copy of it taken recently with support for 64-bit symbols, and elf_aros is an old one that I have no idea of what its for or where it came from. I have no desire to make my modifications in three files, particular when I have no 64-bit system to test on, so I'm going to look at trying to merge these three files back together.

monday, 10 december 2007

posted at 14:46 | comments
tags:

Just a followup about the whole _GLOBAL_OFFSET_TABLE_ thing. Apparently this symbol is provided by the linker, so it makes sense that it doesn't work since the AROS linker is actually a custom ld script known as collect-aros which doesn't handle position independant code at all.

If we were to ever have real ELF-style shared libraries, this is one thing we'd need to implement. The other thing we'd need is a whole load of stuff in LoadSeg(), which is our "runtime linker".

Nothing to see here, just some notes for posterity.

sunday, 9 december 2007

posted at 11:27 | comments
  • mood: sucks
  • music: machinea supremacy - sidology 2

I'm a little stuck. Last night I wrote a trivial startup program to make sure linking and calling into WebKit was working correctly:

#include "config.h"
#include "Logging.h"

extern "C" {

void webkit_init(void) {
    WebCore::InitializeLoggingChannelsIfNecessary();
}

}

int main(void) {
    webkit_init();
    return 0;
}

It compiled fine, but the link failed:

There are undefined symbols in 'ArosLauncher':
         U _GLOBAL_OFFSET_TABLE_

All the bits of information I need to resolve this are scattered around (if they exist at all), but what I've learnt is this. WebKit is compiled with -fPIC, which produces position-independant code. This is what you want when producing an ELF shared library. Essentially what it does is setup an offset table to hold the locations of all the global symbols in the library, and causes the generated code to access those symbols through the table instead of going direct. Later, when the library starts, the runtime linker fills in this table with the correct locations to all the symbols. This allows the OS to place the library anywhere in memory it wants, rather than at the location the library was compiled for initally. This is all great stuff than doesn't make the slightest impact on AROS, as our shared libraries don't work this way. Well, they do conceptually, but thats a topic for another time.

I'm compiling all this code into a static library, but because it was compiled with -fPIC it has lots of references to _GLOBAL_OFFSET_TABLE_. Here's where I'm unsure of what's happening. Either GCC is not setting up the offset table because our patches to make it work on AROS don't enable it (reasonable, since we don't have support ELF shared libraries), or its just implied that if you're linking with a static library you won't need the offset table and are expected to compile with -fPIC. I spent a lot of time last night believing the former, but after being completely unable to find anything in the GCC code that supports this, I'm really starting to lean towards the latter.

Which brings us to the next problem. Currently AROS WebKit is build using qmake, the build system for QT. I chose this because it was the easiest way to get a cross-build running at a time where I had no real idea what I was doing. It would seem that its currently setup to build a shared library, which I'm hacking around at the last stage to make it output a static library. I haven't found an obvious way to disable -fPIC yet.

This highlights the next issue. qmake is not going to cut it going forward. Actually, none of the existing WebKit build systems are really suited to cross-building - its all hacks so far. Before long its going to need a real build system. I'd like to use plain GNU make so that there won't be an issue with compiling the mess on AROS, but there's still going to have to be some stuff copied from the AROS build system to support setting up the Zune stubs, for example. That suggests just using mmake directly, except that I have my reservations about its long-term suitability for anything. The build system is not something I want to debate here, I've said my piece about it elsewhere and I'm deliberately not discussing it until I have time to do my own experiments.

So here I am at a bunch of apparent deadends. I'm going to spend a little more time right now trying to bend qmake to my will, but this whole mess is rapidly getting out of hand. I believe a sigh is the appropriate action at this point.

Sigh.

Update 12:53: Figured out how to turn -fPIC off, and I now get why it wasn't working. I now see logging output on the console, awesome! A better build system is still required.

saturday, 8 december 2007

posted at 18:25 | comments

Today marks a major milestone for the WebKit port. It compiles!

-rw-r--r-- 1 rob rob 24782208 2007-12-08 18:25 libWebKit.a

It doesn't do anything yet, but it compiles. I have 298 stub methods across 41 AROS-specific files. Each one calls the notImplemented() macro, which simply prints the name of the method that was called.

The plan of attack from here is to write a tiny main() that tries to create a frame object and hand control to it. That should yield several million lines of output from notImplemented(). I implement those methods, one at a time, until I get something on screen.

Once I get a decent way into that process I should start to gain some understanding of how WebKit is actually bolted together. Once I have that I can start to think about the design of the Zune API.

The real fun starts now. I'm looking forward to writing some actual code rather than just stubbing functions :)

saturday, 1 december 2007

posted at 09:04 | comments

This week I've been working on another WebCore dependency, though a little different to the previous ones. To work well, it seems that WebCore needs a threading system. AROS doesn't have one. I think WebCore can work with just its stubs, but I don't want to. I want this done properly.

I started looking at how I might implement threads, and it seemed that the interface was general enough that it could be useful to as a shared library for other things besides WebCore to use. And so thread.library was born.

Its almost ready. Threads work, mutexes work, conditions work. The only thing I'm dealing with is what to do when the main task exits while threads are still running. There's a bunch of bad things that can happen, which I don't have time to go into right now, but the best thing I can do is to simply detach the threads and allow them to continue running. See here for more details, though the description is out of date - the code is now doing option 1, and the ThreadBase issues have been dealt with. The last thing to take care of is a small memory allocation issue that is causing a crash, but once thats done I'll check it in to AROS subversion for anyone to use.

Update 2007-12-03: Code is now in AROS SVN, and will be available for use in tonight's nightly and SDK builds. Be sure to read the README before starting.

sunday, 25 november 2007

posted at 20:50 | comments

Late Friday I reached a minor milestone when I got the platform-independent part of WebCore fully compiling and linking. Next up, the tricky bit: the platform dependent stuff, otherwise known as the actual port.

I spent a couple of hours staring at various WebCore classes trying to make sense of them, and eventually I started to get a feel for the structure, though I'm a long way off really understanding it. Basically, WebCore has classes for common GUI elements, like fonts, menus, and so on. To do a port, you have to reimplement these classes to wrap the same functionality in whatever graphics system you happen to be targeting. It was around this point I realised that I know basically nothing about the AROS GUI toolkit, known as Zune.

I had a look around for examples and documentation, and I started to see what was going on, but a lot of the code is a mess and its hard to get a clear picture of what's happening in my head. The only option left to me is to write a small application using some of the Zune features that I'll need to get an idea of what makes it tick.

I thought about it a bit on Saturday and today spent a couple of hours implementing a this little app that I call fonty:

Its a font preview program. You give it a font name and point size, and it'll render some text in that font. We already have stuff like it, so its not particularly useful, but so far I've learnt about the basic structure of a Zune application, how to make a Zune custom widget class (I have a seperate FontPreview custom class), and how the Amiga font system works. It'll soon have a context menu that allows selecting different styles, and changing the text. Again, not really great in terms of usability, but lets me see how everything works. And kinda fun to write too :)

wednesday, 21 november 2007

posted at 06:35 | comments
tags:

Michal writes about his continuing pain with the Amiga LONG/ULONG types on 64-bit AROS. Some guidelines for types:

  • If you're writing new code, just use the normal C types, and if you need types of a specific width, look to C99 uint32_t, etc. On AROS, LONG is always 32 bits, even on 64 bit systems. The C type long, however, can be 32 or 64 bits. Don't assume they mean the same thing.
  • Don't use ULONG, BYTE, IPTR, etc except when calling a system API that uses them, and then take care to make sure your type conversion is spot on.
  • The possible exception to this is BOOL, but only ever assign TRUE or FALSE to it, and never explicitly test its value; that is use if (flag), not if (flag == TRUE).
  • Don't store pointers in non-pointer types. If you want a generic pointer, use void *. If you need to convert between an integral type and a pointer, use intptr_t/uintptr_t.
  • Don't do clever bit things with bit fields, like Michal describes for FHF_WRITE. Just say what you mean.

This community service announcement brought to you by the variables i, tmp and foo in the hopes that it helps the general health and wellbeing of people like Michal who have to decipher yours and my bad code by themselves years after it was written :)

tuesday, 20 november 2007

posted at 13:45 | comments

I finally finished my dependency porting stuff, with libxml2 coming to life late last night. I haven't tested it properly yet, as its test suite requires glob(), which we of course don't have. I'll look at integrating a version of it soon so that I can run the tests. For the moment I'm totally over dependency porting, and eager to get onto WebKit proper.

Before bed I wrote the first line of AROS-specific code in WebCore. Ready? Here it is:

typedef struct BitMap *DragImageRef;

I have no idea what it does yet, but it was enough to get the relevant file (WebCore/platform/DragImage.h) to compile, and thats all I care about right now.

The build is going well. So far I'm just stubbing platform-specific classes to get the thing to compile. Once its compiled, I'll start implementing those stubs.

One thing that was missing that would have been difficult to work around inside WebCore itself was the reentrant time function localtime_r(). A bit of hacking on the bus this morning and AROS now has this function, along with its friends ctime_r(), asctime_r() and gmtime_r(). Phew.

Tonight's work is adding stubs for PlatformMenuItemDescription, whatever that is :)

sunday, 18 november 2007

posted at 21:16 | comments

Today I finished porting cURL, a library for getting things from the internets (or actually, anything with a URL). Its probably the dirtiest port I've done so far, both because the configure script is a mess (it knows enough to know that I'm cross-compiling, but then doesn't know enough about cross-compiling to do anything other than get in my way), but also because of the bsdsocket.library madness (which if you've been in #aros at all in the last couple of days you'll have heard my opinions on).

Obligatory screenshot:

Code at /git/aros/curl.git.

In other news, I committed my mlib patches this morning after a little testing and tweaking by Markus Weiss to get them working on PPC. I'm quite proud of it - it was a big, unknown thing and it came off without a hitch.

So now, armed with the 20071118 nightly (available in just a few short hours), you can (theoretically) possible to built all of the Traveller stuff done so far. If only you all had some build instructions .. :P

friday, 16 november 2007

posted at 21:53 | comments

Quick one before bed: a port of OpenSSL, which is needed for cURL, which is needed for WebCore.

It was actually a pretty easy port to make. OpenSSL is ported to so many platforms already that it was pretty much just a case of copying stuff from similar platforms. Amusingly, the platform most similiar to AROS as far as OpenSSL is concerned is Netware :)

Code available at /git/aros/openssl.git.

thursday, 15 november 2007

posted at 14:09 | comments

The results are in. The browser will be called "Traveller" (that's British spelling, with two ells). I had already thought of this as a potential name before asking for ideas, and when a couple of people suggested it too I knew it was good.

The reasons I like it are threefold:

  • Its a good companion for Wanderer.
  • It carries on the tradition of giving browsers a name related to finding the unknown: Navigator, Explorer, Konqueror, Safari, etc.
  • It references an in-joke among the members of my team at work, so its just a little bit personal too.

So thanks everyone for your input. I enjoyed hearing all your ideas :)

Relatedly, Paul J. Beel asked me a bunch of questions about the project and has just posted my answers over at the AROS Show. That should pretty much cover what exactly it is I'm doing and what you can expect.

I'm looking for someone who can is savvy with graphics to produce some art for the browser - icons, throbber, about screen, etc. I have ideas, but need someone who knows how to produce art and animations to give me a hand. Contact me via email (rob@cataclysm.cx or grab me on IRC (fce2 on irc.freenode.net).

Now that all the excitement and administrivia is out of the way, time to do some actual hacking.

wednesday, 14 november 2007

posted at 16:02 | comments
  • mood: coy

Just a few quick updates.

First, thanks all for your name suggestions. I hated some of them, I loved some of them, and I've finally decided on the name. Its one that I had thought of beforehand, but a couple of people suggested it here too. I'm not revealing it yet though; Paul J. Beel of The AROS Show has sent me some questions for an interview and I've promised that I'll reveal the name there. Of course I'll post it here shortly after, but of course you all read his stuff so you won't need it :)

I've started porting the WebCore dependencies. First up is the easy one, SQLite, which I finished porting this morning. Its a horrendous port, with no file locking and hacked up path handling, but it was the cheapest and fastest I could do, and will suffice for what I want. I don't want to get bogged down on tangents; WebCore itself is going to take enough time and brain to do without being distracted.

I'm now publishing my work as I go. The repositories for WebKit and its dependencies will appear at http://cataclysm.cx/git/. Feel free to clone from them and do whatever you want with the code. I'll post some build instructions soon; its quite hairy. I've also put my AROS repository up, which is where I'll publish stuff that hasn't made it to AROS SVN yet (usually becaused its unfinished and/or broken).

Thats all for now. Heading home now :)

sunday, 11 november 2007

posted at 21:01 | comments

Ever wanted to name a web browser? Here's your chance. I need a name now so that I have a way to refer to the whole project, rather than "WebKit port" (accurate until I start work on the chrome) or "browser bounty" (duh).

I have a couple of ideas, but feel free to post a comment with names of your own devising. I'll choose the one I like the most, or if they all suck, I'll choose one of my own. Its not a democracy, you know ;)

Update: Name has been chosen. Thanks all for your suggestions :)

saturday, 10 november 2007

posted at 10:07 | comments

Its been a big week of AROS coding, with a milestone being reached last night: JavaScriptCore, the JavaScript engine inside WebKit, is now compiling and running inside AROS. As such, I'm satisfied the a full port of WebKit to AROS is feasible, and as such, I've taken on the bounty to produce a browser.

My process for building WebKit has been simple. I made minor changes to their build system to use AROS crosscompilers, and then let it build until it breaks. Then I go in, figure out what died, and fix it. Often this is easy, requiring only some platform defines and such. Sometimes its been a little harder, which is where posix_memalign() came from. The really fun thing happened at the start of the week when the build failed because a couple of math library functions were missing.

Our math library (known as mlib or arosm, depending on where you look, though every other platform calls it libm, go figure) was originally taken from a math library written at Sun way back in 1993, and released for free. We got our copy from FreeBSD in 1999, and it was updated again in 2003. Its missing a lot of stuff though, notably things from C99.

I had a look through the FreeBSD code and found the functions I needed, but on noticing just how much stuff was missing I decided it might be better to do a full refresh of libm. As is usual when I start on something, it rapidly got out of hand.

I had to make a few changes to our core headers to provide all the necessary defines and types and such to make it work. The new code also has an amount of architecture-specific code for using the FPU. Fortunately FreeBSD supports all of the architectures that we have active ports for (i386, x86_64 and ppc), so it was just a matter of getting the right code into place.

In any case, lots of tweaking and merging has been going on such that I now have about 20000 lines changes spread out over 21 patches. I haven't committed them yet as I'm waiting on some build macros from Staf to allow me to build the architecture-specific files into the library correctly. My hacked version seems to work well, and passes a couple of tests from Fred Tydeman's C99 FPCE test suite. I'll run all the tests soon, but I expect them to pass without issue.

Once the patches can compile cleanly, I'll try to get some other AROS devs to review them, as they're big and I'm scared. Once its all deemed good, they'll go in, and we'll be doing fancy math forever. Hurrah!

Anyway, after shoring up the holes in AROS, it was back to JSCore. The code is exceptionally well written, and easy to port. Apart from adding #ifdef here and there, the only actual code I had to write was stuff to help the garbage collector find the stack base, and thats two lines in kjs/collector.cpp:

    struct Task *task = FindTask(NULL);
    return task->tc_SPReg;

The JavaScript engine test program testkjs runs properly. The only issue is that the garbage collector is not fully cleaning up all the objects at script exit, which I think may be a memory management issue. I haven't fully tracked it down, but the folks in #webkit (particularly bdash) have been very helpful and I'm expecting to have it sorted out soon.

So thats my progress so far. My plan for the browser proper is to implement it in two stages. The first is the port of WebKit proper, which is a porting JavaScriptCore and WebCore, writing a trivial launcher application, and porting libraries it depends on and otherwise fixing things in AROS. Once thats done, the second stage begins, which involves integrating WebKit into AROS proper. I haven't thought this through fully yet, but I expect at this point that I'll be writing a Zune widget to allow applications to embed WebKit, and from there writing a Zune application to be the browser proper.

I'll be making my git repositories available shortly, so the brave can track my progress. And you'd better believe that only the brave need apply - you need to be willing to track AROS and WebKit SVN repositories and regularly recompile AROS, gcc and WebKit. Oh, and there's a 20-step build process for ICU as well, one of the WebKit prerequisites. Its early though, this will be made easier after I'm finished so other people can hack on this too.

saturday, 3 november 2007

posted at 12:40 | comments
tags:

I just finished implementing posix_memalign(). It will help with JavaScriptCore porting, as its allocator/garbage collector wants to do lots of memory tricks, including unusual alignments. I'll write more about my WebKit porting progress later.

I love doing pointer arithmetic. It spices up C so that I feel like I'm writing Perl one-liners:

int posix_memalign (void **memptr, size_t alignment, size_t size) {
    UBYTE *mem = NULL, *orig;

    /* check the alignment is valid */
    if (alignment % sizeof(void *) != 0 || !powerof2(alignment))
        return EINVAL;

    /* allocate enough space to satisfy the alignment and save some info */
    mem = AllocPooled(__startup_mempool, size + alignment + AROS_ALIGN(sizeof(size_t)) + AROS_ALIGN(sizeof(void *)));
    if (mem == NULL)
        return ENOMEM;

    /* store the size for free(). it will add sizeof(size_t) itself */
    *((size_t *) mem) = size + alignment + AROS_ALIGN(sizeof(void *));
    mem += AROS_ALIGN(sizeof(size_t));

    /* if its already aligned correctly, then we just use it as-is */
    if (((IPTR) mem & (alignment-1)) == 0) {
        *memptr = mem;
        return 0;
    }

    orig = mem;

    /* move forward to an even alignment boundary */
    mem = (UBYTE *) (((IPTR) mem + alignment - 1) & -alignment);

    /* store a magic number in the place that free() will look for the
     * allocation size, so it can handle this specially */
    ((size_t *) mem)[-1] = MEMALIGN_MAGIC;

    /* then store the original pointer before it, for free() to find */
    ((void **) &(((size_t *) mem)[-1]))[-1] = orig;

    *memptr = mem;
    return 0;
}