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_ids 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.
classBScreen
{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_spaceColorSpace
();BRect
Frame
(); screen_idID
(); void*BaseAddress
(); uint32BytesPerRow
(); status_tWaitForRetrace
();
These calls are pretty much self-explanatory, except that WaitForRetrace
is temporarily unimplemented (it just returns B_ERROR
right now).
uint8IndexForColor
( rgb_color rgb ); uint8IndexForColor
( uint8r
, uint8g
, uint8b
, uint8a
=0 ); rgb_colorColorForIndex
( constuint8index
);
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.
uint8InvertIndex
( uint8index
);
Passed a color index, InvertIndex
returns the index of a suitable
inverse. It uses the inversion map returned by
.
ColorMap
()->inversion_map
constcolor_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_colorDesktopColor
(); voidSetDesktopColor
( rgb_colorrgb
, boolstick
=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 uint8BScreen
::IndexForColor
( rgb_colorrgb
) { returnIndexForColor
(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.
voidMyView
::Draw
(BRect
where
) {BRect
area
( 10, 10, 110, 110 ); rgb_colordeskcolor
;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 uint8icol
=screen
.InvertIndex
(screen
.IndexForColor
(deskcolor
) );SetHighColor
(screen
.ColorForIndex
(icol
) ); } else { // Invert the rgb_colorSetHighColor
( ~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):
voidBorder
::MessageReceived
(BMessage
*m
) { constcolor_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
falls within the bounds
of the color table. Then, using a simple table lookup (instead of the
slightly more expensive m
->what
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 BScreen
s. 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.
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:
int32count
= 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 voidKeyDown
(const char *bytes
, int32numBytes
);
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.
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/dr9/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.
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.