Be Newsletter
Issue 75, May 28, 1997
Table of Contents
BE EVENTS: Be Demo Tour in San Francisco, CA
BeOS Demo Meeting:
Saturday May 31, 1997 from 6:00pm to 9:00pm
Where:
INTERNET alfredo - CyberCafe
790-A Brannan Street
San Francisco, CA 94103
For full directions to INTERNET alfredo,
please see:
http://ina.com/alfredo/directions.html
BE ENGINEERING INSIGHTS: BScreens and Puppy Paws
By Scott Bronson
The beauty and sensibility of a final result often belies
the time and effort that went into creating it. Good dancers
appear to have been born moving effortlessly. An attractive,
well-dressed human being likely didn't wake up looking quite
so polished. Everything we see, from the most majestic
landscape to the least significant spore, required a period
of growth.
Whatever the refinement of the result, this means that
everything went through periods of lankiness and growing
pains. Puppy paws appear far too big for the legs they're
mounted on. A baby's head dwarfs its tiny body. When
growing, dependencies require that some parts grow faster
and be in place sooner than others.
A browse through the DR9 Advanced Access header files will
hint at things to come, like multiple displays. The header
file Screen.h describes the new API for applications to
obtain information about screens in a remarkably dynamic
multiple screen environment.
Imagine a full multitasking operating system that will allow
displays to come and go at will. Let's say the user is done
at the office (imagine!) and -- without restarting --
unplugs his BeOS notebook from his 17" monitor, tucks it
under his arm, and takes it home. Without any help, the OS
notices that the 17" CRT is gone and the 14" built-in LCD
display should be the new main screen. In a flurry of
windows moving and updating, everything in the system shifts
to the new configuration with no surprises for the user.
Certainly a nice image. But not without a few minor problems
to solve. For instance, how does a program refer to specific
screens when they can appear and disappear at will? It
cannot use a pointer, as it is hard to tell when the pointer
is invalid, and the consequences for misuse are dire.
Neither can it use a "GetNthScreen " call, as the screen
indexes will change when screens are added and removed (when
monitor 1 is removed, monitor 2 becomes monitor 1, monitor 3
becomes monitor 2, and so on).
The solution is the screen_id , a unique, opaque 32-bit
identifier, representing a screen attached to the computer.
When a screen is disconnected, its screen_id falls into
permanent disuse. When it is reconnected, it is assigned a
new ID. Because screen_id s are meant to be lightweight, they
are not persistent across boots. A solution involving far
more than 32 bits will be developed later to persistently
identify screens.
And, now the star of the show. The BScreen is an extremely
lightweight, easy to construct class that represents a
single screen. BScreen s are meant to be constructed
immediately when information is needed, and to exist for as
short a time as possible. Although construction and
destruction take insignificant time, some of the calls (such
as BaseAddress and BytesPerRow ) are slower than you might
expect because they involve a synchronous round-trip
AppServer request.
To prevent race conditions with the results of the BScreen
functions, the display will not change when any BScreen
objects referring to it are in existence. The display is
locked in the BScreen 's constructor and freed in the
destructor so neither the Screen preferences panel nor
anybody else will be able to change the horizontal or
vertical size, bit depth, color palette, or anything else
that might affect client applications. This is why it is
extremely important to destroy the BScreen , allowing the
display to change again, as soon as possible. A user won't
react kindly to an application hogging his screen, and when
informed which is the culprit (it's not hard to figure out
who is misusing the BScreen ), will remorselessly blow it
away.
class BScreen {
BScreen( screen_id id=B_MAIN_SCREEN_ID );
BScreen( BWindow *win );
~BScreen();
The best way to construct a BScreen object is to pass in a
window. The constructor will return the screen that contains
that window (or the majority of the window). If you need
more flexibility, you can pass the ID of the desired screen.
Because BScreen s are meant to be created on the stack, the
destructor should be called automatically. Manually creating
and deleting BScreen s on the heap is a sure recipe for leaks
(again, likely leading to an irate user and your
application's unceremonious termination).
bool IsValid();
When screens can come and go at will, it will be possible
for the BScreen constructor to be passed a screen_id which
is no longer valid. The BScreen will be constructed anyway,
but will refer to the main screen instead of the screen you
asked for. This is so you don't have to do error checking
every single time you construct a BScreen . Accidentally
using the main screen will prevent crashing, and will
generally produce acceptable results anyway. IsValid will
return false if the BScreen refers to the main screen
instead of the screen you asked for.
color_space ColorSpace();
BRect Frame();
screen_id ID();
void* BaseAddress();
uint32 BytesPerRow();
status_t WaitForRetrace();
These calls are pretty much self-explanatory, except that
WaitForRetrace is temporarily unimplemented (it just returns
B_ERROR right now).
uint8 IndexForColor( rgb_color rgb );
uint8 IndexForColor( uint8 r, uint8 g, uint8 b,
uint8 a=0 );
rgb_color ColorForIndex( const uint8 index );
The BeOS has a system color table that applies to every
bitmap, every 8-bit screen, basically everywhere indexed
color is used. VRAM is cheap enough now that if you need
realistic color, it is much easier to simply set the display
to 32-bit color than to manage and arbitrate the palette
(even with full OS support). So why not make these calls
global? Because even though this capability will likely go
unused in the BeOS, the hardware DOES allow each individual
screen to have its own color environment.
uint8 InvertIndex( uint8 index );
Passed a color index, InvertIndex returns the index of a
suitable inverse. It uses the inversion map returned by
ColorMap()->inversion_map .
const color_map* ColorMap();
ColorMap() allows you to save time by doing your own table
lookups. Don't use the color map after its BScreen has been
destructed. Suitably bad things will happen.
rgb_color DesktopColor();
void SetDesktopColor( rgb_color rgb,
bool stick=true );
These set the screen's desktop color. If stick is true, then
it saves the change to disk. If not, then the desktop color
change will last only until the computer is restarted.
};
inline uint8 BScreen::IndexForColor( rgb_color rgb )
{ return IndexForColor( rgb.red, rgb.green, rgb.blue,
rgb.alpha ); }
Finally, a pinch of syntactic sugar.
Let's see how to use BScreen . First, a simple example that
draws a rectangle, colored the inverse of the desktop color,
in a view.
void MyView::Draw( BRect where )
{
BRect area( 10, 10, 110, 110 );
rgb_color deskcolor;
BScreen screen( Window() );
// Now, the screen containing the majority of the window
// has been locked. It will not change while the
// function is executing.
deskcolor = screen.DesktopColor();
if( screen.ColorSpace() == B_COLOR_8_BIT ) {
// do the inversion calculation using indexed colors
uint8 icol = screen.InvertIndex(
screen.IndexForColor(deskcolor) );
SetHighColor( screen.ColorForIndex(icol) );
} else {
// Invert the rgb_color
SetHighColor( ~rcol.red, ~rcol.green, ~rcol.blue );
}
FillRect( area );
// The screen variable is destroyed, and its display
// released and allowed to change again, when this
// function returns.
}
If the screen is currently in 8-bit mode, the inversion
calculation is done using indexed colors. This is not
strictly necessary -- inverting the rgb_color would provide
visually acceptable results -- but this is a good example of
how to manipulate color indexes.
Otherwise, if the screen is not in 8-bit-mode, the screen
must be in a direct mode, and we'll calculate the inverse
directly from the color's individual components. Finally,
the rect is filled in, and the desktop color's inverse is on
screen.
A somewhat more advanced usage of BScreen (snipped from the
Connect source code):
void Border::MessageReceived( BMessage *m)
{
const color_map *cm = BScreen( Window( )).ColorMap( );
if (m->what < sizeof(cm->color_list)/
sizeof(cm->color_list[0])) {
SetViewColor( cm->color_list[m->what]);
Invalidate( );
}
}
First, notice that the BScreen object is anonymous -- it was
never given a name. The rules of construction and
destruction still apply, so the BScreen will not be
destroyed until the flow of execution leaves the enclosing
brackets; in this case, when the function returns. Even in
the worst case, Border::MessageReceived takes a tiny amount
of time to execute, so this is acceptable. The variable cm
will go out of scope when the BScreen is destroyed, so its
validity is ensured for the duration of the function.
The conditional makes sure that the index m->what falls
within the bounds of the color table. Then, using a simple
table lookup (instead of the slightly more expensive
ColorForIndex call), the appropriate rgb_color is passed to
SetViewColor . Simple, straightforward, and there is no
chance that the color table will change or disappear until
the function returns.
BScreen is not meant to be used to lock the screen (though
it can certainly be misused to do that for now). A more
sensible way to allow applications to directly and
asynchronously modify screen bits (i.e. DMA) is in
development.
Do you remember get_screen_info and friends from the DR8
InterfaceDefs.h ? They're still there in the Advanced Access
DR9, but they are tiny little functions implemented using
BScreens. They will go away in the full DR9 release. Use the
old API at your peril.
So, yes, with BScreen I've added a tiny bit of lankiness to
the DR9 BeOS. Lanky not because it's hard to use (at least,
I sure tried to make it simple), but because significant
parts of it are not used today. However, just as a dog's
legs eventually grow to fit its paws, the BeOS will grow to
fit its header files, and the result will be lean, fast, and
powerful.
BE ENGINEERING INSIGHTS: Unicode UTF-8
By Don Larkin
You may have heard that DR9 adopts the Unicode Standard for
encoding characters and, in particular, the UTF-8
transformation of Unicode character values. You can read all
about UTF-8 in "The Unicode Standard, Version 2.0" published
by Addison-Wesley, but here's a synopsis for those of you
that don't have the book.
Unicode is a universal encoding scheme for all the
characters in the major scripts of the world -- including,
among others, extended Latin, Cyrillic, Greek, Devanagiri,
Telugu, Hebrew, Arabic, Tibetan, and the various character
sets used by Chinese, Japanese, and Korean. It assigns a
unique and unambiguous 16-bit value to each character,
making it possible for characters from various languages to
co-exist in the same document. Unicode makes it simpler to
write language-aware software (though it doesn't solve all
the problems). It also makes a wide variety of symbols
available to an application even if it's not concerned with
internationalization.
Unicode's one disadvantage is that all characters have a
width of 16 bits. Although 16 bits are necessary for a
universal encoding and a fixed width is important for the
standard, there are many contexts in which byte-sized
characters would be easier to work with and take up less
memory (besides being more familiar and backwards compatible
with existing code). UTF-8 is designed to address this
problem.
UTF-8 stands for "UCS Transformation Format, 8-bit form"
(and UCS stands for "Universal Multiple-Octet Character
Set," another name for Unicode). UTF-8 transforms 16-bit
Unicode values into a variable number of 8-bit units. It
takes advantage of the fact that for values less than
0x0080, the Unicode character set matches the 7-bit ASCII
character set -- in other words, Unicode adopts the ASCII
standard, but encodes each character in 16 bits. UTF-8
strips ASCII values back to 8 bits and uses two or three
bytes to encode Unicode values over 0x007f.
The high bit of each UTF-8 byte indicates the role it plays
in the encoding: If the high bit is 0, the byte stands alone
and encodes an ASCII value. If the high bit is 1, the byte
is part of a multiple-byte character representation.
In addition, the first byte of a multibyte character
indicates how many bytes it takes to represent the
character: The number of high bits that are set to 1 (before
a bit is 0) is the number of bytes in the encoding.
Therefore, the first byte of a multibyte character will
always have at least two high bits set. The other bytes in a
multibyte encoding have just one high bit set.
To illustrate, here is how UTF-8 arranges the bits in one-,
two-, and three-byte encodings (where a '0' or '1' indicates
a control bit specified by the standard and an 'x' is a bit
that contributes to the character value):
1: 0 x x x x x x x
2: 1 1 0 x x x x x 1 0 x x x x x x
3: 1 1 1 0 x x x x 1 0 x x x x x x 1 0 x x x x x x
Note that any 16-bit value can be encoded in three UTF-8
bytes. However, UTF-8 discards leading zeroes and always
uses the fewest possible number of bytes, so it can encode
Unicode values less than 0x0080 in a single byte and values
less than 0x0800 in two bytes.
In addition to the codings illustrated above, UTF-8 takes
four bytes to translate a Unicode "surrogate pair" -- two
conjoined 16-bit values that together encode a character
that's not part of the standard. Surrogates are extremely
rare. See "The Unicode Standard" for details.
The UTF-8 encoding scheme has several advantages:
- The single byte that encodes an ASCII value can't be
confused with a byte that's part of a multiple-byte
encoding. You can test a UTF-8 byte for an ASCII value
without considering its context; if there's a match, you can
be sure the byte is the ASCII character. UTF-8 is fully
compatible with ASCII.
- The first (or only) byte of a character can't be confused
with a byte inside a multibyte sequence. It's simple to find
where a character begins. For example, this macro does the
job:
#define BEGINS_CHAR(byte) ((byte & 0xc0) != 0x80)
- The string functions in the standard C library -- such as,
strcasecmp() , strcat() , and strlen() -- can operate on a
UTF-8 string.
However, it's important to remember that strlen() measures
the string in bytes, not characters. Some Interface Kit
functions, like GetEscapements() in the BFont class, ask for
a character count; strlen() can't provide the answer.
Instead, you need to do something like this to count the
characters in a UTF-8 string:
int32 count = 0;
while ( *p != '\0' ) {
if ( BEGINS_CHAR(*p) )
count++;
p++;
}
Also, you should be careful when using the string comparison
functions to order a set of strings. Unicode tries to be a
universal encoding and orders characters in a way that's
generically correct. However, it may not be correct for
specific characters for specific languages. (Because it
follows ASCII, UTF-8 is correct for English.)
- For European languages, UTF-8 generally yields more
compact data representations than would Unicode. Most of the
characters in a string can be encoded in a single byte. In
many other cases, UTF-8 is no less compact than Unicode.
The BeOS assumes UTF-8 encoding in most cases. For example,
a B_KEY_DOWN message reports the character that's mapped to
the key the user pressed as a UTF-8 value. The message has a
data array named "byte " with each byte of the encoding as a
separate item. The bytes are extracted from the array and
passed as a string to the new version of KeyDown() , along
with the byte count:
virtual void KeyDown(const char *bytes, int32 numBytes);
You can expect the "bytes " string to always contain at least
one byte. And, of course, you can test it for any ASCII
value without caring that it's UTF-8:
if ( bytes[0] == B_TAB )
. . .
Similarly, BeOS objects that display text in the user
interface -- such as window titles and button labels --
expect to be passed UTF-8 encoded strings, and will pass you
a UTF-8 string if you ask for the title or label.
Although the BFont class allows other encodings, which you
may need to use from time to time, the system fonts are
stuck with UTF-8. It's recommended that you stick with it as
well wherever possible.
News from the Front
By William Adams
What kind of programmer are you? That's what I ask myself
on a regular basis. Am I motivated by fame, fortune,
prestige, or something like a desire to change the world?
Well, honestly, it's a combination of all of these. I've
done the mission critical custom app thing to great success,
and now I've moved on to more interesting things. When I
program the BeOS, I'm excited. I mean really. Staying up
until 4am for that "one more compile" just so you can see
picture in picture video running on a standard BeOS machine?
Well, that's what I did this weekend.
ftp://ftp.be.com/pub/samples/videomania.tgz
I don't watch TV that much, but when a TV tuner/video
capture card costs only $127 at Fry's, how could I possibly
resist. The interesting thing about the Hauppauge board is
that it has a tuner, and composite video inputs. You can
switch between them on the fly. With two of these boards in
your machine, you basically have 4 video input sources.
This is better than my TV at home, and costs a lot less.
I'm telling you, if you haven't already gone out and bought
one of these capture cards yet, now would be a good time.
The other interesting thing about the Hauppauge board is
that it works better at full screen rather than smaller.
Why is that? DMA my fine fellow, DMA. The Hauppauge board,
or more accurately the Brooktree Bt848 video capture chip,
does DMA transfers directly into video memory if you tell it
to. So full screen 60 fields per second 32 bit color looks
pretty cool, and you don't need high energy machines to
achieve this performance. When you display at a smaller
scale, the Bt848 has to do some scaling. This doesn't
really slow it down, but the quality isn't as good. The
beauty of DMA is that the CPU doesn't really get involved.
A lowly PowerPC 603 66MHz could do the job.
The thing that keeps me programming is the leap frog
inspiration that really cool technology causes. Once you
have one of these capture cards, you might think of ways to
improve your "entertainment system." You might add easy
access to the web and TV Guide, or you might find it
interesting to add control of your FireWire-based VCR and CD
players. Whatever your motivation, there's nothing like
getting all excited about driving a tractor all over the
apps that other poor farmers are still struggling with.
Judging from our mail and BeDevTalk postings, you're all
very active with DR9. We're pumping out sample apps as fast
as we can, and working on documentation. Keep sending us
your input as to how we can better serve you. Without your
efforts, this platform will be a technology looking for a
solution. Keep up the good work and keep sending in that
valuable praise and criticism. We in turn will continue to
try and provide you with the best platform upon which you
can dream up your killer apps.
Back From Europe
By Jean-Louis Gassée
I spent a week in the old country, meeting software
developers, investors and business partners, mostly from
France, but also from other parts of Europe. We've invested
early in Europe, based on a combination of beliefs and
connections. The connections relate to my checkered past in
the computer industry over there. I worked in the French as
well as the European HQ of my alma mater Hewlett-Packard, at
Data General, at Exxon Office Systems (briefly, quickly
recognizing the error) and starting Apple France, before
fulfilling an old dream and moving to the Silicon Valley. As
a result, when we started the company, we benefited from a
hospitable European network. Friends invested in the company
and introduced us to other investors; we abducted people to
California and linked up again with associates from earlier
lives.
In many respects, the company probably wouldn't be around if
it weren't for our European connections. In 1991, when we
started raising money, US investors were understandably
reluctant. Microsoft was already perceived as the invincible
juggernaut -- the agility came later when they became a
born-again Internet company. At the time, PDAs, set-top
boxes and 3DO game consoles were in fashion and we couldn't
quite demonstrate our OS side by side with older platforms.
The "no legacy, no baggage" equation was hard to explain.
In so called "high-tech" industries, Europe is perceived as
having fallen behind the US. In several instances, it is
even true as in the case of the PC business. Companies such
as Olivetti or Nixdorf were once industry leaders. Their
misfortunes haven't dulled European appetites for new
technology. In many respects "conservative" Europeans are
more daring than New World players. VCRs, CDs and, more
recently, the Macintosh and digital cellular telephones
enjoyed a better early acceptance in the old continent than
in the US. (Once the new standards get accepted in the US,
the market quickly makes up for a late start.)
In Be's case, our unconventional proposition found early
takers in Europe. Some investors were not awed by the
Microsoft "€ber alles" mystique, others saw in us the
Silicon Valley connection. One of them told us bluntly he'd
"never invest in a deal like this based in Europe"...
European developers react in a similar fashion. First, in an
apparent paradox, just like the blunt investor, many local
developers would be reluctant to bet their time and energy
on a European media OS. And, second, our evangelism efforts
got "traction" earlier in Europe than in the US. Does it
mean the famous "tractor app" will come from Europe rather
than the US? It is still too early to tell, but we see
another positive factor in our European connection. It is
even harder to make money in the software business in Europe
than it is in the US. As a result of our use of the Net to
market and deliver software, European developers see the
BeOS as a way to break into the US market unavailable with
the conventional distribution network. Our goal is to make
their hopes come true.
|