Be Newsletter
Issue 101, November 26, 1997
Table of Contents
Be Developer Classifieds
Be Developer Classified are an opportunity for BeOS
developers to tell each other about projects they're working
on, are thinking about working on, or would like to work on.
Here's your chance to request assistance on a project, or
make your talents known to other projects.
For more information or submissions guidelines, send e-mail
to newsletter@be.com.
- User Input Desired: Supreme3D
SupremeGS, Inc. is working hard to bring you Supreme3D(tm)
on the BeOS(tm). Supreme3D is a revolutionary new 3D
modeling and rendering application that greatly simplifies
the 3D model design process.
SupremeGS(tm) is seeking user input for the design of
Supreme3D(tm). If you are a current user of 3D modeling
tools, we'd like to know what features you use the most and
for what design reasons are they used.
Please reply to supreme3d@supremeGS.com.
For more information, see http://www.supremeGS.com/.
- Developer Tools: Software Testing
As a professional software test developer, I find it
impossible to work with systems and software without
subjecting it to the rigors of testing.
I'm a Be Developer, I am willing to aid other Be Developers
in software design and testing methodology on their
projects, free of charge. A tested app is a working app!
Please contact Taveres L. Ford at: tavares@cray.com.
BE ENGINEERING INSIGHTS: Buried Treasures...
By Dominic Giampaolo
In a system like the BeOS, much effort goes into documenting
the specifics of programming the BeOS and not as much is
spent documenting more common parts of the libraries, such
as the C library.
This makes sense, since there are numerous books on the C
library and POSIX that cover it in adequate detail.
Unfortunately, when some of us incorrigible C weenies add
functions or extensions to the C library it means that they
tend not to get documented.
Thus, it pays to peruse the header files found in:
/boot/develop/headers/posix/
If you poke around in this directory, one "interesting"
header you'll find is parsedate.h . This is the header file
for the parsedate() function that I added to the BeOS C
library (which is part of libroot.so ). The parsedate()
routine is a sophisticated date parsing function that knows
how to convert from a human-readable string into a standard
time_t variable (i.e., the number of seconds since January
1, 1970). It even knows about time zones.
The parsedate() routine is pretty nifty, because it
understands common time formats like "Mon, June 10th, 1993
10:00:03 am GMT". In fact, parsedate() understands virtually
every imaginable verbose time format. This includes all the
typical formats that you'd see in an e-mail message or
Usenet news posting, or that other programs would print
using ctime() or strftime() . The initial list of date
formats supported by parsedate() was generated by culling
the Date: line from around 80,000 news postings, so it's
reliably comprehensive.
Having a routine that can parse such strings and return a
canonical integer time is very useful if you ever have to
parse an e-mail header or input a date that was output from
another program. One drawback to using time formats such as
these is that their rigidity makes them difficult for users
to type. This becomes an issue if you need users to input a
date as part of your program and you want to use
parsedate() .
To remedy the problem of inflexible time formats,
parsedate() also understands many natural date and time
specifications. The parsedate.h file alludes to this, but
doesn't really go into detail about what these formats are.
The parsedate function accepts the following "natural" time
formats (square brackets indicate an optional item):
yesterday
today
tomorrow
[last | next] dayname (monday, tuesday, etc;
e.g., last monday)
[last | next] hour
[last | next] week
[last | next] month
[last | next] year
number minute (e.g. 15 min, -30 minutes)
number hour (e.g. 4 hours, -1 hour)
number day (e.g. 5 days, -3 days)
number month (e.g. 1 month, -2 months)
number year (e.g. 2 years, -3 years)
Some slightly less casual but still fairly loose formats
also work:
11/10/97 3pm
Dec 25th 5:00pm
Friday July 9th
10am Sunday
Because parsedate() understands these types of formats, you
can use it in a variety of situations. It's easy to imagine
a reminder/calendar program that, as an option, lets users
just type a day (i.e., remind me next Thursday of ...). It's
also possible to imagine a mail filter which would scan
message text for date strings and build a calendar
automatically (strings like "this Friday" are easily
detectable).
parsedate() is also helpful in the BeOS Find panel. For
example, if you need to find everything created in the last
two days, you can do a find by last modification time and
simply enter the string "-2 days". Other common finds might
be everything modified since "yesterday" or "last week".
Another aspect of parsedate() is that when there's a choice
of how to interpret a date, it assumes you want a date after
the current time. For example, if today is Wednesday and you
give a time of "monday", parsedate() interprets that to mean
next Monday. If that's not what you want, you may have to be
more explicit or use a relative time format like "-2 days".
We'll add support for other "casual" date formats as we come
across them (I've added about 10 more since I started
writing this article). If you have suggestions for other
common formats, send them to us and we'll see about adding
them to the list (barring ambiguity problems).
To wrap things up, here is simple demonstration program that
shows you how to use parsedate() and lets you play with
inputs to it.
/*
This is a simple program to demonstrate using parsedate()
on the BeOS.
dbg@be.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <parsedate.h>
int
main(int argc, char **argv)
{
char str[256];
time_t remind_time;
struct tm *tm;
printf("\nI am Chronos, the keeper of time.\n");
printf("I am feared by ctime() implementors everywhere.\n");
printf("Enter a time and I will parse it!\n\n");
while (fgets(str, sizeof(str), stdin) != NULL) {
if (str[0] == '\n')
continue;
if (strcmp(str, "quit\n") == 0 ||
strcmp(str, "exit\n") == 0)
break;
/*
woo-hoo! here it is...dun dun da nah...
the call to parsedate()!
pretty complex eh? The -1 argument means to parse
relative to now.
*/
remind_time = parsedate(str, -1);
if (remind_time == ~0) {
fprintf(stderr,
"\nHmmm:\n %sis not recognized.\n", str);
fprintf(stderr,
"You should tell my trusty assistant dbg@be.com\n\n");
continue;
}
printf("\nThe canonical form for the time you entered was:\n");
/*
convert to a struct tm which has all the time fields
broken out
*/
tm = localtime(&remind_time);
printf(" Year: %d Month: %d Day: %d Hour: %.2d:%.2d\n",
tm->tm_year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
tm->tm_min);
/*
also print the result of ctime()
*/
printf(" %s\n", ctime(&remind_time));
}
}
Have fun and maybe next time we'll poke around in that
mysterious looking header file called malloc_internal.h ...
A Remembrance of Things Past: Writing Mass Storage Device Drivers
By Dmitriy Budko
When was the last time you used a RAM drive? Do you still
remember what a RAM drive is?
On my first PC (an AT 286, 12 MHz, 2 MB RAM) a RAM disk was
the only workable way to use more than 1 MB of memory while
running under MS-DOS. Unlike creaky MS-DOS, a current OS
like the BeOS usually uses free memory in the file system
cache; in general, caching algorithms provide much better
performance than a simple RAM disk. Recently though, I was
overcome by inexplicable nostalgia for the good old days and
I decided to write a driver for that "disk" of yesteryear.
So if you have memory to burn, you may want to try using
some of your surplus as a RAM "disk." If nothing else you'll
find out that a RAM disk driver is an excellent example of
using a device driver not only as a hardware interface but
also to emulate it! And if you develop a file system driver,
a big RAM disk can speed up the testing.
I assume that you know the basics of BeOS device drivers, so
I'll focus on features specific to mass storage devices
(floppy drive, IDE disk, IEEE 1394 [FireWire] disk) and on
some tricks related to the volatile nature of RAM.
The driver has to support all standard entry points. In
addition it has to handle some mass storage device I/O
control codes.
I tried to make the driver as simple as possible, so it
supports only one RAM disk. Its dynamic configuration is
almost nonexistent, and its emulation of the hard drive seek
process is very primitive. This simplification let me forget
about synchronization problems.
Here's the driver source code. You can find the original
source, unaltered for or by e-mail transmission, at:
ftp://ftp.be.com/pub/samples/drivers/obsolete/ramdrive.zip
/****************** cut here ******************************/
#include <OS.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <stdlib.h>
status_t vd_open(const char *name, uint32 flags,
void **cookie);
status_t vd_free (void *cookie);
status_t vd_close(void *cookie);
status_t vd_control(void *cookie, uint32 msg, void *buf,
size_t size);
status_t vd_read(void *cookie, off_t pos, void *buf,
size_t *count);
status_t vd_write(void *cookie, off_t pos, const void *buf,
size_t *count);
static void format_ram_drive(void* buf);
static uchar* create_ram_drive_area(size_t drive_size);
static status_t delete_ram_drive_area(void);
static void emulate_seek(off_t pos);
#define RAM_DRIVE_RELEASE_MEMORY (B_DEVICE_OP_CODES_END+1)
#define RAM_DRIVE_EMULATE_SEEK (B_DEVICE_OP_CODES_END+2)
#define RAM_DRIVE_SIZE (8*1024*1024)
#define RAM_BLOCK_SIZE 512
#define MAX_SEEK_TIME 1000.0 /* microseconds */
#define PREFETCH_BUFFER_SIZE (32*1024)
static const char* const
ram_drive_area_name = "RAM drive area";
uchar icon_disk[B_LARGE_ICON * B_LARGE_ICON];
uchar icon_disk_mini[B_MINI_ICON * B_MINI_ICON];
int emulate_seek_flag = FALSE;
uchar * ram = NULL;
static const char *vd_name[] = {
"disk/virtual/ram_drive",
NULL
};
device_hooks vd_devices = {
vd_open,
vd_close,
vd_free,
vd_control,
vd_read,
vd_write
};
status_t
init_driver(void)
{
dprintf("vd driver: %s %s, init_driver()\n",
__DATE__, __TIME__);
ram = create_ram_drive_area(RAM_DRIVE_SIZE);
if(ram == NULL)
return B_ERROR;
return B_NO_ERROR;
}
void
uninit_driver(void)
{
dprintf("vd driver: uninit_driver()\n");
}
const char**
publish_devices()
{
dprintf("vd driver: publish_devices()\n");
return vd_name;
}
device_hooks*
find_device(const char* name)
{
dprintf("vd driver: find_device()\n");
return &vd_devices;
}
status_t
vd_open(const char *dname, uint32 flags, void **cookie)
{
dprintf("vd driver: open(%s)\n", dname);
return B_NO_ERROR;
}
status_t
vd_free (void *cookie)
{
dprintf("vd driver: free()\n");
return B_NO_ERROR;
}
status_t
vd_close(void *cookie)
{
dprintf("vd driver: close()\n");
return B_NO_ERROR;
}
status_t
vd_read(void *cookie, off_t pos, void *buf, size_t *count)
{
size_t len;
status_t ret = B_NO_ERROR;
if(pos >= RAM_DRIVE_SIZE)
{
len = 0;
}
else
{
len = (pos + (*count) > RAM_DRIVE_SIZE) ?
(RAM_DRIVE_SIZE - pos) : (*count);
emulate_seek(pos);
memcpy(buf, ram+pos, len);
}
*count = len;
return ret;
}
status_t
vd_write(void *cookie, off_t pos, const void *buf,
size_t *count)
{
size_t len;
status_t ret = B_NO_ERROR;
if(pos >= RAM_DRIVE_SIZE)
{
len = 0;
}
else
{
len = (pos + (*count) > RAM_DRIVE_SIZE) ?
(RAM_DRIVE_SIZE - pos) : (*count);
emulate_seek(pos);
memcpy(ram+pos, buf, len);
}
*count = len;
return ret;
}
status_t
vd_control(void *cookie, uint32 ioctl, void *arg1,
size_t len)
{
device_geometry *dinfo;
dprintf("vd driver: control(%d)\n", ioctl);
switch (ioctl)
{
/* generic mass storage device IO control codes */
case B_GET_GEOMETRY:
dinfo = (device_geometry *) arg1;
dinfo->sectors_per_track =
RAM_DRIVE_SIZE/RAM_BLOCK_SIZE;
dinfo->cylinder_count = 1;
dinfo->head_count = 1;
dinfo->bytes_per_sector = RAM_BLOCK_SIZE;
dinfo->removable = FALSE ;
dinfo->read_only = FALSE;
dinfo->device_type = B_DISK;
dinfo->write_once = FALSE;
return B_NO_ERROR;
case B_FORMAT_DEVICE:
format_ram_drive(ram);
return B_NO_ERROR;
case B_GET_DEVICE_SIZE:
*(size_t*)arg1 = RAM_DRIVE_SIZE;
return B_NO_ERROR;
case B_GET_ICON:
switch (((device_icon *)arg1)->icon_size)
{
case B_LARGE_ICON:
memcpy(((device_icon *)arg1)->icon_data, icon_disk,
B_LARGE_ICON * B_LARGE_ICON);
break;
case B_MINI_ICON:
memcpy(((device_icon *)arg1)->icon_data,
icon_disk_mini, B_MINI_ICON * B_MINI_ICON);
break;
default:
return B_BAD_TYPE;
}
return B_NO_ERROR;
/* device specific IO control codes */
case RAM_DRIVE_RELEASE_MEMORY:
return delete_ram_drive_area();
case RAM_DRIVE_EMULATE_SEEK:
emulate_seek_flag = *(int*)arg1;
return B_NO_ERROR;
default:
return B_ERROR;
}
}
static void format_ram_drive(void* buf)
{
static const char format_str[16] = "RAM drive ";
uchar* ptr = (uchar*)buf;
off_t i;
dprintf("vd driver: format_ram_drive(%08x)\n", buf);
for(i=0; i<RAM_DRIVE_SIZE/16; i++)
{
memcpy(ptr, format_str, 16);
ptr += 16;
}
}
static uchar*
create_ram_drive_area(size_t drive_size)
{
void* addr;
area_id area = find_area(ram_drive_area_name);
if(area == B_NAME_NOT_FOUND)
{
area = create_area (ram_drive_area_name,
&addr,
B_ANY_KERNEL_ADDRESS,/*kernel team will own this area*/
drive_size,
B_LAZY_LOCK,
B_READ_AREA | B_WRITE_AREA);
if((area==B_ERROR) ||
(area==B_NO_MEMORY) ||
(area==B_BAD_VALUE))
addr = NULL;
}
else
{
area_info info;
get_area_info(area, &info);
addr = info.address;
}
return (uchar*)addr;
}
static status_t
delete_ram_drive_area(void)
{
area_id area = find_area(ram_drive_area_name);
if(area == B_NAME_NOT_FOUND)
return B_ERROR;
else
return delete_area(area);
}
static void
emulate_seek(off_t pos)
{
static off_t old_pos = 0;
if(!emulate_seek_flag)
return;
if(abs(pos-old_pos)>PREFETCH_BUFFER_SIZE)
{
old_pos = pos;
snooze((int)(rand()* MAX_SEEK_TIME)/RAND_MAX);
}
}
/****************** cut here ******************************/
What happens is that init_driver() creates the kernel memory
area that's used to store the data. The driver can't use
malloc() /free() because when the driver is unloaded the
memory (and all data) is lost. It's actually present
somewhere, but there's no easy way to find it when the
driver is loaded again.
So init_driver() calls create_ram_drive_area() , which tries
to find the previously created memory area with the name
"RAM drive area." If the search is unsuccessful the driver
is loaded the first time. In that case
create_ram_drive_area() creates the memory area. It uses a
currently undocumented flag, B_ANY_KERNEL_ADDRESS . This flag
gives ownership of this memory area to the kernel, so the
area will not be deleted when the application that opened
the driver quits.
It also uses the B_LAZY_LOCK flag so the driver doesn't
consume RAM until the first time you use it. I could have
used B_FULL_LOCK and put the memory allocation in vd_open() ,
but being lazy I didn't want to provide a critical section
synchronization for it.
vd_open() can be called a few times simultaneously;
init_driver cannot. publish_devices() creates the device
file in /dev/disk/virtual/ram_drive . The
"/virtual/ram_drive " part of the name is arbitrary, but
"/dev/disk " is mandatory if you want to use a standard disk
setup program -- like DriveSetup -- to configure the RAM
disk.
vd_open() , vd_free() , and vd_close() do nothing and return
success.
vd_read() and vd_write() check to see if the caller tries to
read/write over the end of the disk. They adjust the
requested length accordingly, then simply copy the data
from/to the requested offset in the RAM area to/from the
caller's buffer.
If emulate_seek_flag is set the driver calls emulate_seek() .
emulate_seek() is a crude imitation of real hard drive
mechanics and caching. The current implementation is not in
a critical section so it is capable of multiple concurrent
seeks. It would be great to have a real drive with an
infinite number of head arms! Readers are welcome to create
a more realistic model.
Still, with this function implemented, the RAM drive is
better suited for testing a file system driver. It can block
the caller's tread as a driver for a real drive would do.
vd_control() handles four generic I/O control codes for a
mass storage device: B_GET_GEOMETRY , B_FORMAT_DEVICE ,
B_GET_DEVICE_SIZE , B_GET_ICON . The first three are
mandatory; the last one is optional.
This driver has zero-initialized arrays for large and small
icons. The real driver should provide more pleasing icons
than black rectangles.
RAM_DRIVE_RELEASE_MEMORY deletes the allocated memory area
and thus destroys all information on the RAM drive.
RAM_DRIVE_EMULATE_SEEK sets or clears the emulate_seek_flag .
A control panel application for the RAM drive could send
such commands.
Now here's how to build and install the driver:
- Instruct the linker to produce an add-on image.
- Disable linking to the default shared system libraries.
- Export the driver's entry points -- for example, by
exporting everything.
- Place a copy of the appropriate kernel file (
kernel_joe ,
kernel_mac , or kernel_intel ) in your project directory.
Link against this file.
- Copy the compiled driver in the
/boot/home/config/add-ons/kernel/drivers directory.
- Use your favorite disk format program or BeOS
preferences/DriveSetup to partition and/or install BFS on
the /dev/disk/virtual/ram_drive device.
That's it. You're all set!
I hope that this simple device driver may help get somebody
started in BeOS driver development. If any seasoned device
drivers out there have feature requests or comments (why
does Be have such-and-such stupid restriction or does not
have such-and-such device driver API ...), let me know and
I'll try to implement them. The Intel port in particular may
require same changes: @#$% ISA 16 MB limit on DMA, PCI
interrupt sharing, ISA PnP, etc.
Speak now and you can make your life running the BeOS on
Intel that much easier!
DEVELOPERS' WORKSHOP: BYOB!
By Doug Wright
"Developers' Workshop" is a new weekly feature that
provides answers to our developers' questions. Each week, a
Be technical support or documentation professional will
choose a question (or two) sent in by an actual developer
and provide an answer.
We've created a new section on our website. Please send us
your Newsletter topic suggestions by visiting the website
at: http://www.be.com/developers/suggestion_box.html.
Hello Be people. As the newest Developer Technical Support
Engineer, I have spoken with some of you recently regarding
specific problems but this is my first taste of the Be
Newsletter audience. Playing live shows in a band once or
twice a month has tamed my wild urge to run and hide in
front of a crowd, but writing an article that persists over
time definitely feels different then being live on stage.
So with a little nervousness, I present you with my first
(not last!) bit of Be insight, in two parts. The subjects
were inspired by many things but the titles were inspired by
my favorite thing -- BYOB!
Part I: Bring Your Own Bugs!
Melissa has graciously given me the task of evaluating bugs
submitted by developers. In attempting to climb this ant
hill I have found that some people are natural bug writers
and others, well, are more like Hostess Twinkies. To help
out all those American snack cakes out there, I will offer a
few guidelines to writing a good bug (and the only *good*
bugs are the ones you can hunt down and kill!).
Step 1: A reproducible case is needed. If you can't
reproduce it, neither can we.
Step 2: Document each step necessary to reproduce the bug.
For example:
- Open a Terminal
- Type twinky -old
- Open a Tracker window
- Create a folder called Hostess
- Right click the folder
- Crash!
Step 3: Document your machine configuration. Include all the
hardware AND software your are running. For example, a
developer recently forgot to mention that MALLOC_DEBUG was
turned on.
Step 4: If the app that crashes is yours, include the code.
Don't worry, that little window on the bug report form
scrolls for a long time! Just paste it right in there.
Now I know you're thinking "Hey, I've got 50 source files,
they won't all fit." You need to find the piece of code that
is causing the crash and put it in a little test app.
HelloWorld is great for this. It has an app and a window and
a view. Most bugs don't need more than that to cause
trouble. HINT: This can also help you find bugs in your own
code!
If the only way to cause the crash is to include your entire
source, then zip it up and send it in to devservices@be.com
with a reference to the bug number that you get when you
submit the description using the web form.
Step 5: The last part of Step 4 is really Step 5. Write down
your bug number so that you can contact us about it in the
future.
If you skip any one of these steps, your bug will more than
likely get classified as "Unreproducible." This is bad for
you and for Be, because your bug will still be running
around in the next release causing you and everyone else
problems! If you have any trouble with the bug reporting
process or you feel that we've made a grave error and
classified your bug as a feature, please don't hesitate to
fill out a Developer tech Support form in the Registered Developer Area and we will look into your
problem.
Part II: Build Your Own Buttons!
Are you building that killer tractor app, and you want the
interface to be oh-so-cool? It's not going to knock their
socks of with a bunch of plain-jane buttons. You need to
customize! So I've prepared some sample code to get you
started making your very own buttons. Find the complete
archive on the Be website at:
ftp://ftp.be.com/pub/samples/intro/ButtonWorld.zip
The BPictureButton class is a button that takes two
BPicture s as arguments. You can build these pictures out of
BBitmap s, a combination of BBitmap s and BButton s (for that
grey look) or any other drawing routine. The two pictures
each represent one of two states, on and off.
I started by scanning a familiar form. I used Photoshop on
the Mac ;-( to save my scanned image in raw format. You need
to remember the dimensions of the image and the bit depth.
The width needs to be a multiple of four in order to use the
command line tool craw to convert them into code. My images
were 48 x 48 so I just typed "$craw 48 48 myrawimage " in a
terminal.
(For more information on how to use craw check out...
http://www.be.com/aboutbe/benewsletter/Issue15.html
This is an oldie but a goodie! William Adams also addressed
creating custom graphics from images in a News from the
Front article...
http://www.be.com/aboutbe/benewsletter/Issue77.html
Some of the samples he referred to have been moved,
including mkimghdr , which is now at...
ftp://ftp.be.com/pub/samples/interface_kit/obsolete/mkimghdr.tgz.)
Craw generates an unsigned char array. blue4x4 and blue4x4on
are the names of my two arrays. Now I'm ready to fill some
bitmaps and create a button...
/* NOTE: This is all happening during the construction of a
Window inheriting from a BWindow. */
BRect rect;
rect.Set(0,0,47,47);
//bitmaps for the pictures
BBitmap onBitmap(rect, B_COLOR_8_BIT );
BBitmap offBitmap(rect, B_COLOR_8_BIT );
//fill bitmap
onBitmap.SetBits(blue4x4on, 18432, 0, B_COLOR_8_BIT);
offBitmap.SetBits(blue4x4, 18432, 0, B_COLOR_8_BIT);
/* Next, I create two BPictures and draw my bitmaps
in them. */
//tempview for creating the picture
BView *tempView =
new BView(rect, "temp", B_FOLLOW_NONE, B_WILL_DRAW);
AddChild(tempView);
//create on picture
BPicture *on;
tempView->BeginPicture(new BPicture);
tempView->DrawBitmap(&onBitmap);
on = tempView->EndPicture();
//create off picture
BPicture *off;
tempView->BeginPicture(new BPicture);
tempView->DrawBitmap(&offBitmap);
off = tempView->EndPicture();
//get rid of tempview
RemoveChild(tempView);
delete tempView;
/* Finally I create my BPicture button and the other things
that it needs including the message that will be sent to
its target. */
//create a message for the button
BMessage *pictmsg = new BMessage(BUTTON_MSG);
pictmsg->AddString("text", "Picture Button");
//create a picture button using the two pictures
rect.Set( 120, 45, 167, 92 );
BPictureButton* pictureButton =
new BPictureButton(rect, "picture", off, on, pictmsg,
B_TWO_STATE_BUTTON);
/* The last argument for the BPictureButton is a flag for
the mode. B_TWO_STATE_BUTTON behaves like a toggle
switch. Turn it on with one click. Turn it off with
another. A B_ONE_STATE_BUTTON is only on while you hold
the mouse down. */
/* Once you have created your button you can add it as a
child to the window. The buttonView is a BTextView to
which we will send our message. */
// add view and button to window
AddChild(buttonView);
AddChild(pictureButton);
/* Finally in order to direct the message your button sends
you need to assign it a target. */
// make the view the target of the buttons
pictureButton->SetTarget(buttonView);
Now when you click on the button, a message is sent to the
buttonView which displays the string contained by the
message. To see how the BStringView handles the message,
please check out the complete sample. Have fun and remember,
we're happy to have you BYOB at Be! If you need more
information, don't hesitate to ask!
A Nice Comdex
By Jean-Louis Gassée
It is fashionable to complain about Comdex, from the bad
food, the taxi lines, expensive hotel rooms, sore feet at
the end of the day and silly carnival acts in the booths of
companies who should know better. Perhaps, but I still like
Comdex, and I liked this one even better, for two reasons.
The first is Umax. Our partner graciously hosted us on their
booth in the main hall, providing us with the opportunity to
show both the PowerPC and the Intel versions of the BeOS as
befits their own business addressing both standards.
As expected we got both good reactions and blank stares,
when not eyes rolling. Some visitors knew us, or had heard
about us and were happy to get a progress report. Others had
no idea we existed and a few questioned our sanity. When we
got the opportunity, we disposed easily of the mental health
question by pointing out the difference between OS/2 trying
to dislodge Windows and the BeOS happily coexisting with the
general-purpose OS.
This experience is a useful reminder of what awaits us in
the Intel-based market. It's not just larger than the
PowerPC market, it's much different and our reputation, the
exposure we enjoy in the PowerPC segment, aren't worth much
in the new space. The newer Intel version performed well
during the week, much better than we had anticipated and,
towards the end, we sneaked in a "just baked" port on an
Intel-powered laptop.
The second reason to like Comdex this year is the abundance
of technology coming out of gestation, ready to become a
real product at Fry's some time in the next twelve months.
Flat panels were big, literally and in their ubiquity. A
forty-six-inch panel is still horribly expensive, but the
smaller models are soon to grace the desktops of Corporate
America.
Closer to our business, video cameras, ever higher-speed
graphic cards, still cameras, IEEE 1394 connections,
high-bandwidth disk adapters...all sing the song of better,
faster, more affordable digital media.
This year we didn't hear the old saw: "The industry is
becoming commoditized, boring, less innovative." Confusing,
a little disorganized perhaps, but we like that, there is
little room for a start-up such as ours in a perfectly
stable and organized world.
As for the expensive rooms and the bad food, a minivan gets
you a little out of the way, the hotel prices plummet and
you even find restaurants without slot machines.
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: Help with error...
We've all seen it...the "Data > 32K" compiler error message.
What does it mean? It means you've asked for too much local
data. The 32k limit is enforced by the compiler in order to
be ANSI compliant, but beyond that, is biting off huge
chunks of local data bad programming? Earl Malmrose saw some
advantages in the practice:
"Being local, you have automatic garbage collection of a
sort - [the data] will never be leaked. You also have to
write less code."
Jon Watte listed some reasons behind the limitation, among
which:
"In a multi-threaded environment, each thread has to be
given its own stack...there is a very real trade-off between
how many threads you can create, and how much stack space
they get."
Nonetheless, there were objections to the "bad programming
practice" characterization. It was contended that the
negative citations were architecture specific (hardware or
software) -- that there were no intrinsic reasons why huge
local data is bad. Then Osma Ahvenlampi added this:
"In addition to the problems pointed out by others, note
that it [relying on the stack for memory allocation] will
not give you any kind of error recovery. What happens when
there isn't enough memory to give you that big a stack
frame? Crash, most likely."
- Subject: Digital Signatures on the BeOS
Can a PGP-style signature be stored as an attribute of a
file? (Sure.) But would the attribute itself affect the
signature for the file? (Not if you didn't want it to --
attributes can be selectively ignored.)
Expanding the equation, what if you wanted to verify
attributes as well as data? Peter Folk suggested a 3-tier
scheme:
PGP__SIG is a signature for the main data stream.
PGP_<attributename>_SIG is a signature for whichever
attributes you care to sign (excluding PGP__SIG ).
PGPSIG_SIG is a signature of all the PGP_*_SIG attributes.
This may be overkill, suggests Jon Watte:
"Having one signature for each attribute is rather
wasteful... Instead, you can sign the data with one
signature, and the union of all non-changing [attributes]
with another; that should be enough even for the most
paranoid among us."
Speaking of paranoia, the thread veered into a discussion of
reliable key retrieval and verification. Anthony Towns
provided a tidy wish list that summarized the elements of
the problem (paraphrased here):
- Some way of storing public keys. This would naturally
expand to include encryption as well as verification
keys...
- Some way of easily requesting keys. This is strongly
related to the public key database: first you check it,
then you check a public key server somewhere.
- Library support for verifying signatures.
- Convenient methods for signing files (including support
for various signature algorithms).
- An integrated encryption API.
But should Be be in the encryption business? Some folks
think not.
Also, it was contended that the entire
signature-in-an-attribute approach is flawed: Attributes can
get lost in an ftp transfer (for example). Important
signature information should be encoded in the data portion
of a file.
- Subject:
BRect confusion
Should the Width() of a BRect return the "virtual" width of
the rectangle, or the number of pixels the stroked and
filled rect touches? The function returns the former, a
practice that many developers are confused by; when you ask
for the width of a rectangle, you should get a count of the
number of (horizontal) pixels it encloses. But, goes the
counter-argument, such a measurement would need to consider
the pen size, so the status quo is proper. If you want the
pixel-touched measure, you have to add the pen size to the
rectangle width.
Eric Berdahl contends that the width +pen_size business is a
product of Be's "center pen" approach. He would like to see
Be adopt a more flexible pen model (i.e. "inset" and
"outset" pens).
- Subject: Be supporting developers
Devtalkers take a step back (or across, or something) and
discuss Be's marketing approach, its seemingly unshakable
association with Apple, whether a brainwash-the-CS-
department approach can work, the role of free and eminently
portable software in an OS company's success, and other
non-technical matters that all broached the question: How
shall Be thrive? Lots of opinions, or, at least, a lot of
attitudes.
Side-stepping back into the tech stream, another question
was raised: What's a Be app? Does a port count, or is there
some other defining element. Some proposed litmus strips:
BWindow . If you don't have a BWindow , you're not a Be app.
BMessage s. Non-UI apps (servers) can still qualify if they
respond to BMessage s.
|