Note: this article was written by Daniel Reinhold.
I remember several years back (about spring '96) when I first discovered the joys of the internet and was amazed at the sheer number of goodies to be plucked. This was well before BeOS Intel, so I spent alot of time downloading Windows shareware programs. After a few months, however, the excitement died down considerably when I discovered an unfortunate truth: most of those shareware programs were complete crap. If they didn't crash within a few minutes, their interface was so awkward that I just couldn't stand to use them.
There are many styles of bad interface, but one of the more common (and in many ways, humorous) of the lot is what I call the "thin geek layer". This is where the interface consists of a myriad of buttons (often tiny) and/or complex nested menus containing long lists of items whose labels seem to pretty much match the underlying function calls. It makes me grin to see such programs, because I can almost tell, by looking at the front end, what the back end must be like. I have a pretty clear feeling that when I click on a button called 'RasterPunkFoobitz' that it directly calls a rasterPunkFoobitz() function. The programmers who write these kinds of programs are obviously so enamored (or distracted) with implementing all the whiz-bang functionality that they don't give much thought or effort into the idea of presenting an interface designed with the user in mind.
This is the sort of thing that makes user interface designers hyperventilate and turn blue. The desired goal for any good interface is to present a world view that corresponds with what the user cares about and the set of tasks he wants to carry out. The back end should implement this by whatever means necessary to fulfill the illusion.
Creating a good user interface isn't just the task of application programming. It's also the concern of library design -- particularly when it's part of an application framework. In this case, the users are developers and the interface is the API (Application Programming Interface). An API must obey all the rules of good user interface just as surely as any user program if it is to going to be effective and usable.
Implementing the BeOS API
The BeOS API, I am happy to say, is a very good API -- that is, a very good design. Because of this, the front end presented to developers -- the various kits with their associated headers, class definitions, documentation -- do not necessarily correspond one to one with the back end implementation -- libraries, add-ons, servers.
While this is certainly a good thing from a design stand point, it does create a certain confusion with regards to how we, the Haiku project, implement the BeOS API. Because, as implementors, we must be just as concerned with the back end as the front. Even more so, since the front end (the API specs) are already established and it is the back end that must be recreated from scratch.
The confusion arises when people look at the Haiku programming teams that we have set up and try to understand how their particular "pet" concern fits in. Oh sure, for the most part, the division of labor into the various teams is quite obvious and makes sense: the Printing team works on printing, the Network team implements networking, and so on. There is no problem with this. But where do drivers fit in? What about missing kits? For example, why is there no team for the Support Kit? Where's the team for OpenGL? Why is there only one team to handle both the Application Kit and the Interface Kit?
One answer might be that we don't know what we're doing. Since there's probably an element of truth in that statement, we'll disregard that nasty little observation for now and move on to more useful explanations (he says slyly with a large grin on his face).
The large diversity of tasks and skills required to implement an OS naturally require that you break the whole thing down into a collection of parts. You put different people with specific skills into their own area of expertise. When several people work together to implement one particular piece of functionality, that is called a team. The teams co-operate, of course, but it's quite amazing (and a testimonial to the good design of the BeOS API) the extent to which these various teams can work independently as their own little islands.
These teams are about the business of creating the back end. The front end, i.e. the BeOS API, acts as a projec spec and, as a design element, is basically done. We may make a few very, very minor alterations on the road to R1, but, for all practical purpose, this can be considered finished.
Now, what exactly are the parts to be written? Well, at the risk of sligthly oversimplifying the situation, you could say that the BeOS consists of three major parts: the kernel, the drivers, and the application framework. The kernel is, well, the kernel. The drivers are kernel add-ons such as video card and other hardware drivers and all file systems. The application framework defines userland. It's where all the apps live with their associated windows, loopers, messages, etc.
Let's focus on the application framework. For the BeOS, this has a C++ interface. The vast majority of functionality is placed into C++ classes. Lots of classes.
Pretend for a moment that we have the complete set of classes before us in one long list (no, I haven't actually compiled such a list, but even if I had, I wouldn't put the whole thing here, for goodness sake).:
BApplicationYou get the idea, there's alot of 'em.
(many others omitted)
Now consider the task of re-writing all these classes, which is precisely what the Haiku project is doing. We start at the top and go down the list, one by one, and implement each class, right? Bzzzt. Wrong. Off with your head. We may be a little dumb, but we are not stupid.
Naturally, most of these classes can be clustered together into related groups that help implement a particular type of functionality. BPoint, BRect, BFont, and a score of other related graphics classes would naturally be written by folks who have expertise in that area. Likewise, BFile, BDirectory, BEntry, etc. call for a different set of knowledge and skills. This is not to say that some people couldn't work on all of these (some probably have the talent to do so). But even then, you want focus. You want for each person to be able to concentrate effectively on each set of classes they must implement.
Ok, I'm sure that no one disputes what I've said about this. Yes, you should divide up the application framework into the various different pieces of functionality, find programmers who have the corresponding skills and interest, and then assign them to that piece. Get several of them working together and you have a programming team. No one argues with this.
But what exactly are the pieces? What are these natural divisions I have spoken of that define the different areas of functionality? The BeOS kits, you might well answer. Well...no, not really. That's where the confusion comes in.
You see, the programming teams are all about creating the back end. The kits, however, are a description of the front end.
It's tempting to say that the kits are a marketing fiction. That's probably going a bit too far. But the point is, those kits are really just a description in the BeBook to help programmers understand how to use the BeOS API. They are Be's attempt to organize and explain all those classes: how they work, how to use them, how they inter-relate, etc. Their particular organization of the kits wasn't written in the stars -- they could well have organized it somewhat differently.
Since they describe the front end, those kits do not necessarily represent the only or even the best way of dividing up the work of implementing the classes. The BeOS API is no "thin geek layer" -- what you (the developer) see in the kits is what you need to see in order to get your tasks accomplished. But as implementors of the API, we have to organize the work in a way that best suits the needs, desires, and skills of those who are actually in the trenches, filling out the back end.
Having said all this, in reality, those kits often *do correspond* to a natural area of functionality, even as far as implementation is concerned. For example, Be found it convenient to divide up all the classes and headers for the Media related stuff into one unit which they call the Media Kit. Likewise, we find it convenient to put all the tasks involved in re-creating the Media Kit into one team.
So you see, the kits as described by Be in the BeBook don't neatly fit into the task of implementing the BeOS. They don't *perfectly* match our requirements; on the other hand, they do match to a certain extent. It's a near miss. Because of this, we often use the term "kit" to describe a piece of implementation that we're working on, which only confuses the matter. To muddy the waters even further, we have used the term "kit" to label each of the high-level folders in the CVS repository. Thus, when looking for the project source code, you find them organized into folders called "kernel_kit", "app_kit", "game_kit", etc.
We're not trying to intentionally confuse the issue. We're just struggling for the appropriate terminology to describe what we're doing. The term "kit" is there from the BeBook, it's simple, well-known, and reasonably catchy. You tend to use what you have.
While a developer needs to have the API documented so he can understand how to use it, and the have headers available so that he can compile his code, the actual functionality is provided by the libraries. Since the libraries are the back end, there is no required correspondence to the kits. So is there a correspondence? Again, yes and no. Be sometimed matches a library one-to-one with a kit -- e.g. the Network kit is implemented in the libnet.so library. But in other cases, there is no set correspondence.
Consider again the imaginary list of all BeOS classes referred to above. Had Be wanted to, they could have compiled all of them into one big, mondo library -- say, libeverything.so. That certainly would have been a very simple setup and it would have worked. But it does have disadvantages: essentially every part of the OS would have to be loaded in memory at all times, even if certain parts were hardly ever used by particular users. Since almost all parts of the OS are used at some point, this isn't as bad as it seems. Still, it offends the sensibilties of many programmers who prefer a more fine-grained approach. Another disadvantage, from Be's perspective, would be that every time someone recompiled a new class, regardless of how peripheral its duties, the whole library would have to be recompiled. Not very good from a project management stand point.
As a matter of course, Be took something of a middle ground. Many of the kits are implemented as separate libraries, but there are a couple of big, all-encompassing libraries at the core. There is libroot.so, the "root" library, which exports all the kernel functions that are available in userland. Then there is the mother of all libs, libbe.so, the "Be" library, which implements all the core functionality of the application framework. libbe.so contains all the guts needed for the Application Kit, the Interface Kit, the Storage Kit, the Support Kit, and a few others. It's a biggy.
One of the distinctive features of the BeOS is its client/server architecture. This means that most of the OS functionality is delivered at run time by servers -- background processes that are started at boot up and are always running.
The most common example is the app_server which works in tandem with your application to provide all the graphics and messaging for your application's windows and views. Note that the app_server manages both the graphics operations (described in the Interface Kit) and the windowing/messaging operations (described in the Application Kit). This is why we have only one team dedicated to both kits: the reality is the back end functions of the app_server completely cut across both kits (Application and Interface) and trying to handle this as two teams would just be duplicating effort.
Nor does every kit have a server. There is no game server or storage server, etc. The media kit actually has two: media_server for normal media applications, and the media_addon_server for media addons.
Tying it all together
OK, let's summarize a bit. We have three basic entities to deal with in order to organize the work of recreating the BeOS: the kits, the libraries, and the servers. The kits are the interface spec, designed by Be and adopted by Haiku to serve as the project guidelines. The libraries are the actual compiled code that supply the OS functionality. The servers are userland processes that dish out the functionality to client programs at run time.
Finally, we have the teams -- groups of programmers united to implement each particular piece of the OS. They must organize the other three entities together in a way that makes sense for the type of back end work they are engaged in.
To try to visualize the relationship between these, here is a table that lists each kit from the BeBook and then maps the corresponding library, server, and designated team for it:
Kit Library Server Team
Application libbe.so app_server App/Interface
Device libdevice.so (None) (None)
Game libgame.so (None) Game
Interface libbe.so app_server App/Interface
Kernel libroot.so (None) Kernel
Mail libmail.so mail_daemon Networking
Media libmedia.so media_server Media
Midi libmidi.so midi_server MIDI
Network libnet.so net_server Networking
OpenGL libGL.so (None) (Game?)
Storage libbe.so (None) Storage
Support libbe.so (None) (None)
Translation libtranslation.so (None) Translation
Input Server libbe.so input_server Input Server
Screen Saver libscreensaver.so (None) ScreenSaver
Deskbar libtracker.so DeskBar
Tracker libtracker.so Tracker
There are some awkward gaps in the table above, but if you reorganize the table with respect to teams, or libraries, or servers, you get the same results (just different gaps). These various elements all have a notion of partitioning the components of the OS, but each with distinctive viewpoints. Change the viewpoint, and you get a slightly different organizational scheme.
Filling in the gaps
Where is the Printing team in the table above? Actually, there is no Printing Kit in the BeOS API -- only a single class called BPrintJob which is in the Interface Kit. Deskbar and Tracker don't have an assigned team either, but these are already taken care of in the OpenTracker project, so we needn't be concerned about it.
The Support Kit does not have a team, but does it really need one? That kit isn't really a kit like all the others that are organized according to common purpose and function. Instead, the Support Kit is just a hodge-podge of classes that didn't find a place within any other kit. It's just a convenient name for all the orphaned classes. Some, such as BArchivable and BFlattenable are pertinent to the sending of BMessages and, thus, are being implemented by the App/Interface team. Others are generic classes, such as the BString class, that could be implemented by any team member.
Where is OpenGL? That hasn't been decided yet. No specific team has been setup to implement this, although there has been discussion that it could be handled by the Game Kit team. Or perhaps the App/Interface team will do it instead. Or maybe a separate team will be factored out and the Game Kit and App/Interface teams can then use and build on the base implementation. More than likely, this will not be dealt with in R1.
And what about the parts outside of the application framework? This is probably the least well documented aspect of the project. Basically, most functionality not part of userland will be implemented by the kernel team. There can be exceptions, particularly for add-ons -- e.g. BFS (the Be File System) has its own programming team.
Drivers, on the other hand, will likely be taken up by whoever is best equipped to do the work. The basic drivers for the mouse, keyboard, hard disk and CDs (at least the initial versions) will be written by members of the kernel team. An intial, baseline graphics driver will come from the App/Interface team. And the members of the Printing team are writing the printer drivers. Really, anyone who has the skills and motivation can jump in on this work. We could set up a "Drivers" team, but there is probably little point. The main purpose of a programming team is to join together the collective brains and efforts of the members towards a specific task. But each type of driver tends to require very detailed knowledge about a specific area. So members of a Drivers team might well have only their collective membership in common.
In short, it's a bit of a mess. The inter-relationships between the various elements is something of a cats cradle. The teams must recreate all the kits in the API, but they are actually implemented in the libraries and servers. Each kit does not necessarily correspond to a specific library, each library does not necessarily have a corresponding server, each team does not necessarily implement either a specific library or kit. Clear as mud, no?
Ultimately, how we divide up all the various implementation tasks comes down to the skills, interests, and participation of the project members. For example, right out of the gate, Haiku will have far better printing support than the BeOS ever did. This is not because we sat down at the beginning and decided, 'dammit, we're going to excel at printing in Haiku'. It's just because we happened to have attracted the attention and help of several programmers with long experience with printing and printer drivers.
If you see an unfilled gap in what I've presented in this article, and you have the programming chops to make it real, then join the project and make it happen. I promise you that you'll be welcomed and no one will get in your way.