BeBox Intro
Volume Number: 12
Issue Number: 1
Column Tag: Cutting Edge Technologies
Opening the BeBox 
A programmer’s introduction to the Be operating system
By Peter Potrebic and Steve Horowitz, Be Inc.
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Be, Inc. was founded in 1990 by Jean-Louis Gassée, former president of Apple
Products, to design and build a new computing platform. The first product offered by
the company is a multiprocessing computer called the BeBox. It contains two PowerPC
603 processors and a new, modern operating system.
This article will describe some of the philosophy behind the design of the BeBox.
It will go into some detail explaining the system software architecture with an
overview of the key components. The article also includes source code and a
description of a sample application, to give you a feel for writing software on the
BeBox. For more detailed information on Be and the BeBox, as well as a look at
developer documentation, please visit our web site: http://www.be.com.
Design Philosophy
The BeBox was designed with the technology enthusiast in mind. As Jean-Louis is fond
of saying, “Only the lunatic fringe will lead us to the truly innovative applications.”
Every machine is programmable right out of the box, and comes bundled with
programming tools from Metrowerks and full developer documentation. We do not
expect that the BeBox will be your first computer - it will not run any Macintosh
software nor will it run Windows. However, if you are excited by new technology,
know how to write code, and have a pioneering spirit, the BeBox may have something to
offer you. See Figure 1 for a screenshot showing a few running Be applications.
Figure 1. Sample screen shot
History has shown that it is difficult to predict the markets where a new
computer might be successful. We believe that developers will lead us, and so our goal
is to provide a unique set of system software features, a low-cost hardware platform,
great development tools, and an extensive developer support program. Additionally, we
plan to help our developers reach their potential customers by offering electronic
distribution of their software on the Be web site. From all of this we hope to see new
and innovative applications for markets we could not possibly envision.
The BeBox was designed by an extremely small team over approximately four
years. We started with a clean slate and focused on a couple of key ideas. One of the
fundamental design techniques was to give responsibility for each major system
component to one person. By concentrating the decision-making process, the engineers
were allowed a great deal of freedom without having to justify and get consensus for
every design decision. However, since most of the components were interrelated there
was enough informal interaction to assure that we were all on the same track. This
approach allowed us to avoid some of the pitfalls of overdesign and resulted in what we
believe is a simple, efficient, and fast set of system software components.
Another decision we made early on was to take a C++ light approach. We wanted
to use an object-oriented programming language, but not necessarily to use every
feature the language had to offer. Programmers need not be C++ experts to program
the BeBox. This decision makes the system more approachable by a larger number of
people with a variety of programming experiences. It is also fairly easy to absorb the
Be class libraries and get a simple Be application up and running quickly.
System Software Overview
The design of the Be operating system is based around a client/server architecture,
with a microkernel at the lowest level providing all the OS functionality used by the
higher levels. The developer makes use of a set of client-side C++ class libraries and
C functions to write Be applications.
The system software consists of a number of key components. Several servers
are built on top of the kernel including an application server, storage server, network
server, and media server. The servers run in separate protected memory areas and
implement the majority of system functionality available from the C++ class
libraries. The class libraries are broken up into a series of kits which export the
functionality from the servers to the client applications. Among the kits available to
Be applications are an application kit, interface kit, OS kit, device kit, media kit, midi
kit, networking kit, and storage kit. The details of all of these kits are beyond the
scope of this article. Instead, this article provides a brief summary of each server’s
basic functionality, followed by a description of the major pieces in a simple Be
application.
The Be kernel provides many fundamental system services to the higher-level
servers including the ability to deal with multiple processors, true preemptive
multitasking and multithreading, memory protection, shared memory, semaphores,
virtual memory, shared libraries, and loadable drivers. The kernel scheduler
supports multiple processors in a manner completely transparent to the application.
This and other features of the kernel provide an extremely fast and efficient foundation
upon which the rest of the operating system is built.
The application server is the main server with which applications communicate.
It is in charge of all the window and view management, graphics rendering, font
management, user interface interaction, event handling, and so on. One important
aspect of the Be programming model implemented by the application server and
interface kit is that every window in the Be system is given two separate threads. The
application server maintains a thread on the server side, which communicates with a
client-side thread running in the application’s address space. This allows all windows
to update their contents and respond to user interaction asynchronously, and is a major
factor in the overall responsiveness of the Be system. This also means that any
multiwindowed Be application is automatically multithreaded and multiprocessing with
little work from the developer. You can, of course, spawn more threads for your
application using other classes in the application framework or the lower-level kernel
functions.
Another important server is the storage server. It implements the built-in
database functionality in the Be operating system. The storage server allows
programmers to design tables that describe records, create and delete records, and
receive notification of database changes. With the integrated database, every file on the
BeBox has its own database record. This acts as an extensible storage system, allowing
programmers to create unique tables containing whatever information they want about
a file. The fields in these records (as defined by the table) are then searchable and
viewable by all applications in the system. Additionally, the storage server
implements a “live” query mechanism that allows any Be application to construct a
query and then, if desired, get notified continuously of any changes to the set of records
located by the query. This behavior is seen in the system Browser (an application akin
to the Macintosh Finder) which is part of the operating system, but this service is
available to all Be applications.
The network server provides basic system networking services. It implements
the TCP/IP protocol stack and provides a socket interface resembling Berkeley sockets.
There is also a PPP layer included in the server, and support for Ethernet. Standard
networking tools such as ftp, ftpd, and telnet are included with the Be system.
The media server provides a set of tools for manipulating large streams of
multimedia data. This server handles the transportation of buffers of media data
through a pipeline of interested data handlers. Each handler can subscribe to a media
stream. By subscribing, the handler is given a chance to manipulate or read the data as
it passes through the system. The buffers are implemented as shared memory areas,
which can be read by multiple applications in separate address spaces without having
to copy the buffers between applications. Additionally, you can use the media server to
synchronize separate media streams, using a global scheduling object that allows easy
coordination of audio and video data.
Be Application Basics
A simple Be application will primarily make use of the features in the application kit
and the interface kit. The core objects used by an application are BApplication,
BWindow, BView, BMessage, and BLooper. BLooper (feel free to call it “blooper”) is
the main object used to encapsulate thread spawning and message handling. Each
BLooper object has its own thread that responds to messages sent to it by other
objects. Since BApplication and BWindow both inherit from BLooper they are
automatically message handlers running in their own threads.
Generally, a BApplication object is created first. It runs an event loop in the
main thread of the application, and is used for basic messaging between applications. It
can also be used to exchange messages with windows or other threads in your
application. Additionally, the BApplication object receives notification of files to
open at launch time or once the application is running. The BApplication object is
the central control object from which other windows and objects are created and can be
used to synchronize sharing of global objects within an application.
Most of the communication between threads, windows, and applications in the Be
system is done using a BMessage object. A BMessage has the ability to contain an
arbitrary number of “data” entries to pass information around, as well as a “what”
field to tell the receiver what type of message it is receiving. BMessages are the
fundamental objects used to share information between threads and applications. They
are also used to implement drag-and-drop and the clipboard.
To actually display anything in your application, you must create a BWindow
object and add a BView to it. Each window you create, by virtue of its BLooper
inheritance, will spawn a new thread to handle drawing, messaging, and user
interaction - without monopolizing your application’s main thread. Windows are also
the containers of BView objects, and handle the display of any drawing that takes place
in a view. A BWindow can contain multiple views and, in turn, views can contain other
views. Each view has its own drawing environment and all drawing is automatically
clipped to the bounds of the view. When a window needs updating, it calls the virtual
Draw() function for all the views in the update region. Within Draw() other functions
are used to render graphics primitives, such as bitmaps, lines, shapes, and strings.
Most of the user interface components in the Be system such as buttons, scrollbars,
menus, and others are derived from BView, and handle drawing and mouse events
automatically.
There are many more classes in the Be application framework available to
developers. The ones described above are simply the most fundamental to a Be
application. They provide the basic functionality for an application that wants to
launch and put up a window to do some drawing. Since the entire Be operating system
is relatively small, it is easy for one person to understand it all. It is also easy to
learn and to get an application up and running quickly in the Be environment.
The Bouncing Ball
Now let’s cut to the chase and explore a sample application. It seems that every new
computer platform comes with a bouncing-ball demo. Each platform uses this
application to show something particular or unique about itself. The bouncing-ball
demo on the BeBox, called BeBounce, builds on this tradition - and add adds a unique
twist.
The BeBounce application opens a small window that contains a single bouncing
ball. The twist is that launching a second instance of the application opens a “gap”
between the windows, the size and location being determined by their relative
positions, and the ball is then able to bounce between the two “partner” applications.
As either window is moved around the screen, the gap between the windows follows the
motion, growing larger or smaller and moving around each window frame as dictated
by Euclid (i.e. by geometry). See Figure 2. If the ball happens to enter the gap it is
magically teleported to the other application.
Figure 2. The BeBounce application
In addition to showing some basic Interface Kit techniques, the BeBounce
application demonstrates the straightforward way in which applications can
communicate. As explained earlier, each Be application runs in its own address space
and each window runs in a separate thread. Additionally, each application has a “main
thread” represented by a BApplication object. The multithreaded nature of an
application is inherited from the BLooper class. BLooper objects execute a message
handling loop which responds to messages posted to the looper using its PostMessage
function. The looper then dispatches those messages, in sequence, to the “target”
BReceiver object by calling the virtual MessageReceived function. Note that
BLooper is a kind of BReceiver so a looper can also receive messages.
The BeBounce application consists mainly of one object derived from
BApplication (TBounceApp), one object derived from BWindow (TWindow), one
BMessenger object, and one object derived directly from BLooper (TBall). In the Be
system, BMessage objects can be sent around within an application. However, to send
messages between applications a BMessenger object is needed. The messenger object is
a one-way connection between two applications. In BeBounce, when the ball enters the
gap a message is sent to the partner using the messenger object, literally passing the
ball to the partner.
Let’s take a look at some code, starting with the main() function:
main(int argc, char* argv[])
// to randomize the movement of the bouncing ball
srand((long) sys_time());
// make the new application object and start it running
my_app = new TBounceApp();
my_app->Run();
// application is finished so cleanup and return
delete my_app;
return 0;
}
The basic idea is to create the application object and start it running. The Run()
function returns when the application quits. The TBounceApp object is then deleted
and the program returns. Most applications on the BeBox will have a similar main()
function.
The application object
The BApplication object is the real launching point for most applications, and BeBounce is no exception. Here is the definition of the TBounceApp class:
class TBounceApp : public BApplication {
public:
TBounceApp();
virtual ~TBounceApp();
virtual void MessageReceived(BMessage *msg);
void InitPartner(thread_id tid);
void RemovePartner();
bool SendToPartner(BMessage *msg);
bool SendPosition(BRect rect);
private:
TWindow *fWindow;
BMessenger *fPartner;
The application constructor does several interesting things. First, when the
application is launched it needs to determine how many instances of the BeBounce
application are already running. Here is a portion of that function:
TBounceApp::TBounceApp()
: BApplication(MY_SIGNATURE)
... // some initialization code
/*
This version of BeBounce only supports 2 instances of the application running at
the same time. So if there are already 2 instances running force a QUIT. */
BList list;
be_roster->GetThreadList(MY_SIGNATURE, &list);
long app_count = list.CountItems();