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
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:
Keep attribute names short, but descriptive. Remember to avoid
namespace conflicts.
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.
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 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.
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.
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?
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.
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.
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.”