Be Newsletter
Issue 102, December 3, 1997
Table of Contents
Be Developer Classifieds
Be Developer Classified are an opportunity for BeOS
developers to tell each other about projects they're working
on, are thinking about working on, or would like to work on.
Here's your chance to request assistance on a project, or
make your talents known to other projects.
For more information or submissions guidelines, send e-mail
to newsletter@be.com.
- BeOS Software: OmicronSoft's EasyInstallerMaker
EasyInstallerMaker, version 0.1.0, creates installation
instructions for BeOS applications, files, libraries, and
documentation. The developer chooses the folder containing
the application, files, and/or other folders they wish to
install and selects standard or custom destinations.
Generated EasyInstallerMaker file is used with
EasyInstaller, which installs your software on your
customer's machine.
Currently available at BeWare and OmicronSoft. Downloaded
version provides partial functionality.
See web site for features and more details:
http://www.inetworld.net/tegen/OmicronSoft/
Contact John Tegen, tegen@inetworld.net, with questions or
for more information.
- BeOS Developers: Marketing Materials Assistance
2D, 3D product developers! This Energetic Expert with a
BSEE, 9 years of real world animation production
(features/series/CD-ROM) and software product
development/coordination skills wants to help YOU define,
enhance, and/or create materials for showcasing your new
products!
My resume and art samples are available for review. Please
contact Kurt Wiley at either kwiley@earthlink.net or (650)
570-6891.
- BeOS Project: Visual Programming Tool
Any developers in the Midwest region interested in writing a
development environment for the BeOS that is similar to
Delphi/C++ Builder? I think a tool like this would immensely
speed development on the BeOS and be truly useful.
If you're interested in working on the project, contact Jim
Crafton ddiego@one.net.
BE ENGINEERING INSIGHTS: Writing Add-ons in C++
By Cyril Meurillon
When the compiler sees a function, it creates a symbol that
encapsulates the function's prototype. This is called "name
mangling"; the mangled symbol name is how your application's
compiled code identifies the functions that it calls.
It used to be that few application developers needed to
worry about how a function's name was mangled. But as more
and more programmers use "add-ons," the issue of
understanding and decoding mangled names becomes more
important. This article explains name mangling basics, how
mangling affects add-ons, and what you can do to make your
life (and the lives of the developers that will load your
add-ons) easier.
Here's a simple name-mangling example. Compiling for the
PPC, the Metrowerks compiler turns this function
declaration:
int fred(unsigned long, int *);
into this:
fred__FUlPi
The amusingly euphonious "FUlPi " actually means something:
- "F" indicates that this is a function.
- "Ul" represents the type of the first argument (unsigned long).
- "Pi" (pointer to int) is the type of the second argument.
Let's look at an example of a C++ member function:
void foo::fred(const char *, short);
This becomes:
fred__3fooFPCcs
- "3" is the string length of the class name ("foo").
- "foo" is the class name.
- "F" is the function indicator.
- "PCc" stands for "pointer to const char" (the first argument).
- "s" stands for short (the second argument).
Now assume you're writing an add-on in C++. Let's consider
first the simple case of exporting a function. (We'll look
at exporting classes later.)
Here's our add-on source:
// declaration, possibly in a separate header file
#pragma export on
int fred(unsigned long, int *);
#pragma export reset
// definition
int fred(unsigned long i, int *p)
{
return p[i];
}
Here's the code that loads the add-on, does the
symbol lookup, and then invokes the function:
image_id imid;
int (*f)(unsigned long, int*);
static int p[] = { 9, 2, 7, 1 };
// Load the add-on
imid = load_add_on("my_add_on");
if (imid < 0) {
... // add-on not found
}
// Find the symbol for the fred() function
if (get_image_symbol(imid, "fred__FUlPi",
B_SYMBOL_TYPE_TEXT, &f) != B_NO_ERROR) {
... // symbol not found
}
// Invoke the function
printf("f(%d, %.8x) = %d\n", 1, &p[0], (*f)(1, p));
...
Tres bien, you think. But you have sinned in your ignorance.
The mangling scheme that I have just described applies to
the PowerPC compiler. It does not apply to the Metrowerks
compiler for Intel. That means you have just written code
that is not portable. Our function fred() would actually be
mangled this way for Intel:
?fred@@YAHKPAH@Z
Just as cryptic as the PPC -- and even more fun to say out
loud.
The best solution to this problem is to avoid it altogether
by "externing" your add-on functions. The fred() function
declaration, for example, would look like this:
#pragma export on
extern "C" int fred(unsigned long, int *);
#pragma export reset
The definition remains unchanged.
The lookup code also remains unchanged, with the exception of
the symbol name that is now unmangled:
// Lookup the extern'ed fred() function.
if (get_image_symbol(imid, "fred",
B_SYMBOL_TYPE_TEXT, &f) != B_NO_ERROR)
This code (when compiled for the target platform) will run
on both the PPC and Intel versions of the BeOS.
Let's now talk about how to export a class from an add-on.
Because the class constructors and destructor are mangled,
we cannot invoke them directly in a portable manner. The
solution is to write and export a function that creates an
instance of our class. Of course, this function needs to be
externed to avoid name mangling.
Let's say we want to export a class Pet .
Here's Pet.h :
class Pet {
public:
Pet(const char *name);
virtual ~Pet();
virtual const char *name();
private:
char buf[32];
};
And Pet.cpp :
#include <string.h>
#include "Pet.h"
#pragma export on
extern "C" Pet * instantiate_pet(const char *name);
#pragma export reset
Pet *instantiate_pet(const char *name)
{
return new Pet(name);
}
Pet::Pet(const char *name)
{
strncpy(buf, name, 32);
buf[31] = '\0';
}
Pet::~Pet()
{
}
const char *Pet::name()
{
return &buf[0];
}
And here's the application code that loads the add-on and
instantiates the object:
#include "Pet.h"
...
image_id imid;
Pet *myPet;
Pet *(*f)(const char *);
imid = load_add_on("Pet");
if (imid < 0) {
... // add-on not found
}
if (get_image_symbol(imid, "instantiate_pet",
B_SYMBOL_TYPE_TEXT, &f) != B_NO_ERROR) {
... // symbol not found
}
myPet= (*f)("fido");
printf("pet name is %s\n", myPet->name());
delete myPet;
...
Note that only instantiate_pet() is exported from the
add-on. Because all the functions called from the
application are virtual (Pet::~Pet() and Pet::name() ), their
invocations go through the virtual table, which is pointed
to by the instantiated object. Therefore no symbol of class
Pet is explicitly referenced by the application.
In a future article, we'll look at a more difficult -- and
more practical -- example, such as subclassing BView in an
add-on.
A Remembrance of Things Past: Processor-Independent Device Drivers
By Arve Hjønnevåg
If you have previously written or are now writing a device
driver for BeOS you may be interested in learning how to
make it work on the upcoming BeOS for x86 hardware. In order
for a driver to work on different hardware platforms, it
must be independent of any special processor feature. To
help you avoid creating dependencies, I've formulated four
general rules for writing device drivers:
- Do not use assembly language without providing a
fallback for other processors.
- Use endian-independent code to access external devices.
- Use system functions for all timing and delays.
- Use macros to access ISA I/O.
If you follow these rules, a simple recompile will make the
driver work on a different processor, in most cases. For
further explanation, read on.
First, if you use assembly language, always provide a C
version of the same code and use the preprocessor to include
the right code at compile time. A simple example might look
like this:
#if __POWERPC__
__asm long long
longlongzero()
{
li r4, 0
li r3, 0
blr
}
#elif __INTEL__
__declspec(naked)
longlongzero()
{
asm {
mov eax, 0
mov edx, 0
ret
}
}
#else
long long
longlongzero()
{
return 0;
}
#endif
Second, the device that the driver controls may have
commands and arguments in a specific byte order. Let's say
that our device has a 16-bit command register mapped in
memory at address MYCMDADDR . The device expects a command in
little endian byte order and it defines two commands:
LAUNCH =0x0100 and DISARM =0x0001.
To make the driver source code processor-independent, use
the macro B_HOST_TO_LENDIAN_INT16(arg) , defined in
<support/byteorder.h >, whenever you write to the command
register:
void
guard_thread()
{
while(1) {
*MYCMDADDR = B_HOST_TO_LENDIAN_INT16( DISARM );
snooze(1000000);
}
}
The third item on the checklist is timing. Take a device
that has a command register but provides no acknowledgment
that the command has been received. If a command is written
to the command register before the device reads the first
command, the first command will be overwritten. Since we
know, however, that the device processes a command in 5
microseconds, if the driver needs to write two commands, we
just put a 5 microsecond delay, spin (5) , between the two
commands. In cases when the register is used by
different functions, the driver may work fine without adding
any delay -- though this is bound to break on a future
system.
One way to ensure that no command is missed is by always
adding a delay after sending a command. But although this
method always works, it is only efficient when commands are
sent in large chunks. A better way to solve the problem is
by always accessing the command register through one
function. This will add a delay only when it is needed. The
following function shows you how to do this:
void
SendCommand(command_t command)
{
static bigtime_t lasttime = 0;
while((lasttime + 5) > system_time()) ;
*COMMANDADDR = B_HOST_TO_xENDIAN_COMMAND_T(command);
lasttime = system_time();
}
Fourth and finally, we need to macro-ize access to ISA I/O.
On PowerPC hardware the ISA I/O space is mapped in memory
and can be accessed by normal memory operations, while the
x86 hardware uses special I/O instructions to do this. The
next release of BeOS will have predefined macros that allow
the same code to do ISA I/O on both platforms. These macros
are not defined in PR2, but using your own macros to read
and write to I/O addresses will make the switch easier.
Using macros to access I/O changes the previous example to
look like this:
void
SendCommand(command_t command)
{
static bigtime_t lasttime = 0;
while((lasttime + 5) > system_time()) ;
ISA_WRITE_COMMAND_T(COMMANDPORT,
B_HOST_TO_xENDIAN_COMMAND_T(command));
lasttime = system_time();
}
Now, when you receive the next BeOS release, you can define
ISA_WRITE_COMMAND_T to expand to the corresponding
system-supplied macro.
DEVELOPERS' WORKSHOP: Axe
By Doug Fulton
"Developers' Workshop" is a new weekly feature that
provides answers to our developers' questions. Each week, a
Be technical support or documentation professional will
choose a question (or two) sent in by an actual developer
and provide an answer.
We've created a new section on our website. Please send us
your Newsletter topic suggestions by visiting the website
at: http://www.be.com/developers/suggestion_box.html.
This week's request was sent in by Mrs. Rose Hovick of
Seattle, Washington:
"Can I have an example of a working app that uses the
General MIDI synthesizer?"
Sure. If you make a donation at the $0 dollar level, you can
download a program called Axe from the MIDI section of Be's
source code site:
ftp://ftp.be.com/pub/samples/midi_kit/obsolete/Axe.zip
Axe is a mindless sound-generating program with a simple,
no-help-needed interface. It displays a round playing field
that looks something like a radar screen; it also displays a
two-tiered palette of instruments, divided into orchestral
groups.
Choose an instrument (from the lower palette) and then click
in the radar screen; you've just composed the first note of
the rest of your life. Choose another instrument, click
another note. Lather, rinse, repeat. Don't force the music,
it will come to you. It will breathe, expand, and, frankly,
it will sound like garbage. Axe isn't Art.
When you click in the playing field, a little circle appears
under the mouse. When the little red bead that's circling
the radar screen passes by your note circle, a noise is
made. The pitch of the noise depends on how close you are to
the center of the radar: Pitch heightens as you approach the
perimeter. To erase a note, click on the little note circle.
To erase everything, hit the space bar.
This all may sound like a joke -- and it is, sort of -- but
it's the kind of joke that you can waste many hours laughing
at. And it comes with source code so you can make it even
funnier. The code isn't heavily commented, so I'll point
out some features and caveats:
- Axe uses the big General MIDI synthesizer
(
/boot/beos/etc/synth/big_synth.sy ). If you don't already
have a copy of the synthesizer, you can download it by
going to:
http://www.be.com/products/beos_download/packages.html
- The entire synthesizer file is loaded when the app is
launched, so start-up time takes awhile -- ten seconds or
so, depending on the speed of your machine. This is an
eternity compared to most apps.
- The two circles within the radar screen designate the C
below middle C (the inner circle) and the C above middle C
(the outer circle). Musically, this is the "sweet zone"
for many instruments, although for our purposes, it's just
as interesting to avoid this section -- try dropping a
clump of baritone sax notes an octave or two below this
zone for a satisfying growl.
- Don't be timid or discrete. You can't write music with
this thing, all you can do is make noise, so the more
notes the better.
- You can't change the speed of the radar's sweep. And, if
you're a code-fiddler, I implore you not to add such a
feature. Dogged periodicity is Axe's most important
characteristic. If the frequency of the sweep were
alterable, the illusion would fall apart.
- The menu items aren't connected to anything. I was going
to put in some wire so you could save a "performance," but
I forgot to list it as a goal.
- The cpu-wasting graphics look best in 32-bit.
Here's one of my favorite Axe tricks: Lay down a series of
low-pitched timpani notes that circumnavigate the radar.
Look for the instruments called "Halo" and "Manhole" in the
"Music FX" section; put in a few of lines of these in the
sweet section -- combined, they sound like an angel with a
hangover. Now stare at the center of the radar for awhile
(the "burn in" circles) and pretend you're floating five
feet above your body. Instant near-death experience.
UI and the Integrated Browser
By Jean-Louis Gassée
I've been toying with idea of a tongue-in-cheek column in
which I would claim that the Department of Justice was suing
us for bundling a browser with the BeOS. Clearly, in our
case (the column would continue), Janet's hounds weren't
sniffing the foul odor of monopolistic abuse of power.
Instead, their warrant was misguided egalitarianism, a
posture of fairness in going after the smallest as well as
the mightiest of offenders.
But with the Bill and Joel (Klein) show entering a dull
third act of highly technical lawyering, my proposed column
lost its edge. We'll know more by Friday, but the smart
money is betting on a protracted, confusing fight that will
ultimately be rendered moot by Windows 98 and its integrated
browser.
Regardless of the legal outcome, and even though the
integration of Explorer isn't really "done" yet, we can
already recognize the influence of Web browser UI on general
UI ideals. Like it or not, Web browsers are changing the way
desktops look and feel. It may be half-baked, but with
Windows and Explorer we have a large scale experiment in UI
integration. So far, the results are mixed:
- The Big Idea was to "seamlessly" integrate all sources
of data. My hard disk and my favorite Web site were to
look and feel the same. A nice idea -- but, currently,
not a practical one: Remote data could easily be made to
look like the contents of a hard disk, but until bandwidth
limitations are overcome, the "feel" won't be the same.
Of course, we'll have DSL lines or cable modems in a
not-too-distant future but, as a somewhat privileged T-1
user, I can attest to the fact line speed isn't
everything. The entire Web fabric needs an overhaul
before we can fuse the UI paradigms (that's the one and
only time I'll use the "p" word today).
- Conversely, making the contents of a folder look like a
Web page doesn't improve my ability to navigate large
amounts of data and, in some cases, increases the feel of
clutter.
- The "Channels" experiment is even less convincing but,
as it is even younger than the browser, we can leave it
for another day.
- "Push" has been criticized for being nothing more than a
classical client-server interaction in disguise -- but
isn't disguise the basis of a UI? E-mail is a very
successful push application when you're on a LAN, and
reverts to the original form with a dial-up connection.
Hopefully, with time, we'll become better at sorting out
the types of data we want pushed to our desk and the ones
we'd prefer browsing for.
- Some other modest "improvements" have fallen out of
browser integration, such as auto-highlighting when the
cursor hovers over an object, and one-click action (as
opposed to selection). Nothing to criticize here -- but
these aren't really new. If memory serves, we saw these
with HyperCard ca. 1987.
So, aside from a few nice tweaks, the merger of the browser
UI with the more conventional desktop feels forced. After a
while, most users turn off the Web-like desktop for a
simpler, more intuitive icons-and-folders mode, saving Web
mode for Web interaction.
Modality -- having different ways to approach different data
-- has long been bemoaned as wrong-headed and overly
restrictive. An illustrious computer scientist acquiantance
still sports a "NO MODES" license plate. This was a
meaningful protest in the days when the UI controlled (or
"guided," in softspeak) the user. But what we're starting to
find, in this experiment, is that if the context is truly
different enough, we like modes. You can't hammer a nail
with a loaf of bread.
|