Be Newsletter
Issue 96, October 22, 1997
Table of Contents
Be Demo Tour in Europe
- Geeks Tea
Be Office, Paris, France
October 31, 1997 from 4:30 to 8:30PM
All developers are welcome
For directions to the Be Europe office:
http://www.beeurope.com/English/Contacts/bureau.html
- Apple Expo
Olympia Hall, London, UK
November 5 to 8, 1997 from 10:00AM to 6:00PM
More info can be found at:
http://www.apple-expo.com/
- MacWorld and Publishing Expo
Messegelande, Dusseldorf, Germany
November 13 to 15, 1997 from 10:00AM to 6:00PM
More info can be found at:
http://www2.mac-world.de/mac-world/index_web.html
- IPISA
Centro Universitario ISU, Milano, Italy
November 29 to 30, 1997 from 8:30AM to 7:30PM
More info can be found at:
http://bilbo.di.unipi.it/ipisa/index_eng.html
BE ENGINEERING INSIGHTS: Scripting Anyone?
By Peter Potrebic
Anyone interested in a brief example using the new scripting
architecture in PR1 and PR2? I heard a few "yes's" so here
it is.
For a long time developers have been asking for scripting
support in the BeOS. We listened and we added such support
in the PR1 release. In keeping with BeOS philosophy the goal
was to add scriptability with minimal complexity. Hopefully
we've achieved that goal. Let us know what you think.
For a full technical description see:
http://www.be.com/developers/may_dev_conf/transcripts/messagingandscripting.html
for a transcript of the messaging/scripting session at BeDC,
or read about scripting in the Be Book:
http://www.be.com/documentation/be_book/app/scripting.html
Now I'll give a little example of how to create a class that
is scriptable. Let's suppose you have a view class that can
have one of three states. It also contains a rectangle that
defines a hot spot inside the view. Let's suppose that you
want to script those 2 properties. Here's what the class
declaration might look like:
class TTriView : public BView {
TTriView(BRect frame, int32 state, BPicture *pict)
...
void MessageReceived(BMessage *msg);
BHandler *ResolveSpecifier(BMessage *msg,
int32 index,
BMessage *specifier,
int32 form,
const char *property);
...
void SetState(int32 state);
int32 State() const;
void SetHotSpot(BRect rect);
BRect HotSpot() const;
};
In the TTriView constructor you can see the 2 features of
the class that we will make scriptable, its state and hot
spot. The class also overrides the MessageReceived hook
function. It is in that function where the script messages
will be handled.
The last overridden function is ResolveSpecifier . This is
the key function in terms of making a class scriptable. This
is how a class advertises the "specifiers" that it supports.
Let's start with this function:
BHandler *TTriView::ResolveSpecifier(
BMessage *msg,
int32 index,
BMessage *specifier,
int32 form,
const char *property)
{
BHandler *target = NULL;
if ((strcmp(property, "State") == 0) {
if (form == B_DIRECT_SPECIFIER)
target = this;
} else if ((strcmp(property, "HotSpot") == 0) {
if (form == B_DIRECT_SPECIFIER)
target = this;
}
if (!target) {
target = BView::ResolveSpecifier(msg, index, spec,
form, prop);
}
return target;
}
Let me explain a little of what's going on in the
ResolveSpecifier function. The class is declaring that it
understands 2 properties, "State " and "HotSpot ". For each of
these properties it can handle DIRECT specifiers only. There
are more kinds of specifiers, see the documentation in the
Be Book for more details.
If the object can handle the message it should return itself
(the 'this ' pointer). This tells the system that the
scripting message should be handled by this object.
Otherwise it is very important to call the inherited version
of ResolveSpecifier because some inherited class might
actually understand the message, even if the derived class
doesn't.
After getting directed to the correct object, a scripting
message will be passed to the MessageReceived function. It
is in this function that the message is actually processed:
void TTriView::MessageReceived(BMessage *msg)
{
bool handled = false;
BMessage reply(B_REPLY);
status_t err;
switch (msg->what) {
case B_GET_PROPERTY:
case B_SET_PROPERTY:
{
BMessage specifier;
int32 form;
const char *prop;
int32 cur;
err = msg->GetCurrentSpecifier(&cur, &specifier, &form,
&prop);
if (!err && (strcmp(prop, "State") == 0)) {
if (msg->what == B_GET_PROPERTY) {
reply.AddInt32("result", State());
handled = true;
} else {
int32 new_state;
err = msg->FindInt32("data", &new_state);
if (!err) {
SetState(new_state);
reply.AddInt32("error", B_OK);
handled = true;
}
}
} else if (!err && (strcmp(prop, "HotSpot") == 0)) {
if (msg->what == B_GET_PROPERTY) {
reply.AddRect("result", HotSpot());
handled = true;
} else {
BRect new_spot;
err = msg->FindRect("data", &new_spot);
if (!err) {
SetHotSpot(new_spot);
reply.AddInt32("error", B_OK);
handled = true;
}
}
}
break;
}
... // other 'cases' go here
}
if (handled)
msg->SendReply(&reply);
else
BView::MessageReceived(msg);
}}
In the above code you can see that the class supports either
GET ing or SET ing the 2 scriptable properties of TTriView .
These properties can't be created or deleted (i.e.
B_CREATE_PROPERTY or B_DELETE_PROPERTY ). More complex
classes will support these and other options.
When getting or setting the State of the view, the data
being dealt with is an integer. For this reason the code
uses FindInt32 and AddInt32 in the SET and GET cases
respectively. In the HotSpot case the corresponding FindRect
and AddRect functions are used.
Note that when setting an attribute, the data should always
be found in an entry named "data":
err = msg->FindRect("data", &new_spot);
-- or --
err = msg->FindInt32("data", &new_state);
When handling a GET message, the data being returned should
always be placed in a entry called "result ":
reply.AddInt32("result", State());
-- or --
reply.AddRect("result", HotSpot());
Placing an error code in the entry "error " is another
scripting convention worth noting.
The final step is to send a reply to the scripting message.
Notice that a reply is sent whether the message is setting
or getting data. In the GET case it is obvious that a
reply is needed, because the requested data must be
returned. In the SET case the class still returns a message
to indicate the success or failure of the SET .
That's all there is to this simple scripting example. As
always, if there are any questions just post them to
BeDevTalk or send me a message.Content-Type:
application/x-be_attribute; name="scripting.article"
News from the Front
By William Adams
Did you get the message? I'm sure I sent it, why haven't you
responded? If you've been using BMessageFilter s, then your
brain cells can probably recognize this pattern of thought.
As written previously, BMessageFilter s are wonderful things.
They make dynamic extension using BLooper s and BHandler s
relatively easy in the BeOS. I've gone over how you can use
a filter to capture things like keydown s, mouse movements,
and any other messages that are normally headed for your
classes. Now I want to talk about some of the mechanics for
using them in particular situations.
Did you know that after you use BLooper::AddCommonFilter ,
your message filter is added to the bottom of a list. When a
new message comes in to be processed, this list of filters
is traversed from the top to the bottom. That is First in
First Processed. This is an implementation detail, but it's
necessary to know in order to achieve certain behaviors in
message processing.
Here's a task. You want to create a user interface test
harness. You need to capture and playback all keystrokes and
mouse movement. You want to be able to create a script that
will saves these events in some way for later playback. How
can you do such a thing? With BMessageFilter s, of course.
First of all, write a capture filter. All it has to do is
get every message, examine it to see if it's a mouse or key
message, and capture it if it is. It will not actually
interfere with the processing of messages, it will just
examine and capture the appropriate ones.
CaptureFilter::CaptureFilter()
: BMessageFilter()
{}
filter_result
CaptureFilter::Filter(BMessage *msg, BHandler **target)
{
switch (value)
{
case B_KEY_DOWN:
// capture keydown
break;
case B_MOUSE_DOWN:
// capture mouse down
break;
}
return B_DISPATCH_MESSAGE;
}
Simply put, this filter will examine all messages, and
capture for later playback all the ones that are either
keydown, or mousedown. Simple enough. You can embellish it
with the actual capture code. To use this filter, you can
simply call AddCommonFilter on whatever window object whose
events you want to capture. Easy enough.
Now let's say you want to do something a little bit more
fancy, before messages are actually captured, you want to
filter them out to eliminate excessive mouse movement, or
you want to modify the events in some way before they are
captured.
Well, you can simply call AddCommonFilter again and your
filter is in the chain...but not in the right order.
Remember, they are processed in first-in, first-processed
order. We need to be able to change this processing order.
The only really good way to do this is to sub-class the
BLooper , or BHandler , and add the appropriate message filter
list processing code yourself.
What we want to achieve is the reversal of ordering in the
message list. There is no real easy way for you to change
the processing order, so you need to change the order in
which the items are added to the list.
virtual void AddCommonFilter(BMessageFilter *filter);
We can achieve what we want by implementing this single
method. Reading the comments of this code will give you the
flavor of what is going on.
void
MessageLooper::AddCommonFilter(BMessageFilter *filter)
{
// Check for locking. We have to be locked before we
// can proceed or multiple threads could be trying to
// create the filter list at the same time.
BLooper *loop = Looper();
if (loop && !loop->IsLocked())
{
debugger("MessageLooper::AddCommonFilter - Owning Looper
must be locked before calling
AddCommonFilter");
return;
}
// BLooper::RemoveCommonFilter doesn't reset the looper to
// NULL when the filter is removed, so this check is not
// valid after the filter is removed. This is a bug and
// will be fixed in future releases. Until then, you might
// want to disable the check.
//if (filter->Looper() != NULL)
//{
// debugger("A MessageFilter can only be used once.");
// return;
//}
BList *newList = CommonFilterList();
if (0 == newList)
{
newList = new BList();
SetCommonFilterList(newList);
}
// Most important line, this makes sure the items
// are added to the top of the list instead of the
// bottom.
newList->AddItem(filter, 0);
}
Once you use this new AddCommonFilter , your filter list will
act as a stack, with last-in, first-out processing. So you
can add a filter that captures all messages, and doesn't
send them on to the Looper until it has sent them out to the
network and back, or whatever else you want to have happen.
The main benefit of having your filters processed in this
order is that you can layer on and supersede functionality,
which you can't do with the default processing order.
It's only one method to implement, and you benefit from it
tremendously in certain situations. I have found it to be
quite useful, and my code is more flexible and dynamic for
it.
As this newsletter approaches its 100th issue, there will be
more authors participating than ever, bringing you more
details of the BeOS to help you write that tractor driving
killer app. I'll see you at the front.
One Step Behind...
By Jean-Louis Gassée
That's how I felt the morning after writing last week's
column discussing the little contractual misunderstanding
between Sun and Microsoft. A few hours after our newsletter
hit the Net, Sun and Microsoft posted the text of their Java
licensing agreement on the Web.
As my e-mail correspondents vigorously suggested, I read the
contract, and I read it again, and I read it again. And I
decided to wait for the Agenda conference held in Phoenix,
Arizona, on October 20-21. Scott McNealy, Bill Gates,
Stewart Alsop, Bob Metcalfe, John Doerr, Andy Grove and
other luminaries were bound to help me understand the finer
points of this dispute.
Right at the end of the Monday morning coffee break, I went
back to the big ballroom, looking forward to Eric Schmidt's
"Java as a soap opera" presentation for enlightenment -- and
entertainment. As I settled down, the conference organizers
were playing one of the satirical videos customarily used to
fill idle times.
On the big screen, Janet Reno was being ribbed for her
ineffectual DOJ anti-trust pursuits and her political tin
ear -- I thought, until I realized it was a genuine
real-time CNN feed and our Attorney General was taking on
Microsoft again.
This time, Janet alleges, Microsoft is in breach of the 1995
consent decree overseeing the practice of bundling software
products, forcing, I'm sorry, "incentivizing" customers to
buy more Microsoft products together and crowding the
competition out. Specifically, the DOJ alleges Microsoft
uses its dominant market position to "force" manufacturers
and consumers to buy the Explorer browser together with the
Windows operating system in violation of the 1995 consent
decree. Further, the DOJ complains, Microsoft uses
non-disclosure agreements to compel their business partners
to give them advance warning of government inquiries and to
prevent them from volunteering information to the DOJ.
Burned by my poor timing last week, I'll wait. So far, Wall
Street seems to be shrugging the complaint off: This morning
Microsoft's stock is up and Netscape's is down -- whatever
that means.
Returning to Java, Eric Schmidt gave a very clever
presentation, taking turns at all sides in the issue, Sun's,
Microsoft's, and the rest of us. Later, we heard directly,
very, from Scott McNealy as well as his mother, a rather
assertive and golden-hearted individual who makes her son
blush. And we were treated to a fireside chat with the
Chairman. And the result is...more confusion.
Regarding the contract, listening to one side or the other
is more instructive on issues of motive than on fact. Legal
eagles are more helpful: Most of the ones not affiliated
with either side seem to agree Microsoft is, or can easily
put itself in at least narrow compliance with the agreement
by posting missing bits, classes somewhere on their site
without necessarily bundling them with their product. An
irony at a time when Microsoft is sued for bundling other
bits. But let's move from contractual Talmud to marketplace
Kriegspiel. What are the possible moves?
Microsoft could decide to pick up its marbles and not offer
a Java implementation at all. With 95% of desktop PCs, would
this kill Java? Not necessarily, Sun or others can offer a
Java VM for Windows and, voilà, Pure Java programs run on
Windows, Java-powered Web pages work. This would allow Sun
and its ally to proceed with their game plan: Expand the
Java definition, and the underlying Java VM to the point of
making Windows unnecessary and irrelevant. Java would morph
into Java OS, sporting a myriad of applications, an
alternative to Windows or, even better, its successor, more
in tune with the new world of hardware-blind distributed
computing applications.
This assumes much passivity from Microsoft. Developing a
Microsoft-unfriendly platform by starting on top of Windows
isn't an easy proposition. All sorts of technical glitches
could "develop" along the way and lawyers salivate today
when they fantasize about this direction. As in the old IBM
days, the Java camp would need to force full disclosure of
all internal Windows APIs. This could take a long time.
Another direction for Microsoft would be to abandon the Java
label, call their "dialect" J++ and happily continue on
their merry way having "embraced and extended" the language
as advertised all along. Once the outcry dies, the Java
consortium can offer a competing "Pure Java" VM and pursue
the course outlined above. This is probably an even more
difficult challenge: Imagine a J++ engine and a Java VM
sitting on the same system. How would most customers feel
about this? Which engine would the developers pick if they
want to make money -- as opposed to making a statement?
There are a few more options left. Microsoft could
capitulate and tell Sun "I've sinned against you," Pure Java
would run everywhere and progressively make Windows passé
and hardware irrelevant. Possible in another universe, but
not likely in this one.
What is perhaps a little more likely is some kind of muddy
compromise. Sun is hinting darkly they don't want to settle
for Microsoft to just pay the contractual $35 million in
liquidated damages and go home. Suns wants Microsoft to be
compelled by the courts to fully honor the licensing
agreement, or, if I read correctly, pay much bigger damages
for the harm allegedly done to the industry. Or settle for
less in a mealy-mouthed compromise.
In the mean time, Sun is already reminding us that Java is
much more important to new, emerging genres of computing
devices such as appliances, network computers, smart cards,
mobile communicators and rings opening doors, ski lifts and
ATMs. This is, after all, a return to the original premise
of First Person and Oak, respectively the originator and
original name of Java. Please remember, I promised
confusion.
One thread says Java is a way to re-open the market by
creating a new computing platform capable of making Windows
less of a toll gate. Another line of discourse states
desktop computers matter a lot less than the many new genres
of computing devices enabled by semiconductor technology and
the communications infrastructure -- and, above all, desired
by consumers.
Which one will work? Your guess is better than mine.
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.
- Subject: BeOS security
Speaking of security: What level of enforcement should an OS
provide? Are security issues a matter of personal
administration (as opposed to OS considerations)? Can
security be enforced (programmatically) at the user level?
The discussion became more pertinent as our listeners
exhorted Be to do the right thing. But which right thing?
Some suggestions included:
- Monitor and log all user activity.
- Provide an encrypted TCP/IP stack with strong
two-key encryption, strong cryptographic authentication,
and variable-domain certificates
Paraphrasing Andrew van der Stock, who typed in a laundry
list that fairly summarized the levels of security:
- Application: Apps should have a way of invoking
security features.
- Underlying kernel services: should check that a
desired resource is available to a client app.
- A coherent file system security model that allows
acl's to be set, and that denies access to intruders.
- A coherent tree strategy that allows plug-ins for
tree access.
- A TCP/IP stack that is immune from all known protocol
exploits.
- Subject: opinion on UI issue
From Josh Berdine:
"...an app is running with a window open in one workspace
and the user requests the app to open another window from a
different workspace. What is the desired behaviour; should
the new window open in the workspace the app is already
running in or in the workspace the user is working in?"
The current behaviour is that the desktop switches to the
"old" workspace. Many readers think that switching
workspaces is almost always undesirable -- if you invoke an
entity from workspace N, it should appear in workspace N. A
proposed better way: Window clones in multiple workspaces (a
la the Workspaces preference itself).
Relatedly, what should happen when you double-click an app
icon for an app that's already running? Some folks think
that the Single Launch approach is misguided: When you
double-click an app, you want a new document (right?) -- you
don't want to be led to the documents that are already open
(if you want the latter, so the argument goes, you can ask
DeskBar to take you there).
Why bother with the programming pain of
single-launch/multiple-doc? Apps are small and fast: Why not
just launch another copy of the app for every document? The
counter-argument: In an object-oriented world, it's not a
pain to support multiple documents, and managing multiple
documents (so you can quickly switch between them, for
example) is certainly easier from a single running app.
Regarding "small and fast": App images may be light in the
BeOS, but that's no reason to waste resources.
Still relatedly, what happens to the Single Launch model
when the BeOS is multi-user with X-style remote windows? You
launch an app, then your friend launches the same app from a
remote workspace. Here the "right thing" is (more) obvious:
You clearly don't want to compete for the same app image.
And more: Duncan Wilcox doesn't like apps that steal window
focus (such as CW's error message window). We second the
disgruntlement.
- Subject: What the?!?
Currently, the BeOS limits its RAMification to 512 meg.
Dominic Giampaolo explains why:
"The problem on PPC is that we use BAT registers to map
physical memory and they can only map 256 megs each. We
chose to use one [BAT] for the video card and [another for]
... other devices. We will boot with more than 512 megs of
RAM but we just won't use it all. This is definitely not the
ideal situation but since such large ram sizes aren't that
common, the solution is acceptable until we can do better."
So what are the "better" solutions, and is Be working on it?
THE BE LINE: Mr. Giampaolo and Bob Herold (Be's Colonel of
Kernels) reassured the crowd that, yes, the solutions are
known and Be will fix this situation.
- Subject: Live updates
Subject: Tracker features
Subject: Tracker questions
Tracker suggestions and observations (solicited by Be's
Pavel Cisler):
Okay, so general updating may be overkill -- But what if
updates were on demand, where demand is signalled by an open
Get Info window (as suggested by Chip Paul)?
- Subject: Realtime Audio mixing and modification
Kuraiken started a thread about realtime audio signal
processing. He'd like to see an API for 3D sound, room
simulation, mixing, networked sound, and so on. Many folks
wrote in to point out that much of what Mr. Kuraiken wants
is here! Now! On your desktop!
Be doesn't have any built-in 3D sound or room simulation
API, but mixing is trivial (although, as was pointed out,
some apps do it incorrectly), and getting audio from the net
is simply a matter of reaching out and touching someone.
The discussion broached the subject of multiple audio apps
sharing a common data stream. Is it better to build separate
processing *add-ons* that plug into a master app, or
separate processing *apps* that subscribe to a global
stream? The argument centered on the efficiency of multiple
vs. single address spaces.
The major controversants, while exhibiting commendable
politesse, remained respectively unmoved by the
counterpoints. Look for this one to drag on into the night,
break into a brawl, and end with up everybody drunk, overly
sentimental, and pledging undying brotherhood.
- Subject: BLoopers losing messages
A BLooper 's message queue isn't infinite. It throws away
messages that overflow its port's 100 message limit. Is
there anything you can do to change the limit?
For a BLooper , yes -- use the constructor; but for a
BWindow , no -- you're stuck with the 100 message default.
However, suggests Brian Stern, an app that needs a queue
that's deeper than 100 messages is either badly designed or
poorly implemented.
Peter Potrebic mentioned that he's looking into making
BWindow more flexible with regard to message queue depth.
John Cooke promptly rained on that little parade:
"It is fine for Be to set whatever limit they like, but
programmers must always be aware that *for all limits there
exists a scenario where the limit will be reached* ...So
whacking up the limit only gives one the illusion of solving
the problem."
For calibration, Michael Crawford sent along a data point:
"Just for comparison, the Mac OS event queue holds 20 (count
'em) 20 events. If the queue overflows, the new ones are
still added to the end, while the ones at the head are
dropped! "
Back to Be: What happens in FBC land if the BWindow
constructor is improved to accept a queue-setting argument?
Will this break compatibility? Says Jon Watte:
"Adding an additional constructor will not trigger the FBC
problem, except it will make applications compiled with a
newer version of the system, using newer features, not run
on an older version of the system -- which is to be expected
in any system."
- Subject: GUI Dreams
User Interface talk: Alexander Stigsen has been thinking
about the principles behind an ideal interface. The keyword
is configurability: Nearly every element of the UI should be
user-definable. Mr. Stigsen breaks the UI world into two
hemispheres: Desktop UI and Application UI, and says of
them:
"It should be possible to totally customize all the system
elements of the desktop... It should be possible for the
user to totally customize the GUI of every individual
application on his/hers system. "
Comments? Plenty. Jay O'Conor pointed out an obvious problem
of ultra-configurability:
"Documentation, ease of learning, usability, support, are
all areas that suffer when an interface is too
configurable... Appearances are generally ok to customize,
as this effects only the 'look' of an interface, not the
'feel'. How the interface operates (feels) should remain
consistent."
|