Be Newsletter
Issue 90, September 10, 1997
Table of Contents
BE DEMO TOUR: San Diego, CA and Orlando, Gainesville and
Tampa, FL
- Wednesday September 10, 1997
BeOS Demo at the Macintosh User Group of Orlando (MUGOO)
When:
6:30pm
Where:
Winter Park Civic Center
1050 W. Morse Avenue
Winter Park, FL 32789
- Thursday September 11, 1997
When: 6:15pm
Where:
Campus of The University of Florida
CSE Building, Room A101
Gainesville, FL
- Friday September 12, 1997
The University of South Florida in Tampa / Be User Group
When: 12:00pm (noon)
Where:
Campus of the University of South Florida
Room: SVC 2080
Tampa, FL
BE ENGINEERING INSIGHTS: Time Zone Support in the BeOS, or
Stealing Code to Solve a Problem
By Mani Varadarajan
Anyone who has used the Time preference panel on the BeOS
has seen that, as of the Preview Release, the system
understands the notion of a time zone. In our increasingly
technologically interconnected world, time zone support is
indispensable for allowing applications to have a uniform
and translatable sense of time between the various parts of
the world. The most common and obvious need for this are in
e-mail and news messages, which can originate anywhere in
the world, and need to be sorted and ordered correctly based
on a uniform notion of time.
The time zone problem is not a trivial one to solve
completely. For example, in the American state of Indiana,
where the county seats of authority seem to be more
independent minded than others, several counties break with
the rest of the state and have different policies concerning
Daylight Saving Time (where the clock is moved ahead an hour
for a portion of the year to utilize daylight). Such
vagaries abound in various other parts of the world.
Luckily, the BeOS is not the first system to face this
problem. As all good engineers eventually learn, one of the
best ways of approaching an old and theoretically
uninteresting problem is to adapt someone else's solution to
one's needs. In this respect, because of its long history of
widespread usage, I found that the Unix world solved the
problem most completely and accurately. And since the world
is also fortunate to have access to the full source code
base of multiple free, Unix-like operating systems, I was
easily able to adapt the code to the BeOS.
The BeOS implementation of time zone support is based on code
from the FreeBSD system (http://www.freebsd.org). This
implementation has three parts to it:
(a) a set of time zone "rules" files, in ASCII text
(b) a "rules" compiler that translates these rules
files to binary format ("zic ", or the zone information
compiler)
(c) a set of C routines that understand these binary files
Time zone rules files for virtually all parts of the world
have been compiled by various people on the Net. The latest
versions of these files, as well as source code for the
rules compiler, is available at
ftp://elsie.nci.nih.gov/~ftp/pub.
The format of these files is trivial. For a given range of
years (the rules files are incredibly thorough, going back
in some cases to the 1800s), there is a rule describing when
Daylight Saving Time starts for that zone. Finally, there is
a line describing the offset from GMT for this time zone. For
Chicago, for example:
Rule Chicago 1955 1966 - Oct lastSun 2:00 0 S
Zone North_and_Central_America/Chicago -6:00 US C%sT
are the relevant lines.
The "Zic " compiles the rules files into binary format, one
for each zone, making the BeOS time zone setting just a
reference to one of these files. No magic here. [FYI: These
files reside in /boot/beos/etc/timezones .] At startup, the
Bootscript runs a clock configuration program which checks
to see if the user has a time zone set.
The setting is the config/timezone file in the user's home
directory, and is normally a symbolic link to a file in the
timezones/ hierarchy. If a time zone is set, the system clock
is conditionally warped to report GMT. This is necessary on
dual-boot systems such as the Mac where the Mac OS stores
local time and not GMT in the system clock.
The routines adapted from FreeBSD check to see if a time zone
is set, load the data files and adjust the time reported
accordingly. If a programmer wishes to privately change an
application's notion of the time zone without affecting the
system's setting, she can do this in two ways:
(a) Dynamically, from a shell
(i) type "export TZ=<timezone_setting> "
(ii) launch the application from the shell
(b) From within the program, add this code:
putenv("TZ=<timezone_setting>");
tzset();
Of course, replace <timezone_setting > with either the
complete path name of a time zone data file, or a path
relative to the timezones/ directory. In other words, both
/boot/beos/etc/timezones/Greenland/Godthab and
Greenland/Godthab will work.
Just a simple example of stealing and adapting code to one's
needs. May the thievery flourish!
Tips on Writing Efficient Interfaces
By George Hoffman
Designing a good user interface is not always easy; I know I've
screwed up enough of them in my time. But something which is
sometimes equally difficult is implementing a breathtaking
user interface cleanly and efficiently. While the BeOS's Kits
allow relatively easy construction of user interfaces, making
them efficient (i.e., fast) is not as trivial. I'd like to
share some useful tips gleaned from the Be
graphics/app_server gurus, as well as from my own
experiences writing BeOS user interface code.
Tip #1: Avoid synchronous calls
There are two kinds of app_server calls: asynchronous and
synchronous. An asynchronous call sends off it's message to
the app_server and returns to the caller; in this way the
server can process the client's request while the client
prepares more data to be sent. On a multiprocessor machine,
the client and server threads will be scheduled in parallel.
This is Good. Most app_server calls, including all calls
that do drawing, are asynchronous.
Synchronous calls are calls that require a response, for
instance, those that request input, like BView::GetMouse() ,
or those that query for server-side information, like
BView::StringWidth() or BView::Bounds() . Synchronous calls
are much slower than asynchronous calls, for several
reasons. First, the Interface Kit caches asynchronous calls
and sends them in large chunks at a time. A synchronous call
requires that this cache be flushed.
Secondly, the nature of a synchronous call requires that the
client wait for data from the server before returning, so
not only must the server send data back to the client,
effectively doubling the latency of the call, but the whole
pathway is effectively serialized, as the client blocks in
the kernel waiting for the returned data. Synchronous calls
are Bad.
In general, caching data on the client side is a good way of
avoiding synchronous calls. Applications that do heavy-duty
fast text layout, for instance, should cache character
escapements and compute string lengths locally rather than
calling StringWidth all the time, as Hiroshi described in
Newsletter 82.
Tip #2: Avoid asynchronous calls
"Huh?", I hear you cry, "First you say synchronous calls are
Bad, and now Asynchronous too? Aren't I allowed to call
anything?"
Sure, but there are plenty of ways to reduce even the number
of asynchronous app_server transactions with a little more
intelligence on the client side. Some examples:
- Given that you have many primitives to draw, try to
order your drawing such that all those with the same color
and pen type are drawn one after another. This avoids
color and pen switching between interface elements.
- Exactly the same with fonts: draw all the text in a
single font at the same time, then switch fonts. For
machines with small font caches, this may also prevent
font cache misses.
- On the topic of clipping regions: always be aware of
them, and don't bother drawing things which are completely
outside of it. Costly
app_server transactions must still
be done even for completely clipped primitives, since
clipping is handled by the server.
- Regularly use "batch" primitives like line arrays and
BView::GetStringWidths() .
- Get used to using
BPicture s; since they are stored on
the server side, they are a nice way of packaging up many
primitives for very fast drawing later. Once a BPicture is
created and packed with any arbitrary number of
primitives, only a single app_server transaction is needed
to blast it to the screen.
- Experiment with drawing directly. If interaction with
one view affects another view's appearance, it might be a
good idea to update that view directly by calling
BWindow::UpdateIfNeeded() after invalidating it, or even
by calling its Draw() method yourself (or some other more
specific internal drawing method) to prevent waiting for
the round trip by the app_server update mechanism.
You'll avoid the latency and the update will look
snappier.
ConstrainClippingRegion() can be used to improve speed
and reduce flicker by batching rectangle fill operations.
Simply create a BRegion consisting of all the rectangles
you need filled, then pass it to ConstrainClippingRegion()
and do a FillRect() on that BRegion 's Frame() . Only one
app_server transaction is done, but you can fill an
arbitrary number of rectangles. (Keep in mind, however,
that it can be computationally expensive to create complex
regions.)
Tip #3: Don't abuse offscreen bitmaps
Each offscreen bitmap which views can draw into (a
capability which is specified at BBitmap creation time) has
a server thread associated with it to do its drawing. It's
therefore wasteful to have lots of them lying around. Try to
share offscreen bitmaps between various views that need
them, or, at the very least, don't give every button its own
offscreen bitmap.
Remember that you don't always need an offscreen bitmap to
avoid flicker, and sometimes it is slower to use one than
not. Line drawing and rectangle filling, among other common
operations, are accelerated by most video cards; filling a
rectangle or drawing a line to a bitmap can be significantly
slower than doing so directly to the screen.
Screen-to-screen blitting (using BView::CopyBits() ) is also
commonly accelerated, and will always be much faster than
drawing the region to a bitmap and blitting from main
memory, which has to go over the PCI bus.
Don't be afraid to manipulate the bitmap data directly for
critical path speed-ups. Drawing a pixel or small rectangle
directly into an offscreen bitmap will be literally orders
of magnitude faster than making an app_server call to do the
same thing, and it's pretty simple.
Tip #4: Don't unnecessarily occupy the window thread
The idea behind the window thread is that there will always
be a thread ready to react to a message from another window,
or user input, or an app_server update message. To allow
this, try to keep the window thread free. If you have a lot
of processing or I/O to do, spawn another thread to do it.
If you need to track the mouse cursor over long periods of
time for interaction with a control, use asynchronous
updates (by intercepting MouseMoved messages) or, again, use
another thread, if appropriate.
Keeping a window locked or its thread occupied for long
periods of time (i.e. over half a second or so) is Not Good.
If a window thread becomes unresponsive, and the user
continues to provide input (even if it's just moving the
mouse around) its message queue will fill up. If this
happens, the app_server will start to throw away update
messages and input intended for the window, and this can
cause erratic behavior by the application.
Tip #5: When drawing anti-aliased text, use the proper mode
This is both for performance and attractiveness. If you're
drawing text on a solid color background, use the B_OP_COPY
drawing mode, and SetLowColor to be the background color.
This will look great, and be fast. If you're drawing text on
a non-solid background, to make it look right you should use
B_OP_OVER , although this can be considerably slower.
Tip #6: Use B_TRANSPARENT_32_BIT
If your view isn't very solid (for instance, if its
background is a bitmap) you'll want to SetViewColor to
B_TRANSPARENT_32_BIT . This will prevent the app_server from
wasting time clearing your view to a background color before
calling Draw() , thus making updates a bit snappier.
I hope these tips help you build a killer -- uh, tractor
user interface. Have fun!
News from the Front
By William Adams
So, last week I went to the "Juice Patch," a local smoothie
shop that serves up drinks that don't taste like rotting
fruit. I was looking for money, so I stuck my hand in my
back pocket and pulled out a $20!! "Bonus, Dude!" I thought
to myself. That's the way programming is some times. You're
going along set in your normal ways, and every once in a
while you stick your hand into those old jeans and pull out
a sweet spot.
The wonderful thing about programming on a new OS is that
there is no end to the new things to discover. Just when
you thought you were an expert, you discover something new,
or gain a fresh understanding of a particular technique,
class, or method. I've recently discovered the joys of using
the BMessageFilter class. Peter Potrebic, Be programmer
extraordinaire, has put quite a few nifty features into the
BeOS in very small packages. BLooper , BMessage , BHandler ,
and BMessageFilter just to name a few. Peter has already
written an article on filters and could do a much better job
than I of explaining the subtle nuances of their use, but I
wanted to share a couple of my own personal favorites.
The well-versed BeOS programmer should know that we think
threads are the best thing on Earth. Thread programming can
often times be messy and difficult to keep straight. So the
BeOS provides the BLooper class to make things simple. The
general idea is that a BLooper controls something, and the
only way to affect changes on that something is to send a
message:
MyLooper::PostMessage(BMessage *msg)
This method is typically implemented by anyone who wants to
create a thread of execution and receive messages. During a
normal event cycle, messages are processed by the BLooper 's
DispatchMessage method. It will look for handlers to deal
with the specific messages, or handle them itself if no one
else is interested.
So what about that fragile base class problem? Huh? Well,
here's the neat thing. During normal message processing, the
BLooper 's DispatchMessage is called. The easiest thing to do
if you want to catch some messages and perform actions based
on them is to sub-class the BLooper , or implement a BHandler
and add the handler to the BLooper ? Or is there another way?
Let's say I want to catch all MY_FOO_BAR messages that are
coming into a looper, and depending on other attributes
associated with the message, I want to dispatch it in my own
special way, say across the network, or to multiple handlers
at once, what to do?
class MyFilter : public BMessageFilter
MyFilter::MyFilter()
: BMessageFilter(MY_FOO_BAR)
{
}
filter_result
MyFilter::Filter(BMessage *msg, BHandler **target)
{
int32 value;
msg->FindInt32("value", &value);
switch(value)
{
// If value is net, then distribute the message
// on the net and let the system know that it has
// already been dealt with.
case net:
DistributeOnNet(msg);
return B_SKIP_MESSAGE;
break;
// Distribute message locally without actually doing
// anything else.
case local:
return B_DISPATCH_MESSAGE;
break;
}
}
To Install this filter you would do something like:
myfilter = new MyFilter();
MyLooper.AddCommonFilter(myfilter);
Now, whenever your MY_FOO_BAR messages come in, your filter
will get a first stab at them before the rest of the
framework tries to distribute them. You didn't have to
sub-class the BLooper or BHandler classes. You did have to
sub-class BMessageFilter , but in a growing system,
sub-classing a nice small object that is unlikely to change
is probably easier than sub-classing a highly active object
like BWindow or BApplication .
In addition, you don't even have to sub-class BMessageFilter
to get this behavior. You can add a filter to the system
using nothing more than a function pointer. That way, as
long as the API for the Filter() function remains the same,
your code will never break.
So, yesterday evening, I was digging through my jeans again,
and I found another $20!! With a big smile on my face I
placed it in my wallet, smug in the thought that as long as
I'm programming the BeOS, I'm bound to have these positive
revelations of new found wealth coming from old familiar
places. Can you believe such luck! If you're programming the
BeOS every day, then you too will experience the same
fortunes no doubt, at least that's what I see From The
Front.
Is There An Opportunity for Us In the Confusion?
By Jean-Louis Gassée
I am, of course, referring to the agitation around Apple's
repurchase of Power Computing's Macintosh license and the
Mac cloning scene in general. Is there an opportunity here
for an end-run? In the last couple of weeks, a number of
Be-watchers have suggested the same poetic alternative:
A BeOS/CHRP coalition. As one e-mailer put it:
"The original appeal of BeOS, the thing which made it so
refreshing, was that it was a modern OS on modern
processors; while Apple behaves like idiots, PowerPC
broadens its lead. Want my money? Here's the formula:
Dual G3 CHRP box + BeOS + Mac Emulator = Juggernaut
Take my money, it's sold!"
Poetic, indeed...but is it the most affordably
prosaic way to get Be on your desktop?
Before I elaborate, a bit of context.
Power Computing was the first and most visible Mac cloner
to graciously host the BeOS as an additional OS for the
user to install on their system. We'll miss their
spirit, the way they reinvigorated the Macintosh community
-- and we'll certainly miss the exposure to their customers
that they afforded us.
But Power, for all its well-earned visibility, isn't the
only or even the most important channel of distribution for
the BeOS. We now have bundling agreements with magazines in
the US (MacUser), Europe, and Japan totaling over
700,000 BeOS CDs. We distributed 28,000 copies at
MacWorld, we'll hand out even more at other trade shows,
and when the Preview Release "point release" is available,
we'll offer Web downloads. We'll miss Power, but their
demise doesn't signal ours.
So how will we survive? Another bit of context:
With our next major release, we'll have an Intel version.
This isn't an uncontroversial port, at least not to some.
I found this in my mailbox last week:
"Face it. In the Intel space OS/2 was a June bug on NT's
windshield. You guys are a mosquito."
The prognosis of our undergoing the same fate as
OS/2 is an opportunity to restate our position:
We're not crazy enough to think we can compete with Windows
(95 or NT, pick your death).
They offer a proven, general-purpose OS (or two), while we
have a (nascent) specialized OS, focused on digital media,
as stated in a previous Newsletter:
http://www.be.com/aboutbe/benewsletter/Issue88.html#Gassee.
OS/2 went to battle with Windows on Windows'
turf. We know better by their example, and we know
that to our intended (Intel-based) audience of technically
proficient users, adding another OS to what they already have
on their hard disk is a known exercise. We want
to complement rather than foolishly attempt to replace, and
we do this in a context where users are comfortable with the
modus bootloadi.
So, how does this compare to CHRP space?
In the first place, it sounds like Apple won't support CHRP.
In other words, the CHRP platform won't be able to run the
Mac OS, just the lonely BeOS. Contrast this with the
multi-OS Intel boxes -- Which one will win? The undeniable
charm of the PowerPC just isn't enough.
Others feel the same. One of our readers, and a frequent
commenter, Philippe Dambournet, wrote to Guy Kawasaki, the
distinguished Apple Fellow and Agitpropagandist, suggesting
a CHRP initiative. Guy wrote back: "It will never happen.
Too many people to get together. And it would have an even
smaller installed base/run rate than Macintosh."
Motorola and IBM don't seem to feel very sanguine about the
PowerPC, either. The New York Times, in today's (9/9/97)
business section had this to say: "Motorola and IBM said
Monday that they were refocusing the Power PC microprocessor
family into new markets beyond the computer industry, toward
applications in consumer electronics and industrial markets."
The article quotes Hector Ruiz, the new President of Motorola's
Semiconductor Product Sector, as saying that Motorola is not
planning a direct competitor to Merced, the PC chip family that
Intel is designing in collaboration with Hewlett-Packard Co. The
future of the Moto/IBM/Apple alliance is, in Ruiz'words, "a work
in progress."
For us, the situation is simple. If Motorola or IBM --
preferably both -- build and market a CHRP platform, with or
without a Mac OS license to boot, we'll be happy to make
sure the BeOS shines their hardware. In any case, we'll
continue with our processor-agnostic strategy, providing
developers and customers cross-platform compatibility on
Intel and PowerPC machines.
I also received many invitations to "comment" on Apple's
recent moves, meaning, given the tone of those messages,
joining in the criticism. I don't see how this would be
helpful, or justified. I, for one, do not believe we have
the full picture of Apple's real strategy.
Regarding the cloners, Apple was faced with a tough choice,
the same one as they faced in '86 when the licensing idea
was first agitated. Damned if you lose the hardware
business, damned if you don't license.
My sense is that Apple has concluded they couldn't face Wall
Street while losing the hardware business and focusing on
software only. They reasoned the cloning situation would
become more and more competitive and they would lose more
and more of the juicier high-end margins because the
cloners, understandably, didn't want to focus on the low
end. And CHRP would have made the competitive situation worse.
I guess Apple is trying to survive while designing a new
product strategy. Seems logical. Of course, we all realize that
this is, perhaps, not a viable long term solution. But that's
not our concern today. Perhaps Apple is just buying a little
time and some room to maneuver.
BeDevTalk Summary
BeDevTalk is an unmoderated discussion group that's a forum
for the exchange of technical information, suggestions,
questions, mythology, and suspicions. In this column, we
summarize some of the active threads, listed by their
subject lines as they appear, verbatim, in the group.
To subscribe to BeDevTalk, visit the mailing list page on
our Web site: http://www.be.com/aboutbe/mailinglists.html.
- ----NEW--------------------------
Subject: interface considerations
UI Guideline fodder. Ed Musgrove solicited opinions on some
interface matters:
- Screen Resolution. Should an app accept the resolution of
the workspace in which it's launched, or should it try to
find (or create) a workspace that fits the app's
expectations? For example, should a window remember the
resolution of the workspace it was last running in and try
to relive the experience?
Almost everyone agreed that switching workspaces is bad --
you should almost always accept the resolution of the
launched-in workspace. Eric Berdahl fine-tuned this concept
by suggesting that games or other "immersive" user
experiences should be allowed to switch workspaces.
- Package Data. Should collateral data (images, sounds, etc)
be stored as attributes, resources, or in completely
separate files?
Resources by a landslide. By using resources, the app
executable file itself becomes a stand-alone package
- User Data Byte Sex. If an app reads and writes its own
data format, should the app stick to a single endian-ness,
or should it use the native byte sex and tag the data?
Use network byte order, says Chris Herborth:
"htons(), htonl(), ntohs() and ntohl() are your friends. On
architectures that happen to correspond to network byte
order, they're empty macros; they won't add anything at all
to your code. On other machines, they usually transform into
fast inline assembly."
The sentiment was echoed by many of our favorite listeners.
Some other UI tidbits were thrown in, such as this anomaly
from Timothy Wayper:
"Launch NetPositive...Start downloading an ftp file...Close
the main browser window...Bang! You're hosed...Why? Because
there's no menu on the little FTP window to allow you to
open another browser, and NetPositive is 'single-launch', so
you can't reopen the app."
Which led Osma Ahvenlampi to suggest that DeskBar should let
you force kill applications.
- ----NEW--------------------------
Subject: Media kit, promotion, and media OS
Yann Kieffer wrote in to challenge Be to support its claim
of being a "Media OS." Specifically, Mr. Kieffer pointed out
a number of weaknesses in the audio portions of the Media
Kit, most of which centered around the lack of "higher
level" (module layer) support. The current stream layer
interface is, in Mr. Kieffer's opinion, too close to the
bone to be used reliably.
Jon Watte responded to some of Mr. Keiffer's complaints by
pointing out that what some folks see as too low level can
also be characterized as efficient. This didn't comfort
everyone -- the ability to trash other subscriber's sound
output was lamented by more than one correspondent
- ----NEW--------------------------
Subject: Macintouch opinion on BeOS
A discussion of the quasi-serious suggestion (voiced in
Macintouch and elsewhere) that a combination of BeOS/CHRP
could become the box for the next generation. This led,
predictably, to a discussion of PEF and XCOFF, and questions
about loading the BeOS on a Linux-bootstrapped system.
- ----NEW--------------------------
Subject: BHRP
Bernd Koester dreams of a "Be Hardware Reference Platform"
that would be eminently laptoppable. Kyle Hearn (who wants
to know how you would pronounce "BHRP"), pointed out that Be
on a laptop is already available:
"You can have a BeOS laptop now, but it'll cost you about
$7000 US. A company in Canada builds laptops made with a
Motorola motherboard that they claim is BeOS compatible. The
model I looked at was made from titanium, aluminum, had a
removable keyboard and an internal Jaz drive (I'm not
kidding)."
Back to the original dream, Chris Herborth scratches his
head:
"This [BHRP] would be a little... strange. BeOS is processor
agnostic, and already runs on PowerPC and Intel. At the
DevCon, Erich [Ringewald, Be's C02] mentioned they were
looking into other processors, too (mostly strange embedded
stuff)."
|