Issue 3-2, January 14, 1998

Be Engineering Insights: Workspace Workout

By Robert Polic

Workspaces are an illusion that allow users to organize their applications and documents into different virtual screens. By default, application windows open in the current workspace and live solely in a single workspace (though the user, through the Workspaces preference panel, can later position the window in any of the available workspaces).

If the default behavior satisfies the needs of your application, you don't need to add any workspace- specific code. However, if your application supports multiple windows, gratuitous animation, or other processing tasks that can stop when its workspace is deactivated and continue when it is reactivated, there area handful of BWindow and global functions to assist you.

In most cases the user should determine which workspace a given window exists in (use B_CURRENT_WORKSPACE in the BWindow constructor), though there may be cases were it makes sense for the application to set the workspace for a window. To create a window in a specific workspace (or workspaces) the BWindow constructor takes an unsigned int32 bit field. A "1" in any bit position indicates the window should exist in that workspace (with bit 0 corresponding to the first workspace). Using B_ALL_WORKSPACES forces the window to exist in all workspaces. Most applications should not use B_ALL_WORKSPACES since this would rapidly defeat the purpose of workspaces.

To change the current workspace (or workspaces) for a window after construction, there is a BWindow method, SetWorkspaces(), that takes the same bit field parameter. An example of an application that may need to do this is a paint program with an editing window and separate tool palette windows. When the user moves the editing window to another workspace, the palette windows should follow. To do this, override the edit window's WorkspaceChanged() method and call the palette window's SetWorkspaces() function:

void TEditWindow::WorkspacesChanges(uint32 old_ws,
                                    uint32 new_ws)
{
  fPaletteWindow->SetWorkspaces(new_ws);
}
Note
Note

Before creating or moving a window to a given workspace you should first determine that the workspace is valid (the user can set between 1 and 32 workspaces). There is a global function, count_workspaces(), that can be used to determine the number of workspaces.

Some applications may wish to create follow-on windows in the same workspace that the main window resides in. To determine which workspace (or workspaces) the main window is in, use the window's Workspaces() method. An application example might be a compiler that creates a status window:

void TCompilerWindow::ShowStatus(...)
{
  BWindow* status = new BWindow(rect, title, type, flags,
                                Workspaces());
}

An application that can curtail processing when its window's workspace becomes inactive should do so. Obviously, a web browser should not stop a download, but it could stop all animated GIFs. To do this, override the BWindow's WorkspaceActivated() method and stop or start processing tasks according to the state variable:

EyeCandy::WorkspaceActivated(int32 ws, bool active)
{
  (active) ?
    release_sem(process_sem) :
    acquire_sem(process_sem);
}

Applications with windows that live in more than one workspace can do this a bit more efficiently by ignoring the ws variable and instead using the global function current_workspace() to determine if the window lives in the current workspace:

EyeCandy::WorkspaceActivated(int32 ws, bool active)
{
  (Workspaces() & (1 << current_workspace())) ?
    release_sem(process_sem) :
    acquire_sem(process_sem);
}

Developers' Workshop: Proper Attribution

By Stephen Beaulieu

As you know, the Developer's Workshop is all about answering questions and providing sample code to developers. We get a list of topics and questions developers send in. We look them over and provide the sample code that we think will solve the problem. Well, being the new kid on the block, I brought a question with me. "When is it appropriate to use attributes to store information for your program?"

Making the most of my direct access to the Be engineers, I wandered around asking my question until I got some answers.

The quick and dirty answer is that attributes are appropriate for storing extra information that is inappropriate or impossible to store in the file itself. This information may change frequently, or can be done without if it doesn't exist.

As always, the quick and dirty answer isn't the full story. But the full story needs a little background information.

Attributes are arbitrary name/value pairs associated with a file, which can be indexed by the file system for querying. Attribute names can be up to 255 characters in length, and their values can be of any size. Attribute names and values are stored together, and a file can only have a single value for a given attribute name. Attributes are stored in the order they are added to the file. Queries are limited to indexed attributes whose values are strings, floats, or one of the various integer types.

Some implementation details suggest ways to optimize attribute storage. These details are subject to change, but the suggestions I'll make shouldn't negatively affect future performance. In any case, these details will be completely invisible to users.

While attributes can store any amount of information, each file has a small "fast attribute" area that is accessible at little cost whenever the file is referenced. This "fast attribute" area is about 700 bytes in size, and is the first place attribute information is stored. As soon as it fills up, the file system creates additional areas to store attributes. The additional areas impose some file system access costs, making them considerably slower than the "fast" area.

So, what can a developer do to maximize the performance of retrieving attribute information? Try and get as much information into the "fast" attribute area as possible. Although there are no specific methods to guarantee that an attribute exists inside of the fast attribute area, we can suggest guidelines that should increase the likelihood of having your attributes accessed in the fastest method possible:

  1. Keep attribute names short, but descriptive. Remember to avoid namespace conflicts.

  2. Create all of your smallest attributes first, leaving larger ones for the end.

With these optimization guidelines in mind, let's look to the BeOS for some appropriate uses for attributes. Perhaps the best overall example is using attributes to store information about e-mail files. The mail_daemon saves each message to a file and adds the following attribute information:

Information      Attribute Name
-----------      --------------
Name             MAIL:name
Subject          MAIL:subject
To               MAIL:to
From             MAIL:from
ReplyTo          MAIL:reply
Status           MAIL:status
Priority         MAIL:priority
When             MAIL:when
Header Length    MAIL:header_length
Content Length   MAIL:content_length

Most of these are self explanatory, and are simply the record of information contained in the file in the attribute system for querying purposes.

The header_length attribute, however, is worthy of special note, as it shows an efficient use of attributes. Header information is not normally needed when displaying a message to the user, but since it's at the beginning of the file it's important to know its length to make it easier to jump to the real contents of the file. Similarly, when the headers are specifically requested, having the length available makes it easy to read that amount of data from the file. This makes parsing the file to get to the end of the headers every time the file is accessed unnecessary.

Other BeOS uses of attributes can be found in StyledEdit and the Tracker. StyledEdit stores all of its style information in attributes. This allows the actual data of the file to be stored as plain text, making it easy to read the file in applications that don't care about the styled information. The Tracker uses attributes to store icon and position information for files and folders.

The People app also uses attributes for information storage. Person files contain only attributes; there is no other data in the file itself. This raises the question of whether it is appropriate to use attributes as a kind of built-in database. The answer is that it really depends on the type of information you want to store. The drawback of such a system is that it is inefficient in terms of storage. Every record would exist as an individual file, taking up a minimum of a block on the drive. Each file must live in some directory somewhere.

While in theory this method should work for nearly any amount of information, as a practical matter a "database" with hundreds of thousands of records would be inefficient both in terms of storage space and speed of accessing information. On the other hand, it can be useful for storing contact information in a place where it's easily accessed by any application.

There are many uses for attributes in addition to those we've touched on in this article. Attributes could be used to store the URL of a downloaded document; the last backup date for a file; the keywords of a document; or gamma correction, color depth and dimensions of an image. We'd like to hear about the uses that developers are putting attributes to. If you have an interesting use, drop us a line at the online support form in the Registered Developers Area. We're curious.

Finally, there are a few things to be aware of when dealing with attributes. Currently, only direct Tracker copying to another Be File System disk or archiving your files with zip preserves attribute information. Most of the other common storage and transfer formats do not save that information (including tar, cp, ftp and mail attachments.) This means that you need to take extra care when transferring files that contain attributes. Also, applications should be aware that a file might not contain the attributes they expect, and be prepared to rebuild them, if possible.

Well, I've gone over many things to consider when using attributes, but I've not touched on how to actually use them. I'll cover that in four weeks, along with some sample code that shows how to read and write files in the BeOS.


CES Snapshot

By Jean-Louis Gassée

Last week, Steve Sakoman and I took time off MacWorld to go to Las Vegas. This wasn't a desperation financing trip, but an attempt to survey the scene at the Consumer Electronics Show. It didn't start well. Steve tried to register electronically and the server promptly got stuck leaving him in registration limbo for a while.

We went in looking for signs of the convergence between consumer electronics and computing. We saw black boxes and silver boxes and titanium colored boxes, but not much convergence. Still, WebTV Plus, the latest version, is really good, TV and the Web coexist nicely, even if they don't merge yet and this product will make Microsoft look very smart.

Smart is an adjective that becomes harder to place when contemplating the latest TCI machinations. John Malone gives a layer of the mille-feuilles to Scott McNealy's Personal Java and another one to Windows CE. But the hardware layer is still to be defined, the microprocessor is to be anointed in the Spring, all the while NextLevel nee and born again General Instruments (the NextLevel name didn't work) claims billions in orders for their next generation set-top boxes.

I guess John Malone is the smartest right now for wringing money out of Sun and Microsoft. Sun is rumored to provide about $10 per box to "compensate" for the hardware penalty required to run Personal Java and Microsoft is rumored to have made similar concessions in excess of the WinCE royalty, while pointing out Java, unlike Win CE, won't run on all boxes, only higher-end ones. The "happy" processor manufacturer knows what to expect.

Will this yield a nice product? The developments will be interesting to watch with each player bringing up hardware or debugging code while watching the other guys trying to create some kind of toll gate in order to recoup their "advance payment" and, at the same time, rereading the legal paperwork looking for loopholes in the contracts.

With this in mind, the un-second-guessed approach pursued by WebTV looks even more attractive, in a different domain for the time being.

We watched with great interest the PalmPC demos (I'm a Palm Pilot user and a director of 3Com, the owner of Palm Computing—so you know the possible biases in my opinions). Same physical size as the Pilot, and running a version of Windows CE, with Microsoft's might, it could become tough competition.

The PalmPC demo involved a man and a woman. The woman played the role of the "conventional" user of paper organizers, big purse, you see the picture, and the man inside the little demo booth was the more enlightened user of a PDA.

On top of the booth, a big screen reproduced the small PalmPC screen for a large audience—I thought, until Steve Sakoman's sharper eyes and elbow brought me back to reality. "They're running a tape," said Steve. Indeed, this was a karaoke demo, the two actors were saying their lines as the video tape of the demo ran. I went around the booth, the male "demonstrator" followed the tape on a small screen and kept flipping his neat little cue cards with yellow highlights as he went along.

Just to acquire more data about WinCE, I bought a Sharp 2.0 device, grayscale display, 12MB of RAM, integrated (soft) modem, and took it on an overseas trip along with my Hitachi portable, my Pilot, my Swiss-Army knife, my screw-pull (don't want to face a bottle of fine Bordeaux without it) and other unmentionables. Without getting into a detailed review of the Sharp hardware nor of Windows CE 2.0, the whole system looks to me as too big, too slow, too unreliable and too battery-hungry (one set of two AAs per day) to compete in the smaller, lighter, more nimble Pilot category.

HDTV was everywhere and looked pretty real. Yes, the sets are horribly expensive. But imagine the following situation: two sports bars in town, one shows football and basketball on HDTV, one on a current set. The beer costs 50 cents more, which bar will get more volume and better margins? Joe, where did you get that set? Sports sells ads, somehow a way will be found to prime this better pump for commercials.

For the rest, wireless devices continue to proliferate happily, GPS devices can now be had for less than $100 and slightly more expensive ones acquire better displays, store maps—they'll be and take us everywhere.

The rest was either unmentionable or too hilarious for this family newsletter, or too sad. In this latter case, I'm referring to the "very high-end" audio with Russian mil-spec 12AX7 triodes, gold-plated everything and oxygen-free copper crystal cables.

We grabbed a cab, ran through the airport, caught a just-boarding Southwest flight to San Jose. The peanuts and the diet coke never tasted better. Comdex looks even better now.


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: B_PAGE_SIZE

If you malloc() a single page (4096 bytes), does the system actually reserve another page for bookkeeping overhead? Should you be wary as your malloc() request approaches a page size boundary? Should you use areas instead?

THE BE LINE: A malloc() smaller than 2048 bytes rounds up to the next power of two. Greater than 2048 rounds up to the next multiple of 4096. The bookkeeping overhead needn't be a concern; for example, the overhead won't push a malloc(4096) call into an 8k allocation. Note that the 2048/4096 numbers are NOT related to the page size (which, coincidentally, is also 4096 bytes); these numbers could change in a later release.

As many readers pointed out, you shouldn't blithely swap create_area() for malloc(). Areas are useful when you need to lock or share a chunk of data; they aren't meant to be fast.

Subject: Mouse Position

AKA: Mouse Position Again
AKA: Pointers.

Should Be allow programmatic mouse positioning? The "all things are possible" camp thinks a modern OS should allow this sort of manipulation.

From Marco Nelissen:

SetMousePosition is *good*. For one thing, it allows you to plug in all kinds of funky hardware to control the mouse cursor with, and you don't even have to write a fake mouse driver for it. It also allows for stand-alone recorded demo's to play...

UI wonks fear that this ability will corrupt interface consistency and confuse users. Many of our listeners adopted a laissez-faire, natural selection position:

Is SetMousePosition() so bad an idea that we can't let programmers do what they will and let Darwin sort them out?” (Trey Boudreau)

Sure, it could be abused, but software that abuses this feature will annoy users, and thereby die a quiet death.” (Marco Nelissen)

Wholesale mouse repositioning was a pretty hot issue, with little room for compromise. But what about mouse constraint? Should a developer be allowed to declare a rectangle that restricts the mouse's movement? Ross A. Knepper thinks this is antisocial...

...remember that the user might have other things going on at the same time. While you're constraining the mouse, the user might be desperately trying to click cancel on the requester that says "melt down the processor in 3 seconds"

And Trey Boudreau responded...

Constraining the mouse motion is likely to be done ... only while a specific set of modifiers are being held down. Programs that do otherwise will likely be un-installed pretty quickly.

Subject: Shutdown message?

When the computer is shutdown, the BeOS doesn't send a "shutdown pending" message, it simply sends a "quit requested" to all applications. Would it be worthwhile to send a special "shutdown" message? Most folks can't see the harm in it; the idea is fine-tuned thus:

  • Broadcast a B_SYSTEM_SHUTDOWN to all applications and then launch all apps [that are] in a special shutdown folder. (Dirk Steins)

  • Let individual apps (or the entire system) set an "interaction at shutdown" level. An unattended server machine (for example) wouldn't want any interaction.

Scott Lindsey took this last point further, applying it interaction at any time:

...there's nothing special about user interaction on quit. It's not reasonable to define an alternative message for every message type that might interact with the user. Seems to me there's two ways of handling it. Either build an interaction level into the messaging system, or attach an optional parameter to the message (where the absence of the parameter is the same as full interaction).

Jon Watte modified the idea to fit the "save changes" situation:

I would prefer a boolean [message field] named 'saving' that tells you whether to save changes or not... The cool thing is that a scripting language might use the same [field] to quit just one application: tell SomeApp quit saving:true

And Osma Ahvenlampi suggested a couple more fields:

Okay, I suggest two modifiers for all standard system messages:

'unattended':bool—don't pop up any dialogs,
                     take the default action.
'force':bool     —message demands immediate action

Subject: Using RefsReceived

Some BMessage thoughts, concentrating on what types. Is 32 bits enough to encode a message's what constant? Marco Nelissen wishes that the what field were 64 bits, thus making vendor/what encoding simple. Should developers adopt a separate 'owner' field convention? As suggested by Wendell Beckwith:

We simply need to define a convention that all foreign messages (i.e. messages traveling outside of your app's address space) contain a int32 named 'owner' and then add your own data to it. Be could fix the BMessage constructor to automatically do this and define a default 'owner' field to be 0/-1 or whatever. This would indicate that the message was generated by an unknown entity.

But what about forwarded messages? Is the original sender always the owner, or should it be the app that last touched the message?

Subject: Control locking

This discussion about provable programs concentrated on what happens when a (non-virtual) function is invoked on an invalid object, where "invalid" can mean deleted or reused.

The practical answer: BLooper's Lock() function doesn't throw an exception when invoked on a NULL pointer. The theoretical question (does it make sense to strive for provability?) faded away without resolution.

Subject: dynamic_cast

Is it possible to "cross cast" an object, changing its flavor from one branch of a multiple inheritance to another branch? For example, should this excerpt, submitted by Marco Nelissen, do anything other than what it does do, which is crash:

class MyButton: public BButton, public MyBase;

MyBase *thing=new MyButton();

... dynamic_cast<MyBase*>((BButton*)thing);

According to Jon Watte, it should, indeed, crash. This foils Mr. Nelissen's attempt to create a varargs function that does its own casting of its semi-anonymous arguments. The desired casting can't be done because the function doesn't know the exact class of the argument.

Subject: text/weird, and other strange mime types

Olaf Seibert was seeing odd file types when he noticed that the problem stemmed from a confusion between app signatures and an app's file type/supported types. Mr. Seibert's testimony was seconded by other listeners; it seems that many folks regularly (or, worse, irregularly) experience messed-up file types. The answer? Check with us next week.

Subject: BeOS "face"

Customizable user interface talk. Nothing much new, here, except for this observation by Jeff A. Campbell:

As a support tech, I KNOW how hard it can be to talk a user over the phone through a process and not know where everything is. I personally want as customizable an interface as humanly possible, but I also want this: The ability to switch back to a non-editable 'interface' with VERY little effort, and no restart.

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