Displaying Newsletter
Issue 37, 08 Mar 2003


  In This Issue:
 
Compile them resources by Matthijs Hollemans 
Compile them resources

Compile them resources

All BeOS programs have "resources". Simply put, a resource is data that is bundled with your application's executable. Typical examples are the application's icons and its signature, but you can attach any data you want. Many applications store bitmaps, text, cursors, and even complete user interfaces (dialog boxes, menus, etc.) as resources.

This may sound a lot like attributes; after all, aren't attributes a way to store additional data alongside your files as well? Yes, but the difference is that resources can only be used with application files, not with just any file. Unlike attributes, they are not intended as general purpose data storage. Instead, they only provide data that "sticks" to the executable. Although applications can overwrite their own resources, this is not recommended. Resources are typically set during compile time and never change afterwards. This means, for example, that you should not use resources to store user settings.

To see the resources that are bundled with an application, you can drop the executable on a tool like Resourcer. You can also go into a Terminal and type listres filename.

Making resources

If you have ever written a BeOS app, you probably used the standard FileTypes tool to create a .rsrc file with the application's signature, the launch flags, version information, and the icons. You then added the rsrc file to your Makefile or BeIDE project, and when you compiled the app, the resources were automatically copied into the executable. Maybe you have also used a tool like QuickRes or Resourcer to stuff additional resources into the rsrc file.

These tools are not the only way to create resources; you can also use a "resource compiler". A resource compiler is a (command line) tool that takes a text-based resource script and turns it into an rsrc file. With a resource compiler, you express your resources as ASCII text using a simple scripting language, which makes the resource files much easier to edit and maintain.

BeOS R5 comes with an (experimental) resource compiler called "beres", and a corresponding decompiler called "deres". The resource definition language they use is called the "rdef" language. Recently I wrote an open source replacement for these tools, called "rc". This article demonstrates some of the ways you can use rc to create your own resource files.

Note that rc is not the only resource compiler for BeOS. The source tree of the Pe text editor contains an alternative compiler called Rez. Why did I write rc if another open source resource compiler was already available? Mainly because Rez does not understand the rdef language. In addition, several projects (such as OpenTracker), already use rdef files. Since the goal of OpenBeOS is to provide an open source re-creation of BeOS R5, I felt it was important to re-implement the resource compiler as well.

The rdef language

The syntax of the rdef language is straightforward, so writing resource scripts is not very difficult. A resource script is a plain text file with one or more resource definition statements. It may also contain C or C++ style comments. By convention, resource script files have the extension ".rdef".

Here is an example of a simple resource script:

resource(1) true;      /* this is a comment */
resource(2) 123;       // and so is this
resource(3) 3.14;
resource(4) "hello world";
resource(5) $"0123456789ABCDEF";

When compiled, this script produces a resource file with five resources. The above example also illustrates the types of data that resources are allowed to have: boolean, integer, floating point, character string (UTF-8), and raw data buffer (hexadecimal).

By default, integer data is stored as a 32-bit int, and floating point data is stored as a 4-byte float. If you want to change the way the data is stored, you have to cast it. The resource compiler understands many of the native BeOS types, such as int8, int16, size_t, and a whole bunch of others:

resource(10) (int8) 123;
resource(11) (double) 3.14;

You can also change the resource's type code. This does not change the way the data is stored, only what it means. The type code gives you, the programmer, a hint as how to interpret the data. To change the type code of a resource:

resource(12) #'dude' 123;

For your own convenience, you can also name resources:

resource(13, "Friday") "Bad Luck";

The resources we have made so far consisted of a single data item, but you can also supply a collection of data values. The simplest of these compound data structures is the array:

resource(20) array { 1234, 5678 };

An array is nothing more than a raw data buffer. The above statement takes the two 32-bit integers 1234 and 5678 and stuffs them into a new 64-bit buffer. You can put any kind of data into an array, even other arrays:

resource(21) array
{
    "hello",
    3.14,
    true,
    array { "a", "nested", "array" },
    $"AABB"
};

It is up to you to remember the structure of this array, because array resources don't keep track of what kind of values you put into them or where you put these values. For that, we have messages. A message resource is a flattened BMessage. A message has a "what" code and any number of fields:

resource(22) message('blah')
{
    "Name" = "Santa Claus",
    "Number" = 3.14,
    "Small" = (int8) 123,        // use cast to change data type
    int16 "Medium" = 12345,      // specify data type
    #'dude' "Buffer" = $"00FF"   // specify a new type code
};

Besides arrays and messages, the compiler also supports a number of other data structures from the Be API:

typecorresponds tofields
pointBPoint, B_POINT_TYPEfloat x, y
rectBRect, B_RECT_TYPEfloat left, top, right, bottom
rgb_colorrgb_color, B_RGB_COLOR_TYPEuint8 red, green, blue, alpha

For example, to add a color resource to your script, you can do:

resource(30) rgb_color { 255, 128, 0, 64 };

Or you can use the field names, in which case the order of the fields does not matter:

resource(31) rgb_color
{
    blue = 0, green = 128, alpha = 64, red = 255
};

The compiler also provides convenient shortcuts for the resources you would normally set from the FileTypes application:

typecorresponds tofields
app_signaturethe app's MIME signaturestring signature
app_flagsapplication launch flagsuint32 flags
app_versionversion informationuint32 major, middle, minor, variety, internal
string short_info, long_info
large_icon32x32 iconarray of 1024 bytes
mini_icon16x16 iconarray of 256 bytes
file_typessupported file typesmessage

To conclude this short summary of the rdef language, this is how you would set the application's signature:

resource app_signature "application/x-vnd.my.app";

As you can see, rc lets you do pretty much everything that the GUI resource tools do. It has several other interesting features, such as the ability to make your own data structures ("user-defined types"), that I haven't touched on here. The documentation that accompanies rc goes into much more depth, so I suggest you take a look at that if you want to know more.

Compiling the rdef script

Once you have written your script, you need to compile it. Since rc is a command line tool, you must run it from a Terminal window. Compiling is as simple as typing:

rc -o outputfile.rsrc inputfile.rdef

If your project uses a Makefile or Jamfile, you can add a rule for rc and it will automatically generate the rsrc file for you when you compile the project. Below I will discuss how to do this for projects that are part of the OpenBeOS CVS tree. But first...

Using the resources in your app

So you have added a bunch of resources to your application. Now what? You create a BResources object, that's what. The BResources class, which is part of the Storage Kit, is initialized with a pointer to a BFile object. For example:

BFile file("/boot/home/SomeFile", B_READ_ONLY);

BResources res;
if (res.SetTo(&file) == B_OK)
{
    ...
}

Typically, you only want to look at the resources from your own application, so first you need to make a BFile with the path to your app. The following code snippet illustrates how to do this:

app_info info;
be_app->GetAppInfo(&info);
BFile file(&info.ref, B_READ_ONLY);

BResources res;
if (res.SetTo(&file) == B_OK)
{
    ...
}

Now that we have a BResources object, we can call its LoadResource() function to load one of the resources into memory:

size_t size;
const void* data = res.LoadResource('type', id, &size);

LoadResource() gives you a pointer to a memory buffer and the size of that buffer. This memory belongs to the application and you are not allowed to modify or free it. Otherwise, do with the data as you please. BResources also contains several functions that return information about the resources, which can be handy if you don't know beforehand which resources are available.

Note that you should only use BResources to look at custom resources. The standard application resources (signature, icons, app flags) should be accessed through the BAppFileInfo class, which is also part of the Storage Kit.

Finally, you can save yourself some trouble if the resources contain images. The Translation Kit's TranslationUtils helper class can create BBitmap objects straight from the resources:

BBitmap* bitmap1 = BTranslationUtils::GetBitmap('type', "name");
BBitmap* bitmap2 = BTranslationUtils::GetBitmap('type', id);

Refer to the corresponding chapters of the BeBook for more info.

Switching to rdef

The plan is to switch all rsrc files from the OpenBeOS CVS tree to rdef in due time. The main reason is that CVS doesn't handle rsrc files very well, because they are binary. Most of our resource files will be much easier to edit and maintain if they are text-based.

To give you an impression of how easy it is to switch to rdef files, let me relate my experiences with switching over StyledEdit. The source code folder of StyledEdit already contained several rdef scripts. These scripts, however, were not used in the compilation directly, and the project's maintainer used the old beres compiler by hand to make the rsrc file. Like most other projects, the relevant part of the Jamfile looked like this:

AddResources StyledEdit :
	StyledEdit.rsrc
;

All I had to do to make the switch was change the file names. Jam is smart enough to first compile the rdef scripts into an rsrc file, and then copy those resources in the StyledEdit executable. After the change the Jamfile looks like this:

AddResources StyledEdit :
	StyledEdit.rdef
	StyledEdit.icons.rdef
	StyledEdit.version.rdef
;

Your project probably doesn't have an rdef script yet. If you already have an rsrc file, then making the rdef is easy, because rc also includes a decompiler that will take one or more rsrc files (or any file with resources) and produce a ready-to-use rdef script. That should save you some typing.

To decompile, type the following from a Terminal window:

rc -d -o outputfile.rdef inputfile.rsrc

Note: Even though it is already possible to replace the rsrc files from the OpenBeOS tree, it can sometimes be a little inconvenient. Some of the resources (such as the application version) are still a little hard to specify because rc's type mechanism is not advanced enough yet. A future version of the compiler will make this much easier. But don't let that scare you from experimenting with rc ;-)

And finally...

You can download rc 1.0 in a pre-compiled package. If you have checked out the OpenBeOS CVS, you can also compile rc yourself. Its source code lives in the /current/src/tools/rc directory. Enjoy!

 
The Art of Jamming, Part 1 by Ryan Leavengood 

Before the musicians in the audience get too excited, I'm not going to talk about getting together with buddies to crank out some tunes. Nor will the cooks in the audience find instructions on making the perfect fruit preserves. No, when I say Jamming, I refer to the act of using the Jam build tool.

In this first part of my series on the Jam build tool, I'm going to provide a high level overview as well as show the product of some of my recent labors with Jam: the Jamfile-engine. For those who have developed on BeOS for a decent amount of time, this may sound familiar. It should, because I have essentially taken the functionality of the Be makefile-engine and "ported" it to Jam.

What Sort of Engine?

For those not familiar with the Be makefile-engine, take a look at your /boot/develop/etc directory some time. In a nutshell, this tool provides a nicely commented makefile which you fill out like a form to use in building your BeOS application. You provide the name for the application, the source files it is compiled from, libraries to link to, extra include directories, etc. At the bottom of the makefile template is a command that includes the makefile-engine, which does all the work to get your project built. The same makefile template can also be used to build libraries and drivers, and the same makefile can be used on both Intel and PowerPC machines. This is unlike BeIDE project files, which are different on each platform. Another difference between BeIDE project files and the makefile template is that the latter is of course plain text, where the former is binary. As many of you probably know, plain text is much better for source control tools such as CVS, and also allows for easier programmatic manipulation.

So as you can see I think the Be makefile-engine is yet another great idea from Be, Inc., and decided to create a Jam-based tribute to it. Of course there are several other good reasons to do this besides paying a tribute. One big one is the fact that I doubt we could distribute the actual Be makefile-engine in an OpenBeOS release. Another is that it just doesn't seem right to have Jam the official build tool for OpenBeOS, yet leave our application developers using a make-based build system.

Jam From 10,000 Feet

From a high-level Jam is essentially an interpreted scripting language with a specialty in building things. I consider it to be a nice evolution of make, in that it is more powerful and in general easier to use. Of course people who are used to make probably won't like Jam at first because it is so different, and requires a different kind of thinking when creating a build script.

So how does one create a Jam build script? First you define any variables you might need or that are used by rules you will call, and then you call those rules. Rules are somewhat like functions or methods in programming languages: they have parameters and perform some useful function, which for Jam usually involves creating one thing from another. To ease the creation of build scripts, Jam has a capable language syntax, and quite a few built-in rules that perform common build-tool functions, like compiling C or C++ files and linking applications. I will go into more detail concerning the Jam language and built-in rules in my next article, but must explain one thing in detail now: Jam's use of whitespace. It is important to learn this early and well: Jam interprets build scripts as a list of tokens separated by whitespace, and to have the files you create interpreted properly, you must have whitespace everywhere. This includes putting whitespace before the semi-colon (;) that terminates a line. This is important to learn now because you must do this when using the Jamfile-engine described below. If you forget to put whitespace in important areas, Jam will produce errors which can be hard to figure out (believe me, I know.)

Jamming Things Up

So now that you have some basic Jam knowledge, let me explain how to set up the Jamfile-engine:

  1. If you haven't already, download and install Jam for BeOS, which you can get here.
  2. Download this: Jamfile-engine.zip
  3. Follow the installation instructions in the README file found in the above zip to get the files in the right place.
  4. Find a good BeOS application that you have the source code to, so that you can create a new Jamfile with which to build it. I used the Clock application from the Be sample code, which is also now in the OpenBeOS CVS tree (under /current/src/apps/clock.) It would be a plus if the application you choose already had a makefile which uses the Be makefile-engine, as the Clock app does (though the one in the OpenBeOS CVS already has a Jamfile.) A makefile of this sort can be spotted by an initial line like this:
    ## BeOS Generic Makefile v2.0 ##
    The version number may be different, but if it looks like that, you have a generic makefile which uses the Be makefile-engine.

Once you have done the above, you can proceed to fill out the Jamfile template for this application:

  1. Copy the Jamfile from the location that the Jamfile-engine files were installed to (which by default is /boot/develop/etc.)
  2. Open the copied Jamfile and the old makefile, if there is one.
  3. Fill out the Jamfile as specified in the comments, or by copying from the makefile. Remember the above discussion of whitespace.

Once you have the template filled out to your satisfaction, open up a Terminal, cd to your application directory and type jam. If you have provided the correct Jam incantations in your template you should find a new obj.X86 directory in your application directory that contains the object files and final application for the project. If you get any errors from the Jamfile-engine, they should be fairly friendly and point you in the right direction. If you get some obscure, hard-to-understand Jam error, look over your template again and make sure you have all the necessary spaces!

One final note to any PowerPC owners in the audience: since I used the Be makefile-engine as a sort of template for my Jamfile-engine, I have tried to include support for PPC machines similar to what was in the makefile-engine. But since I only have an x86 machine I was unable to test this functionality. If anyone would like to test out the Jamfile-engine on a PPC machine and let me know how it goes I would appreciate it!

In my next article I will walk through the implementation of the Jamfile-engine and give more details about how Jam works and how you can put it to more advanced use in any projects you may be working on.

 
Abstractions by Michael Phipps 

Many of the gains that Computer Science has made have come about from abstracting some amount of detail from the "user".

One of the very first was the development of programming languages, starting with assembly language. The very first programmable computers used machine language - there were no assemblers. Instead of "move.l #0,A0", which is almost readable, they might use 0F C5 A2. Abstracting human beings from the instruction set made it easier for those pioneers to build programs. Possibly the next giant step would be the development of subroutines (aka functions). This enabled libraries of common code to be built, allowing developers to share previously created work.

Eventually, it became clear that other people would need to use computers. Scientists and business people needed the ability to write instructions for the massive systems that were being built. Fortran and COBOL were born to meet this need. Both of these languages abstracted the system in different ways. Fortran, coming first, was a very minimal compiler - it didn't offer many of the features that we would consider critical today. It did, however, allow scientists to write things like x=y^2+z on one line, in a manner similar to the way that they did it on paper or in a lab. Their needs were met. Business people don't deal as much in formulas - they think in "normal English". They needed a language that was very English-like but still able to work on the primitive computers of the day. COBOL meets that need very nicely. Both of these languages abstract the user one more step away from the raw machine.

History views both of these steps in a very positive light. While there were incremental improvements and new languages (C is really a vastly improved Fortran, for example), the next level of abstraction is really object oriented languages. The jury is still out, to some degree, on just how much more productive and powerful object oriented languages are over procedural languages. Starting with Simula, then (notably) Smalltalk, C++, etc., object oriented programming doesn't abstract the user more from the machine as much as it abstracts him differently--instead of perceiving code and data, the programmer now perceives only objects. This combining promises to allow the user to think in a more natural, instinctive manner. When you think of a drill, you know that you can start it, stop it, change bits, etc. The object and the actions that you can perform with it combine in the real world, so it would seem logical to combine them in programming.

All of this is review, though, to talk about the two topics that are really at hand. The first is visual programming. Back in the "good old" days, there were flow charts - visual diagrams showing the flow of code. These are excellent tools for visual thinkers to see how their code works. For the rest of us, I believe, they were just another class work exercise. The real advantage to them for non-visual thinkers is that they force the programmer to think while not in front of the computer. Visual programming, on the other hand, focuses on using visual components to actually build applications. One example of "true" visual programming is AmigaVision. While it is out of print (and more than 10 years old) it is an excellent example of this paradigm. I knew people who used it to great advantage, non programmers building professional looking (albeit simple) applications. Most of what people call visual programming today is really not true visual programming. It is drag and drop screen layout. Which is a fine way of doing screen layout and is a good thing. But not the same as true visual programming.

The second topic at hand is something that I have just learned enough to be dangerous about: aspect-oriented programming. Object oriented programming wraps procedural programming in a new wrapper--objects. Back in the procedural (Fortran/COBOL/C) days, you had functions that took parameters and returned values. In OO, the functions still existed, but were called methods and made part of objects. AOP allows you to consider your software from many different perspectives. One perspective might be security. AOP allows you to gather all of your security related code into one unit of compilation (a file, or a class). The security officer for an application could then review the security section far more easily. Additionally, all of the code is in one place, making refactoring and redesign easier. Another aspect that makes sense is logging. With AOP, you can put all of the logging code in one place. Turning logging on and off then becomes trivial. AOP allows you to modify already-compiled code without recompiling. It does so with JVM support (it exists only for Java and Python, AFAIK). An AOP method specifies a "class method"--a normal OO method to run before or after.

Everyone in computer science will admit, if they are being honest, that all of these "paradigms" have problems. Not that, of course, I have mentioned all of the paradigms--I unfairly left out half a dozen or so in the interest of space and time. All of the paradigms have problems. A new design, though, is starting to become clear, at least to me. A design that is visual, with OO and AOP, storing the code in a database with tagged attributes. A design that allows intermixing of languages through a simple, common calling scheme. Maybe someday...