Those of you who read my last Newsletter article, "Chelsea 'n' Me," may be amused to know that about a week after that Newsletter went out, an individual in the whitehouse.gov domain subscribed to the Be Newsletter. He is no longer subscribed (perhaps he's gone under cover) but in case he's still listening in, I'd like to give him a big Hello.
Hello! I'm not a stalker or anything. I don't carry any weapons or have life-size photos of You-Know-Who in my apartment. Uh... I listen to Fleetwood Mac... All the code I write is ISO 9000 compliant... Yeah, just your average, everyday, non-subversive, capitalist Amurikin. Er... these aren't the droids you're looking for...
Release 3, big surprise, is turning into a very ambitious release. Aside from all the little things like porting the code to a completely different architecture and rewriting large portions of several subsystems, we've managed to sneak in some features. I'll tell you about them, but only if you promise not to go tell everyone else. If this gets out, everyone's gonna want a copy. Hey, wait...
One interesting feature that makes its first appearance in Release 3 is
the Be drawing API of a state stack. Those of you familiar with coding in
Postscript or OpenGL® will find the existence of a BView
state stack
comforting and useful; others may need a quick introduction to the
concept.
The idea is fairly simple. There exists a stack of rendering states
associated with each BView
. Performing a "push" to this stack saves the
current rendering state and places it at the top of the stack. Performing
a "pop" restores the state at the top of the stack, and throws the saved
copy away.
A state stack is an abstraction that is potentially very powerful, but can also be somewhat limiting. The motivations for adding a stack-based context to our current dynamic variable-space model are several:
State stacks allow very modular drawing code, when used correctly.
They provide a way to save and restore whole rendering states. This makes sense from a performance point of view, as the stacks are maintained on the server side.
You don't have to use them if you don't like them.
I like them.
One of the best things about state stacks is not, however, that people who don't want to use them don't have to. It's that people who do want to use them can make others do so without them even knowing. That is, one of the most useful properties of a state stack is the context it creates, within which innocent, pre-Release 3 drawing code will function differently but consistently.
Let's take a look at an example, and then I'll explain explicitly how stack manipulations affect each variable of the state:
staticBPoint
points
[4] = {BPoint
(0.1,0.9),BPoint
(0.3,0.6),BPoint
(0.7,0.6),BPoint
(0.9,0.9)}; staticBPoint
origins
[4] = {BPoint
(0,0),BPoint
(1,0),BPoint
(0,1),BPoint
(1,1)}; voidRecurseView
::DrawPanels
(intrecurse
) { if (recurse
>0) {SetScale
(0.5); for (inti
=0;i
<4;i
++) {SetOrigin
(origins
[i
]);PushState
();DrawPanels
(recurse
-i
-1);PopState
(); } } else {BPoint
pt
[4];SetDrawingMode
(B_OP_COPY
);SetPenSize
(0.002);SetLineMode
(B_ROUND_CAP
,B_ROUND_JOIN
,10.0); for (inti
=0;i
<256;i
++) {SetHighColor
(255,255-i
,255-i
); for (intj
=0;j
<4;j
++)pt
[j
] =points
[j
] -BPoint
(0,0.5*i
/255);StrokeBezier
(pt
); }SetDrawingMode
(B_OP_OVER
);SetFontSize
(0.2);SetHighColor
(0,0,0);DrawString
("BeOS",BPoint
(0.25,0.6));SetHighColor
(0,0,255);DrawString
("BeOS",BPoint
(0.243,0.593)); }; } voidRecurseView
::Draw
(BRect
updateRect
) {SetScale
(Bounds
().Width
());PushState
();DrawPanels
(4);PopState
(); }
This code won't even compile under PR2 (sorry!). But it should be pretty clear what's happening here. We recurse, pushing new states onto the stack along the way. When drawing actually occurs, it occurs within the context of the states that are currently on the stack.
This statement is a bit misleading. Often the stack has no effect on current drawing; it is merely an easy way to retrieve state, after it has been mangled by a called function, for instance. But the context created by the stack is important with relation to three variables: scaling, origin, and clipping. Respectively, the local current value of these states are multiplicative, additive, and...uh... and-ive, with the stack context associated with them.
That is, the local scaling is multiplied with the total effective stack scaling, the local origin is added to that on the stack, and the current clipping region is ANDed to that on the stack. This allows the behavior in the above example, in which the code does not know what context within which it is executing, but the level of recursion above the current one has already placed and scaled it in the window.
Extrapolating from this rather contrived example, it should be easy to see that such a technique is useful in a dynamic drawing environment that one might see in a vector-based drawing package. Extending this stack-based methodology to richer feature sets, as our APIs continue to evolve, is straightforward, and it becomes more useful the richer the feature set.
This introduction has been mostly to whet your appetite. The sordid details of state stacks will be documented in the Be Book.
Moving on from state stacks, one of the most visible new features is that most of the renderers are now optionally subpixel precise. This new behavior means much higher quality rendering in many cases. Internal polygonal decompositions will always use subpixel precision, and so any drawing code which uses thick pen sizes will take some advantage of this automatically. In order to maintain backwards compatibility, points passed into the Interface Kit will still be rounded as they were in PR2, unless you know the secret handshake:
newBView
(BRect
(0,0,100,100), "InLikeFlynn",B_FOLLOW_ALL
,B_WILL_DRAW
|B_SUBPIXEL_PRECISE
);
If you're a Be developer, this all probably looks familiar except for
B_SUBPIXEL_PRECISE
. Setting this flag (in the constructor, or later using
SetFlags()
) causes all points passed into the Interface Kit to be used
as-is, with no rounding. This can cause unexpected drawing quirks in some
apps that don't need the precision, so the flag is disabled by default.
Where will subpixel precision be useful? Imagine, for instance, that you
are drawing a parametric curve, and you want to use the app_server to do
your drawing for you. You might try something like this in a BView
's
Draw()
method:
BPolygon
poly
;SetPenSize
(10.0); for (floatt
=0;t
<=1.0;t
+=0.01)poly
.AddPoints
(&BPoint
(t
,t
*t
),1);BRect
b
=Bounds
();b
.InsetBy
(20,20);poly
.MapTo
(poly
.Frame
(),b
);StrokePolygon
(&poly
,false
);
This will work just fine in PR2, but the rendering quality will not be what you'd expect. In Release 3, however, this will produce a very smooth curve, taking advantage of the subpixel precise positioning of each point you are providing.
That said, there are better ways to draw a curve in Release 3. We have added primitives to render cubic beziers. You'll also find full support for all Postscript line capping and joining modes (i.e., rounded, beveled, mitered, etc.). And somewhere along the way, someone accidentally added 15 and/or 16-bit color depth support for several video cards. Oops.
Finally, the last of the biggest changes to the drawing APIs is that
BPicture
data is no longer directly accessible. Old code which directly
reads BPicture
data (using
BPicture
::Data()
) will still work, but this
method is now deprecated and should no longer be used in new code.
To save and load BPictures
, the standard
Archive()
and Instantiate()
(or
Flatten()
and Unflatten()
)
calls should be used. To access the picture
data, a new Play()
method exists, which takes a callback table and plays
back the picture on the client side by calling these callback functions
with the necessary data. This makes accessing the data much easier,
increases the robustness of the format immensely, and paves the way for
easy future expansion. The callback table format is too large to describe
here, but, again, it will soon be documented in the Be Book.
I'll be discussing some of these new features, in the context of techniques for optimizing interface drawing, at the BeDC in March. If you will be attending the conference, and have any particular performance issues you'd like addressed in that session, drop me a line at geh@be.com. I certainly can't promise anything, but I'd like to hear where the interest is.
Personnel have 5 minutes to reach minimum safe distance. Keep coding, and watch your back.
Two weeks ago I digressed about my lack of time awareness. This week I'm
coming to you live from the planet Koozbain. Where we will witness the
mating ceremony of the Koozbainian Hooziwutzle. Wait, that was my
childhood. I told you I have a problem with time. That's why I've decided
to continue with the Ticker example and refine my system_time()
based
ticker. I use a shared area to pass ticks between applications.
<SEGUE> But why, you ask, do you get to write so many articles in the Newsletter? Well, this week I moved up in the rotation to help out Steven Beaulieu, who spent the week in Paris at Comdex IT.</SEGUE>
<SHAMELESS PLUG> That's right, as a Developer Technical Support Engineer, you get to do all sorts of exciting things like travel to exotic places and hang out with geeky foreigners—or geeky Americans traveling abroad.
Other times you are entertained by investigating mind-bending bug reports, explaining new APIs, or exploring a developer's quirky code. My favorite part is getting up in front of the audience either in a Newsletter or live and in person at the upcoming Be Developer's Conference, and trying to explain why I enjoy staying up late writing code for the BeOS. It's great fun!</SHAMELESS_PLUG>
<PLEA FOR HELP>If you would like to live the wild life and feel up to the challenge, submit a resume to jobs@be.com with Developer Technical Support in the subject.</PLEA FOR HELP>
Watch the Be Employment Listings for this and other exciting opportunities. http://www.be.com/aboutbe/jobs/index.html
Now, back to our regularly scheduled broadcast...
Last time I challenged you to yank out the TCIn
class and the chaser
class and stick them in another app, then send a message from the chaser
app to the ticker app with the port_id of the chaser. See the first
article to catch up if you missed it:
http://www.be.com/aboutbe/benewsletter/volume_II/Issue4.html#Workshop
In this week's sample code, I've done just that for you. This week's archive is appropriately named timecodeticker2: ftp://ftp.be.com/pub/media_kit/obsolete/timecodeticker2.zip
It includes timecodeticker from last time with some new code to set up communication with other apps, and an example listener app that syncs to the timecodeticker. I've also changed some of the internals of the timecodeticker. Now, rather than passing all the information directly on a port, it uses a shared area to communicate data from one app to many.
write_port() gets less efficient as the messages get larger. Using a shared area allows me to pass just an index into the memory, which keeps the jitter lower. A chaser can then get all the info it needs when it receives a tick, on its own thread of time. For more information on using areas, check out the Be Book / Kernal Kit / Areas: http://www.be.com/documentation/be_book/kernel/areas.html
I would also like to clarify my use of 30 frames per second in last week's example. NTSC television runs slightly slower than 30 frames per second, at 29.97 fps. I was being misleading by dismissing this point and using a tick period that was calculated straight from 30 fps. For more information on video and timecodes, check out Steve Sakoman's Video Basics articles (1 and 2), and the reference books he recommends: http://www.be.com/aboutbe/benewsletter/volume_II/Issue5.html#Insight http://www.be.com/aboutbe/benewsletter/Issue93.html#Insight
With the BTimecode class, we've created a timecode_type, so I've changed the tcticker constructor to take a timecode_type and use a period based on that. I've also added a multiplier parameter to the ticker so that you can have it shuttle both forward and backward at continuous speeds from 0 to 3 times the frame rate. Next time I'll show you how to hook up the new Slider class from Release 3 as a shuttle controller.
Have fun coding, and I look forward to seeing you at the BeDC in March!
Following a story that appeared in the San Jose Mercury News Business section on February 5th (http://www.sjmercury.com/columnists/gillmor/docs/dg020698.htm), I received many phone calls and e-mails. Although Dan Gillmor's piece confirmed my account of Adamation's showing at Demo 98, my honorable correspondents focused on another item in the article—a report that Be had closed a round of financing in excess of $20 million. What happened, where is the press release on your site, what's the story?
What happened is we decided to pass up the opportunity to issue a press release trumpeting our financing. Reporters get about fifty of these a day and pay little attention to the mind-numbing flow of pompous, freeze-dried verbiage, with the obligatory quotes from the CEO, the VP of Multicultural Diversity, and some choice "industry observer." We know, we just did one of these for our participation to SD 98.
Initially, the Mercury News wanted to write a story on the emergence of BeOS applications, following Demo 98; Dan Gillmor asked me how our financing was doing, to which I replied we were closing the very same day —and that's how his story saved us a press release.
As some readers remember, I described our road show last summer, as we were visiting investors in the US, Europe, and Japan, under the merciless rule of our jackbooted, ex-Navy Seal, investment banker, Bill Bonde, from Cowen and Co. I even got some frowns from our lawyers for rhapsodizing a little too much in this space over the beauty of our business proposition.
But, there we are, with the help of our Demo God, Alex Osadzinski, and a little sanity from our CFO, Wes Saia, we have secured the support from venture, corporate, and personal investors in the US, Europe, and Asia. We now have the resources required to grow the BeOS platform for the benefit of our developers and customers and, ultimately, our shareholders.
It wasn't always easy. We had to answer two questions in succession: what happened with Apple and, a little later, where are the PowerPC clones? Fortunately, as we got help in porting the BeOS to Intel- based PCs, it became obvious our engineers' work had something to offer as a specialized digital media OS coexisting with the general purpose Windows. As a result, enough investors saw a way for us, and themselves, to develop a viable business—and the critical mass for the financing round happened.
In the end, it's that simple, but it doesn't feel that way during the process, nor at the end when a closing has to be coordinated over time zones and cultural differences such as the use of signature pages, a no-no in Europe, for instance. Fortunately, our law firm, Cooley Goodward, in the persons of Andrei Manoliu and Dan Johnston, knows how to dispense more than legal advice: they provide calm as well.
We have the support we needed and the Be team can turn its full attention to developing the platform, in both technical and business senses. Now we have to deliver.
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.
Should Be's documentation and source code examples assume (as they do) that the reader has a working knowledge of C++? The BeOS is supposed to be easy for experienced C++ programmers; why can't this attitude be extended to newbies? As Jeff A Campbell put it:
“With BeOS being such a 'clean slate' as far as operating systems go, wouldn't it be the BEST place to learn C++?”
Most of our listeners agreed that "BeOS for C++ Dummies" would be an admirable undertaking, but Be isn't the entity to take it under. Earl Malmrose:
“No, please no. Be should continue their focus on making the OS the best it can be. Let 3rd parties fill in the niches like books and tutorials. There is already plenty of sample code.”
There was some gnashing of teeth, but most folks agreed: Want to learn C++? Go buy a C++ book.
“I feel that installing a program should be nothing more than decompressing it to the folder where you want it to reside. Is this asking for too much?”
This would require some "environment discipline": An installer shouldn't blithely overwrite existing files, shouldn't hard code path names, shouldn't spew files all over the system, and so on. Listeners suggested that in addition to UI guidelines, Be should publish installation guidelines, perhaps something as simple as a checklist.
And just what all should an installer do? Should an installer also uncompress? Does an installer need to be aware of security issues? Need it be multi-user safe? Some readers felt that an uncompressing installer (or an installing uncompressor) is a security nightmare. Chris Herborth:
“[A script-driven zip program] is a massive security hole, so I don't think anyone is in favour of it. Anything that happens automatically 'for' the user is easy to abuse.
Personally, I prefer to do this:
unpack the archive
read the README
do the installation”
We've heard this song before. Hummed here by Marco Nelissen:
“If you don't trust the installer, then why should you trust the application that it installs?”
Getting back to the original secondary point, just what is an installer supposed to do? Simply unpack an archive? Unpack it in a known place? Find the libraries that are needed? Set up user preferences? Sean Gies thinks that an installer should unpack an app, and find libraries, and warn the user of filename collision...
“Everything else, like preferences and paths, should be dealt with at runtime.”
Regarding data format, most of our listeners agree with Alan Shutko:
“The reason zip or something like it would be good is that you could easily look inside the archive using standard tools. You could also look inside and pull out files on other platforms...”
By default, an inactive Be window don't accept the first mouse click for anything but activation. Should the default be changed? Not on Tyler Riti's dime:
“If developers felt it was a problem, they'd just initialize their
BWindow
s with B_ACCEPTS_FIRST_CLICK
...
there are very good reasons for
not having the window accept the activation click...in document windows
like in a text editor, accepting the first click will unselect
highlighted text and change the cursor location which is unexpected and
counter-productive.”
The talk rummaged around in the menu bin for awhile: Which menus should be obligatory (File? Edit?), where does the About Box invoker go (in a final Help menu?).
Replicants are groovy, but what are they good for? Sean Gies got to thinking and came up with a couple of scenarios that showed Replicants in action. But is anyone actually using Replicants in the manner described? Jon Watte:
“There are people doing work on Replicants. However, it is by design 'only' an enabling technology, not a full-featured suite of protocols for component documents (which is a whole different bag of chips).”
Mr. Watte pointed out, however, that the BeOS does practice some of what it preaches:
“Many user interface elements such as buttons and whole view layouts can be stored as Replicants (which are just Archive()d views) which means that Replicants could be a really fundamental part of a user interface layout tool...”
Duncan Wilcox made some Replicant suggestions and observations:
“A scripting message suite might be defined that lets Replicant containers share the menu bar with the Replicant itself.”
“[With Replicants,] a simple visual scripting language would be feasible —it would let users compose scripts by wiring together data-mungers in a simple dataflow diagram.”
“Replicants aren't only archived BView
s, but might also be sent/pulled by
one app to another. For example an app might pull in an archived BLooper
that implements a specific protocol and talks to the app via a
well-defined scripting message suite.”
Despite all the jubilation, Attila Mezei is cool on the subject:
“A Replicant doesn't know enough of the application which it was inserted in, and the application doesn't know about the Replicant. I wish Be worked on a _simple_ application/document framework which allows inserting plug-in functionalities in existing applications.”
Is Mr. Mezei describing OpenDoc, Apple's much-belittled unified <thing> theory? Perhaps (says Mr. Mezei), but why not learn from the failure and do it right?