|
|
Table of Contents
BE IS HIRING!
The Be employment page is chock full of job openings.
Currently we are aggressively trying to fill positions such
as Japanese Program Coordinator, Developer Technical Support
Engineer, Media Engineer, Reseller Program Manager, and
others. Be all that you can Be:
[http://www.be.com/aboutbe/jobs/index.html]
BEDEPOT.COM: Shopping is a Feeling
By Melanie Walker - melanie@be.com
If I were in Marketing, I would come up with a tag line for
BeDepot.com that went something like this: "Using the BeOS isn't
hard, shopping for it shouldn't be either." As David Byrne
said in True Stories, "Shopping is a feeling."
Unfortunately, of late, the BeDepot.com that we have all come to
know and love has been incredibly difficult to use, and the
prevailing feeling for users, developers, and the BeDepot.com
team alike has been one of frustration.
How did this happen? Putting it succinctly, BeDepot.com was
conceived as a simple but elegant solution for web commerce
in an era that pre-dated the distribution of physical
software, a break in the binary compatibility of the BeOS,
and the idea that developers would want to distribute
binaries for PPC and Intel separately. We have augmented the
system to handle all of these tasks, but not quite
seamlessly. This, coupled with a few select design problems
inherent in SoftwareValet has caused users some grief.
The good news is that we are working to put real solutions
in place that will make shopping at BeDepot.com a pleasure once
more. Most notably, we will be introducing "WebValet" which
gives users online access to all available updates for the
software you've bought through BeDepot.com. In the mean time, we
have come up with the following workarounds to the more
common problems that users are encountering.
Common Problems Moving from R3 to R4
Problem: Lost SoftwareValet settings.
As people move from R3 to R4, a common problem is that
they inadvertently overwrite their SoftwareValet settings
as they do a clean install. SoftwareValet settings are
generated whenever you install a PackageBuilder package.
They are generic files that BeDepot.com uses to update
your software. Without these settings, you can't update.
- Workaround 1: If you back up your R3 SoftwareValet
settings, you can install them in your R4 settings
directory and update as normal under R4. See the
SoftwareValet documentation for more into on how to back
up these settings.
- Workaround 2: If you've already lost your
SoftwareValet settings, you can write to
[support@bedepot.com] and we can regenerate your
settings for you.
Problem: Ordered/downloaded software for the wrong
version of the BeOS.
Some software on BeDepot.com is available for both R3 and
R4. Some software is only available for one or the other.
When you order or download software, it is labeled as
either R3 or R4 software. Be sure to select the
appropriate version for your machine.
- Workaround: If you download the wrong version of an
application, if there exists a version for your machine,
BeDepot.com can get it to you. If the correct version
does not exist for your current version of the BeOS,
BeDepot.com will gladly refund your money. Write to
[support@bedepot.com].
Common Update Problems
Problem: Downloaded wrong update / scrambled update.
Because of the design of the Newer Versions window in the
SoftwareValet SoftwareManager, only one available version
shows up at a time in the window. As a result, it is very
easy to download the wrong version. Unfortunately, due to
a bug in SoftwareValet, you can only update once. Which
means that if you get the wrong version the first time,
you're stuck.
- Workaround: BeDepot.com can arrange for you to get the
update you need. Write to [support@bedepot.com].
Problem: Updating through another browser.
My BeOS partition isn't configured for the Internet. Can I
update through another web browser?
- Workaround: Right now, the only way to update is through
SoftwareValet and therefore you must be using the BeOS.
General BeDepot.com Problems
Problem: Trouble accessing the other version of a product
with platform switching.
Many of the products offered through BeDepot.com offer
"platform switching." That is, if you buy the Intel
version, you get the PPC version for free or vice versa.
However, in practice, if the program is split into
separate binaries for each platform, you can only access
the one you bought.
- Workaround: Currently you can only download the
version that you bought. However, you can obtain the
other platform version through BeDepot.com directly by
writing to [support@bedepot.com].
Problem: ODBC Errors.
From time to time, BeDepot.com users encounter ODBC
errors. These are often due to timeouts and seem to occur
most often during peak hours. However, on occasion, these
are warnings that you have encountered a bug in the
system.
- Workaround: If you encounter an ODBC error, and it looks
like a timeout, chances are good that the server is just
busy. Wait for a while and try again later. If the ODBC
error you get looks like it is something other than a
timeout, feel free to copy the error message and send it
to us, so we can identify and fix the problem. You can
send bug reports to Customer Support at:
[support@bedepot.com].
Problem: R4 Update Delays.
You ordered your update a while ago but you still haven't
received a copy. You're sure that you've registered your
copy of the BeOS on the Be web site, too.
- Workaround: If you have registered your copy of the BeOS
and you still haven't received your copy of R4, you can
look up the status of your order on the BeDepot.com web
site in the Your Account area, under Order Status. If
your order is listed as "On Hold," chances are good that
your order is on hold while we look up your registration
and confirm it. If your order has been on hold for more
than two weeks, please notify Customer Service at:
[custservice@bedepot.com].
For Further Information
In addition to this list, good resources for BeDepot.com
information include the BeDepot.com Help Area
[http://www.bedepot.com/Support/index.html] and the
SoftwareValet FAQs.
When you need to contact a human being, we've got that
covered, too. For ordering issues, you can e-mail
[custservice@bedepot.com]. For technical support issues
regarding BeDepot.com, you can e-mail [support@bedepot.com].
For technical issues regarding the BeOS proper, please
e-mail [support@be.com].
BE ENGINEERING INSIGHTS: Writing a Modular Color Picker
By Pavel Cisler - pavel@be.com - and Robert Chinn - rudeboy@be.com
Here's an idea we've wanted to try out for a long time. It's
always amazing how many cool color pickers there are for
BeOS: roColor, for a start, and more in Gobe Productive,
ArtPaint, new Mail-It, Pe, and other applications. What if
you could choose the coolest one and hook it up as the
preferred color picker for all the apps on your disk? But
because different apps may need different color pickers, it
would be best to have both a default picker and application
specific pickers. If color pickers are written as a separate
module, we should be able to choose the preferred
application for color selection in FileTypes, the same way
we can select a preferred application for
"text/source-code ." Here's how it could work.
A color-defining label/view interacts with a color picker
module using a messaging suite, designed for selecting a
color. The messaging suite is well defined, in the same way
that something like the format of an RTF document is. The
suite has a MIME type associated with it, for example,
"application/x-vnd.Be.colorPicker ". When a color label is
invoked, the preferred application for
"application/x-vnd.Be.colorPicker " is launched and a
messaging protocol is established.
For this to work right, we need to integrate the color
picker application and the color label that launches it
really well. Ideally it should seem as if the module, a
separate application, were really just a color picker dialog
in the same application.
The messaging suite will let us do the following:
- Launch the color picker application and establish a connection.
- Pass in an initial color value.
- Pass in the name of the target color label that the color picker can use to identify its target (in the window title, for example).
- Pass in the initial click location to allow positioning of the color picker.
- Send color updates from the color picker module to the client color label.
- Send color updates back in the other direction -- if the color label is updated, say by drag and drop, the color picker has to catch up and send Apply, Cancel, and possibly Revert messages back from the color picker to the color label.
- Break down a connection when the user closes the color picker.
- Quit the color picker when the invoking color label goes away.
If we can handle all of actions, the color picker should be
pretty well integrated.
Included with this article are three small apps that
demonstrate how to implement this scheme. One is a simple
application with two color labels you can use to set color
values in preference panels (for example). The other two are
different color picker panels.
You'll find the source code for this article at
[ftp://ftp.be.com/pub/samples/application_kit/colorPickerModule.zip]
The code contains most of the fun stuff in this article --
we'll just point out some of the important parts here.
The first color picker is very limited; it just uses the
built-in BColorControl . The second one is less trivial; it
implements a BColorControl -like crayon color picker.
The sample application uses a ModuleProxy class to manage
the connection to the color picker module. This is done more
for tidiness than anything else: ModuleProxy makes it easy
to hook up the described protocol into color labels like
ColorLabel . It's hard-coded to deal with rgb_color values
but could be easily modified to accommodate different value
types by turning it into a template class.
The destructor of ModuleProxy sends a message to any open
color picker to quit itself, so we don't get color pickers
hanging around when the originating dialog is gone. The
UpdateValue call is used when the target client -- the
ColorLabel -- changes its value without the color picker
knowing. This way the color picker can follow the value by
updating itself.
MessageReceived handles messages from the color picker --
forcing an update of the ColorLabel when the color picker
changes -- and establishes and tears down the connection
with the color picker. Invoke is called from the ColorLabel .
Invoke is a little more interesting: if a color picker isn't
running yet, it launches one, sending it a setup message.
void
ModuleProxy::Invoke()
{
if (connectionOpen) {
module.SendMessage(B_WINDOW_ACTIVATED);
// we already have a picker serving us, pull it up
return;
}
...
uint32 buttons;
BPoint point;
target->;GetMouse(&point, &buttons);
BMessage launchMessage(kInitiateConnection);
launchMessage.AddMessenger(kClientAddress,
BMessenger(this));
// this is the messenger we want the color picker to
// interact with
launchMessage.AddPoint(kInvokePoint,
target->ConvertToScreen(point));
// add the current invocation point so that the color
// picker can position itself near the click
launchMessage.AddString(kTargetName, target->Name());
// add the current invocation point so that the color
// picker can position itself near the click
rgb_color color = target->ValueAsColor();
launchMessage.AddData(kInitialValue, B_RGB_COLOR_TYPE,
&color, sizeof(color));
// add the current color value
launchMessage.AddInt32(kRequestedValues, B_RGB_COLOR_TYPE);
// ask for the type of values we need
if (preferredApp.Length())
// we have a specific preferred application for this
// instance launch the picker - use the application
// signature for this particular client
be_roster->Launch(preferredApp.String(), &launchMessage);
else
be_roster->Launch(type.String(), &launchMessage);
// launch the picker, just use the mime type
// to choose the preferred application
}
Note that Invoke uses the BRoster::Launch(const char
*mimeType, BMessage *,...) call to start up the color
picker. If a signature for the preferred color picker was
specified, it will be launched; otherwise it uses the
default preferred color picker for the MIME type. It's
similar to what happens when you double-click a document: it
may have a specific preferred application signature or it
may just use the preferred application for the document's
type.
launchMessage goes to the color picker application's
MessageReceived :
void
SimplePickerApp::MessageReceived(BMessage *message)
{
if (message->what == kInitiateConnection) {
// This is the initial open message that
// ModuleProxy::Invoke is sending us. Pass it on
// to the new color picker dialog which will
// find all the details in it
colorPicker = new SimpleColorPickerDialog(message);
colorPicker->Show();
return;
}
BApplication::MessageReceived(message);
}
SimpleColorPickerDialog extracts the information about the
dialog position, initial color, window title, etc., and
replies back to the ModuleProxy , completing the setup.
The destructor posts a message to the client application
that the color picker was closed, so if the user clicks on
the color label, ModuleProxy can launch a new copy.
MessageReceived handles the rest of the protocol, closing
the connection, responding to B_VALUE_CHANGED , and posting
the appropriate messages for Cancel and OK.
When you right-click on the color label, you get a pop-up
menu that allows you to select the preferred color picker
application for the label. The code is similar to the one
used by FileTypes to select a preferred application for a
MIME type:
void
ModuleProxy::RunPreferredPickerSelector(BPoint where)
{
BPopUpMenu *menu = new BPopUpMenu("preferredApp");
BMenuItem *item = new BMenuItem("Default", 0);
menu->AddItem(item);
menu->AddSeparatorItem();
BMimeType mime(type.String());
// build a list of all the supporting apps
BMessage message;
mime.GetSupportingApps(&message);
for (int32 index =0; ; index++) {
const char *signature;
status_t reply = message.FindString("applications",
index, &signature);
if (reply != B_NO_ERROR || !signature || !signature[0])
break;
BMessage *tmp = new BMessage;
tmp->AddString("signature", signature);
entry_ref entry;
if (be_roster->FindApp(signature, &entry) == B_OK)
// add the application by its name
item = new BMenuItem(entry.name, tmp);
else
// can't find the app, just use the signature
item = new BMenuItem(signature, tmp);
menu->AddItem(item);
// mark the preferred app
if (preferredApp.ICompare(signature) == 0)
item->SetMarked(true);
}
if (!menu->FindMarked())
// mark "Default"
menu->ItemAt(0)->SetMarked(true);
// make the selected signature preferred
item = menu->Go(where);
const char *signature;
if (item) {
if (!item->Message())
// picked "Default"
preferredApp = "";
else if (item->Message()->FindString("signature",
&signature) == B_OK)
preferredApp = signature;
}
delete menu;
}
If you compile the application and the two color pickers,
note that when you click on a color label, it launches a
color picker application; the title of the window is
identical to the name of the color label; and the initial
color of the color picker matches the color label. You can
right-click on the color label and choose the preferred
color picker for the given label. If you keep the default
setting, you can use FileTypes to pick which of the two
color pickers will be used. If you change the color in the
picker, it's updated in the color label. If you drag and
drop a swatch between the two color labels (click on one of
the color labels and start dragging) to change the label
color, the color picker will follow. If you drag and drop a
color from roColor, it changes to that color. If you quit
the application, the color picker quits too -- which is just
what we wanted it to do!
Some possible enhancements -- fun things to try:
- Allow multiple connections -- not very practical in this case, but each time you Invoke the color label, you can instantiate a new
ModuleProxy , adding them to a list, each time a color picker quits you delete it. You could have two color pickers target the same color label; with a little bit of tweaking you could have it set up so that changing one color picker not only updates the color label, but also the second color picker.
- Use scripting instead of a rigid messaging protocol. The color values can be passed back and forth between the color label and the color picker using the Get/Set property calls.
- Write a modular volume control, etc. You could apply this technique to writing other things than just color pickers.
DEVELOPERS' WORKSHOP: Media Kit Basics: Consumers and Producers
By Owen Smith - orpheus@be.com
"Developers' Workshop" is a weekly feature that provides
answers to our developers' questions, or topic requests.
To submit a question, visit
[http://www.be.com/developers/suggestion_box.html].
Several weeks ago, Sheppy gave us a starting point for
learning how to use the Media Kit:
[http://www.be.com/aboutbe/benewsletter/volume_II/Issue45.html#Workshop]
For many of you, however, that's not nearly enough. You want
to see the creatures that lurk under BSoundPlayer's tame
surface. You want to blast buffers over your media net like
terrified badminton shuttlecocks. You want to run your
fingers through the fertile Media Kit soil. And you need the
whole system at your fingertips, yesterday.
Suits me fine. Here's some code for you to peruse:
[ftp://ftp.be.com/pub/samples/media_kit/SoundCapture.zip]
This app does two things. First, it records sounds, using an
optional voice-activation feature, and saves the sounds as
raw audio files. Second, it loads and plays back raw audio
sounds so you can audition what you recorded .
Straightforward this app is, but simple it ain't. It would
take me much longer than a Newsletter article to explain its
operation from the ground up. So, before you read on, please
familiarize yourself with the basic concepts of the Media
Kit by browsing the freshly painted Be Book:
[http://www.be.com/documentation/be_book/index.html]
For now, you'll want to concentrate on the Media Kit classes
BMediaNode , BBufferConsumer , BBufferProducer , and
BMediaRoster . You might also glance at the header file
MediaDefs.h , which describes many of the lower-level
structures and defines that we use. Then, look at this
article and the sample code to see how it all fits together!
The code is pretty heavily commented, even for a DTS
project, so I'll spare the details and just touch on a few
of the more important issues here. OK -- let's get down and
dirty with the Media Kit...
Under the Hood
There are three key classes that make SoundCapture tick:
SoundConsumer , SoundProducer , and CaptureWindow .
SoundConsumer
SoundConsumer is a franchise of BBufferConsumer ; its job is
to process (i.e., "consume") buffers that are sent to it
from a higher source. We've tried to make this class
reusable by making it reasonably extensible. In a similar
manner to BSoundPlayer , we do this by defining two places
where you, the SoundConsumer client, need to decide what to
do:
- The
Process function, which is called each time a buffer of data arrives. You provide functionality to tell it what to do with these buffers.
- The
Notify function, which is called each time something important happens to the node (it starts, stops, or dies an abrupt death, for example). You provide functionality to dispatch any of these events as you see fit.
You fill in these slots by either subclassing SoundConsumer
and overriding the function, or simply by passing
SoundConsumer a hook function to call instead.
We use the SoundConsumer as a simple recorder. We implement
a Process hook function that writes the buffer's data to
disk, and a Notify hook function to makes sure that things
get cleaned up when the node's about to stop or die.
SoundProducer
SoundProducer is a subsidiary of BBufferProducer ; its job is
to produce buffers like clockwork and pass them along to
someone else. Like SoundConsumer , we provide two places for
you to specialize SoundProducer's behavior: Process, which
is called each time a buffer needs to be filled with data;
and Notify , which is called when certain events happen.
We use the SoundProducer to implement simple playback from a
sound file. In this sense, it functions almost exactly like
BSoundPlayer and BSound (save that it only reads raw audio,
doesn't handle multiple sounds, and doesn't do sample format
conversion).
CaptureWindow
In addition to fulfilling its traditional duties as a
BWindow , CaptureWindow also functions in SoundCapture as the
context in which SoundConsumer and SoundProducer are used.
CaptureWindow contains an instance of each class, and does
the following:
- When you click on the Record button,
CaptureWindow connects its SoundConsumer to the system's audio input and starts the node.
- Similarly, when you click on the Play button,
CaptureWindow connects its SoundProducer to one of the system's mixer inputs and starts the node.
- Finally, when you click on Stop (or when one of the
Process or Notify hook functions has determined that the node is done recording or playing), CaptureWindow figures out which node is running, stops the node, and disconnects it.
Consumer vs. Producer
Looking at the responsibilities of a BBufferConsumer and a
BBufferProducer , you'll notice that there are a lot of
similarities. Here some of the more important ones:
- Both consumers and producers create a port, called the Control Port, that the Media Server uses to send messages to them. Messages range from performance event (Start, Stop, and Seek) requests, to receiving buffers, to messages that you define and send yourself.
- Both create a thread, lovingly referred to as the Big Bad Service Thread, which is responsible for handling messages sent to the Control Port in a timely manner. Among the other things that this thread might do, its primary responsibility is to read from the Control Port until a message is received, and then handle the message.
- Both override
BMediaNode functions that implement certain important performance events. Start tells you when your node needs to start. Stop tells you when your node needs to stop. Finally, Seek tells you when you need to change your media time, and what the new media time should be. (More on this in a bit.)
At the same time, there are several significant differences
that you should be aware of. Here's a blow-by-blow:
Performance Events and Media Time
Your node can interpret Start, Stop, and Seek events in
various ways, depending on what makes sense for your node.
For SoundProducer, Start and Stop are interpreted to mean
"start producing buffers" and "stop producing buffers."
Our application does not currently define the concept of
media time for its sound producer, so SoundProducer::Seek
has no effect. In the future, media time would probably be
interpreted as the offset in the sound file you're playing.
On the other hand, SoundConsumer currently defines no
behavior for Start and Stop, and simply accepts buffers at
any time. It also doesn't do anything meaningful with Seek
requests, but passes the media time as a timestamp to its
Process hook function, in case that time has any meaning
to SoundConsumer's client.
Timing and SoundConsumer
As the BeOS media system generally runs in real time, timing
issues are perhaps the most critical part of developing a
media node, and are often the trickiest part to get right.
SoundConsumer has it easy, since all it needs to do is grab
buffers as they arrive and blast them to disk. It doesn't
even care whether the buffers were on time or not. So, all
it needs to do is sit around in its Big Bad Service Thread,
waiting patiently for those buffers to arrive. Once messages
do arrive, it handles them immediately.
Because it doesn't really care about Start, Stop, or Seek
requests, SoundConsumer "handles" them instantaneously,
instead of queuing them up for handling at a particular
time. More complicated consumers might need to handle
performance events accurately, and might need to determine
whether incoming buffers are running behind.
Timing and SoundProducer
SoundProducer , on the other hand, is a lot more complicated.
Not only does it need to handle performance events at their
correct times, but it also needs to produce a steady stream
of buffers -- and those buffers have to be sent at precisely
the right time, so that they don't reach the final output
(your headphones) too late, or so early that the latency of
the system is more than it needs to be. Its Big Bad Service
Thread, therefore, does three different things:
- It checks to see if any pending performance events need to be handled, and handles them when they do.
- It checks to see if any messages have arrived at the Control Port, and handles them when they do.
- When it's time to produce a buffer, SoundProducer stuffs a buffer (using the Process function) and sends it off.
One of the keys to understanding the timing of SoundProducer
is the timeout value passed to read_port_etc . This value
determines how long the thread waits in each iteration of
the loop for messages to arrive. This timeout is set to the
time until the next performance event needs to be handled,
or until the next buffer needs to be produced, whichever
comes first. So, this call to read_port_etc really serves
the dual purpose of receiving messages and snoozing until
the next thing needs to happen!
The other key to understanding SoundProducer timing is the
value returned by BTimeSource::RealTimeFor() . This somewhat
misnamed function does not give the absolute real time that
corresponds to a given performance time, but rather gives
you the real time that you need to do things in order for
their effects to take place at the performance time. It does
this by taking into account the latency that you give it --
that is to say, you tell it the difference between the time
at which you decide to do something, and the time at which
that something actually takes effect. The greater your
latency, the earlier you must start things for them to
happen on time. And, as I have been reminded on any number
of occasions, being on time is extremely important.
Parting Thoughts
There are several directions in which this app can grow.
In particular, you could extend either SoundConsumer or
SoundProducer to do all sorts of wild stuff. You could
override the consumer's hook functions to provide, for
example, an oscilloscope node, or a node that performs
spectral analysis. You could also override the
producer's hook functions to perform sound synthesis.
Go nuts!
Organic Pace of Change
By Jean-Louis Gassée
If I remember correctly, one difference between organic and
mineral chemistry is the introduction of time as a variable
in the reaction. In the mineral world, the ingredients meet
and react; the result is a fixed one, dependent only upon
the initial conditions. Organic reactions, on the other
hand, introduce another variable -- time. These reactions
are slower, and the result is a function of the time of
measurement.
It's the organic model that comes to mind when I look at the
pace of acceptance of new standards. Some organic reactions
actually never take off; for example, ISDN, a high-speed local
loop in every pot. The appearance of others might be a
little misleading -- here I'm thinking of the Internet,
which might cause problems with placing the time origin of
the reaction. What we observe is the fast middle branch of
the S-curve. We might forget the two decades of slow
gestation of the first branch in government and university
research labs.
With a new standard, we often have the misleading intuition
of a quick reaction, only to find that the actual pace of
adoption is more organic than we'd like. USB appears to be
one such example. On the "obvious" side, it's got to happen
-- because it's good. Higher throughput; peace with
interrupts; self-identifying peripherals; hubs; smaller,
nicer connectors; cheaper; saves trouble; better ergonomics.
USB is promoted by Intel, supported by Microsoft, and has
recently been discovered by Apple. On the Intel promotion
side, the effort started five years ago, if memory serves;
motherboards with USB support appeared more than two years
ago. Software support lagged but became somewhat real (we'll
get to the "somewhat" in a moment) with Windows 95 OSR 2.1.
OSR stands for OEM Service Release -- midstream fixes and
improvements distributed to OEMs. But peripherals
manufacturers had been burned before, and didn't jump to
support USB either.
Now we have Windows 98 with USB support and motherboards
with USB hardware, but very few USB peripherals such as
printers and modems. There are exceptions, such as a Kodak
digital camera, but more than two years after the appearance
of USB connectors on the back of PCs, that's all we have --
exceptions. Or, take the surreal case of the new Macs. Much
has been made of the iMac's lack of floppies; to use them
you have to buy a USB external Superdrive. It's nice, color
coordinated (before the new colors were offered), but a tad
expensive at $149, plus almost $9 for a 120 MB floppy.
One can argue you get the best of both worlds -- a drive
that uses old and new floppies (I think) and offers the
capacity of a Zip drive. But turn your attention to the new,
colorful high-end G3 Macs. They don't come with a Superdrive
or a floppy, though some have a Zip drive. Here the surreal
part is the modem. The internal modem is only available on
configurations ordered from Apple, I'm told. You can't get a
USB modem, the internal modem isn't available as a part, and
there are no drivers for "older" modems connected via a
USB-to-serial adapter.
When I asked a leading Mac retailer how they felt about
this, I got what the Car Guys call the mechanic's shrug.
Hopefully, this will become an anecdote as USB peripherals
and software become available. We, of course, will do our
part Real Soon. But now we wonder about FireWire -- will it
take as much time as USB to become more than "somewhat
real," or will recent efforts by Apple to assert IP rights
to the standard slow its organic growth?
Recent Be Newsletters |
1998 Be Newsletters
1997 Be Newsletters |
1995 & 1996 Be Newsletters
Copyright ©1998 Be, Inc.
Be is a registered trademark, and BeOS, BeBox, BeWare, GeekPort, the Be logo and the BeOS logo
are trademarks of Be, Inc. All other trademarks mentioned are the property of their respective owners.
Comments about this site? Please write us at webmaster@be.com.
|