Issue 2-36, September 10, 1997

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:

  1. a set of time zone "rules" files, in ASCII text

  2. a "rules" compiler that translates these rules files to binary format ("zic", or the zone information compiler)

  3. 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:

  1. Dynamically, from a shell

    1. type "export TZ=<timezone_setting>"

    2. launch the application from the shell

  2. 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:

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 unmonitored discussion group in which technical information is shared by Be developers and interested parties. In this column, we summarize some of the active threads, listed by their subject lines as they appear, verbatim, in the mail.

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 re-live 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.

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

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.

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).

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.