Haiku API Bindings
Basics
Not logged in

Documentation | Manual: Basics

Chapter 1: Basics

This manual is largely inspired by darkwyrm's two series of C++ programming lessons for Haiku. While the text is my own, the programs are in some cases inspired by and in other cases simply ports of the programs he used in those lessons.

Namespace

The top-level namespace (package, module) is HaikuR1 instead of Haiku; this was done so that when R2 is released, these bindings can be compiled on R2 using the planned compatibility layer, and programs created with these R1 bindings can then be used unchanged on R2.

Perl

To save on typing, instead of this:

   use HaikuR1::Application;
   use HaikuR1::Window;
   use HaikuR1::View;

   my $app = HaikuR1::Application->new(...);
   my $window = HaikuR1::Window->new(...);
   my $view = HaikuR1::View->new(...);

   if ($HaikuR1::Error != B_OK) {
       # ...
   }

you can write this:

   use Haiku::R1;

   use Haiku::Application;
   use Haiku::Window;
   use Haiku::View;

   my $app = Haiku::Application->new(...);
   my $window = Haiku::Window->new(...);
   my $view = Haiku::View->new(...);

   if ($Haiku::Error != B_OK) {
       # ...
   }

Terminology

API code

The underlying C++ code of the Haiku API.

native code

Code in the binding language. Currently Perl and Python are supported.

glue code

Code (typically C) that allows native code to interact with API code.

native string

A string; depending on the function or method, it may contain non-printable characters and even null bytes.

native integer

Haiku has eight integer types: 8-bit, 16-bit, 32-bit, and 64-bit, in signed and unsigned varieties. In general, you don't need to worry about what kind of integer it is on the C++ side; it will just be automatically converted for you.

However, in some cases, you need to be aware of the integer type. For example, integers contained in Messages will be stored in one of those eight types, and you need to know what kind the source has sent, or what kind the target expects, and use the proper Message method to add or retrieve the integer.

native floating point numbers

As with integers, you generally don't have to care whether the C++ side is using a float or a double, but it does matter with Message data.

boolean

The bindings will use the language's own rules when converting to boolean; any value which your language would treat as true is converted to true; any other value is converted to false. When converting from a C boolean, your language's boolean type is used.

Perl

Since Perl does not have a separate boolean type, the pattern used by builtin functions is followed: 1 for true, undef for false.)

native list

Perl arrayref, Python list. This is generally converted to/from either a C array or the Haiku API's BList object.

native map

Perl hashref, Python dict. This is generally converted to/from a C struct.

pointers

C pointers are, in and of themselves, pretty much useless in other native code. Therefore, functions and methods which take or return pointers will take an additional parameter (not found in the C++ version) representing the type of the data. This can either be a numeric type constant or a class.

Perl

$message->AddPointer("ptr1", $some_int, B_INT32_TYPE);
$message->AddPointer("ptr2", $handler, 'HaikuR1::Handler');

Python

message.AddPointer("ptr1", some_int, B_INT32_TYPE)
message.AddPointer("ptr2", handler, HaikuR1.ApplicationKit.Handler)

Using the type constant B_RAW_TYPE will result in the pointer being converted to/from an integer. This will not do you much good in native code, but at least the pointer can be accessed and passed to other C++ functions.

empty value

Perl undef, Python None. This represents a C NULL.

errors

Haiku API functions and methods generally return an integer status value in the case of an error. Each binding language handles these errors in its own way.

Perl

By default, Perl returns undef and sets $HaikuR1::Error to the error code. (This is a dual value scalar; in numeric context it is the integer code, while in string context it is the corresponding error message.) However, if $HaikuR1::autodie is set to a true value, Perl will instead die. It will still set $HaikuR1::Error, and this is also the value that will be passed to die.

On success, these methods will instead return some defined value. For example, DataIO.Read returns a string containing the bytes read, and DataIO.Write returns the number of bytes written. (The value could be false but defined, such as an empty string or 0.) In other cases, methods simply return 1 as an indicator of success.

Python

Python raises an exception. args[0] is set to the error code, and args[1] is set to the error message. The exception's message will be args[0]: args[1].

On success, these methods will return a value if the C++ method does more than simply indicate success. For example, DataIO.Read returns a string containing the bytes read, and DataIO.Write returns the number of bytes written. In other cases, methods simply return None.

hook

A method that is called on your object by the system. They typically respond to an event, either user-generated (for example, KeyDown) or system-generated (for example, Draw).

In most cases, you will not need to override the hooks, since the default behavior is usually what you want. But if you want to give your button a distinct appearance, you can override Draw and draw it yourself. Or if you want to react to keypresses, you can override KeyDown.

One case where you will need to override a hook is your application or window's message loop, where you usually override the DispatchMessages hook in order to respond to custom messages.

Some hooks are "pure"; i.e., they will be called in reaction to an event and you should never need to call them yourself (unless you're testing something). Looper.DispatchMessage is an example of a pure hook.

Other hooks are "mixed-use"; i.e., sometimes they are called by the system and sometimes you call them yourself; an example of this is Looper.Quit; when an Application quits, it will call Quit on all its Windows, but you can also quit a Looper (or subclass such as Application or Window) yourself. These mixed-use hooks are usually listed under Methods and not under Hooks.

Hooks are usually methods of a subclass, but one object, MessageFilter, gives you the option of passing in a hook function when creating an instance of the default MessageFilter class, instead of subclassing and overriding a hook method.

Ownership

Although many scripting languages have garbage collections mechanisms, C++ does not. (Modern C++ has solutions for this, but the Haiku API does not, since it maintains compatibility with the Be API, which was created using an older version of C++.)

This means that something has to take responsibility for freeing up memory by deleting C++ objects when they are no longer in use. In this documentation, that responsibility is called "ownership".

When you first create an object, the native wrapper object owns it. However, many objects will take ownership of other objects. For example, when you add a Handler to a Looper, or a View to a Window, the new parent object takes ownership. Similarly, when you remove the object, the parent passes ownership to the native wrapper returned by the call that removed it.

However, some objects do not take ownership when you might expect them to; one notable example is that ListView does not take ownership of the ListItems passed to it. So when you add ListItems to a ListView, you need to make sure the native ListItem wrappers remain in scope as long as the ListView is using them.

Threading

Each Looper runs in its own thread; since applications generally have at least one Window in addition to the Application, that's a minimum of two threads for most applications. (Not to mention that Alerts and FilePanels have their own threads as well, although these threads generally do not run native code.)

However, you do not need to worry too much about threading in your native code. The glue code locks the interpreter so that only one thread at a time can run native code (although other threads can be running C++ code). When you call into the Haiku R1 API, the glue code will unlock the interpreter, allowing other threads a chance to lock it and run native code.

You do, however, need to be aware of the general issues of threading. For example, sending synchronous Messages will block the calling thread until it gets a reply; if the target thread is waiting for something from the calling thread, then both threads will block forever.

You also need to be aware that every time you call into an API function, there is the possibility that some other thread ran native code while you were waiting for your API call to return, and therefore some of your native code's data may have changed.