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> intmain
(intargc
, char **argv
) { charstr
[256]; time_tremind_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
...
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/preview/drivers/ramdrive.zip
/****************** cut here ******************************/ #include <OS.h> #include <Drivers.h> #include <KernelExport.h> #include <stdlib.h> status_tvd_open
(const char *name
, uint32flags
, void **cookie
); status_tvd_free
(void *cookie
); status_tvd_close
(void *cookie
); status_tvd_control
(void *cookie
, uint32msg
, void *buf
, size_tsize
); status_tvd_read
(void *cookie
, off_tpos
, void *buf
, size_t *count
); status_tvd_write
(void *cookie
, off_tpos
, constvoid *buf
, size_t *count
); static voidformat_ram_drive
(void*buf
); static uchar*create_ram_drive_area
(size_tdrive_size
); static status_tdelete_ram_drive_area
(void); static voidemulate_seek
(off_tpos
); #defineRAM_DRIVE_RELEASE_MEMORY
(B_DEVICE_OP_CODES_END
+1) #defineRAM_DRIVE_EMULATE_SEEK
(B_DEVICE_OP_CODES_END
+2) #defineRAM_DRIVE_SIZE
(8*1024*1024) #defineRAM_BLOCK_SIZE
512 #defineMAX_SEEK_TIME
1000.0 /* microseconds */ #definePREFETCH_BUFFER_SIZE
(32*1024) static const char* constram_drive_area_name
= "RAM drive area"; ucharicon_disk
[B_LARGE_ICON
*B_LARGE_ICON
]; ucharicon_disk_mini
[B_MINI_ICON
*B_MINI_ICON
]; intemulate_seek_flag
=FALSE
; uchar *ram
=NULL
; static const char *vd_name
[] = { "disk/virtual/ram_drive", NULL }; device_hooksvd_devices
= { vd_open, vd_close, vd_free, vd_control, vd_read, vd_write }; status_tinit_driver
(void) {dprintf
("vd driver: %s %s, init_driver()\n", __DATE__, __TIME__);ram
=create_ram_drive_area
(RAM_DRIVE_SIZE
); if(ram
==NULL
) returnB_ERROR
; returnB_NO_ERROR
; } voiduninit_driver
(void) {dprintf
("vd driver: uninit_driver()\n"); } const char**publish_devices
() {dprintf
("vd driver: publish_devices()\n"); returnvd_name
; } device_hooks*find_device
(const char*name
) {dprintf
("vd driver: find_device()\n"); return &vd_devices
; } status_tvd_open
(const char *dname
, uint32flags
, void **cookie
) {dprintf
("vd driver: open(%s)\n",dname
); returnB_NO_ERROR
; } status_tvd_free
(void *cookie
) {dprintf
("vd driver: free()\n"); returnB_NO_ERROR
; } status_tvd_close
(void *cookie
) {dprintf
("vd driver: close()\n"); returnB_NO_ERROR
; } status_tvd_read
(void *cookie
, off_tpos
, void *buf
, size_t *count
) { size_tlen
; status_tret
=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
; returnret
; } status_tvd_write
(void *cookie
, off_tpos
, constvoid *buf
, size_t *count
) { size_tlen
; status_tret
=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
; returnret
; } status_tvd_control
(void *cookie
, uint32ioctl
, void *arg1
, size_tlen
) { device_geometry *dinfo
; dprintf("vd driver: control(%d)\n",ioctl
); switch (ioctl
) { /* generic mass storage device IO control codes */ caseB_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
; returnB_NO_ERROR
; caseB_FORMAT_DEVICE
:format_ram_drive
(ram
); returnB_NO_ERROR
; caseB_GET_DEVICE_SIZE
: *(size_t*)arg1
=RAM_DRIVE_SIZE
; returnB_NO_ERROR
; caseB_GET_ICON
: switch (((device_icon *)arg1
)->icon_size
) { caseB_LARGE_ICON
:memcpy
(((device_icon *)arg1
)->icon_data
,icon_disk
,B_LARGE_ICON
*B_LARGE_ICON
); break; caseB_MINI_ICON
:memcpy
(((device_icon *)arg1
)->icon_data
,icon_disk_mini
,B_MINI_ICON
*B_MINI_ICON
); break; default: returnB_BAD_TYPE
; } returnB_NO_ERROR
; /* device specific IO control codes */ caseRAM_DRIVE_RELEASE_MEMORY
: returndelete_ram_drive_area
(); caseRAM_DRIVE_EMULATE_SEEK
:emulate_seek_flag
= *(int*)arg1
; returnB_NO_ERROR
; default: returnB_ERROR
; } } static voidformat_ram_drive
(void*buf
) { static const charformat_str
[16] = "RAM drive "; uchar*ptr
= (uchar*)buf
; off_ti
;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_tdrive_size
) { void*addr
; area_idarea
= 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_infoinfo
;get_area_info
(area
, &info
);addr
=info
.address
; } return (uchar*)addr
; } static status_tdelete_ram_drive_area
(void) { area_idarea
=find_area
(ram_drive_area_name
); if(area
==B_NAME_NOT_FOUND
) returnB_ERROR
; else returndelete_area
(area
); } static voidemulate_seek
(off_tpos
) { static off_told_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 thread 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" 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!
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.
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/preview/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...
Be Engineering Insights: craw, shex, and script: Excuse Me?
This is an oldie but a goodie! William Adams also addressed creating custom graphics from images in a News From The Front article...
Some of the samples he referred to have been moved, including mkimghdr, which is now at...
ftp://ftp.be.com/pub/samples/preview/interface_kit/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 picturesBBitmap
onBitmap
(rect
,B_COLOR_8_BIT
);BBitmap
offBitmap
(rect
,B_COLOR_8_BIT
); //fill bitmaponBitmap
.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 pictureBView
*tempView
= newBView
(rect
, "temp",B_FOLLOW_NONE
,B_WILL_DRAW
);AddChild
(tempView
); //create on pictureBPicture
*on
;tempView
->BeginPicture
(newBPicture
);tempView
->DrawBitmap
(&onBitmap
);on
=tempView
->EndPicture
(); //create off pictureBPicture
*off
;tempView
->BeginPicture
(newBPicture
);tempView
->DrawBitmap
(&offBitmap
);off
=tempView
->EndPicture
(); //get rid of tempviewRemoveChild
(tempView
); deletetempView
; /* 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 buttonBMessage
*pictmsg
= newBMessage
(BUTTON_MSG
);pictmsg
->AddString
("text", "Picture Button"); //create a picture button using the two picturesrect
.Set
( 120, 45, 167, 92 );BPictureButton
*pictureButton
= newBPictureButton
(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 windowAddChild
(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 buttonspictureButton
->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!
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 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.
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.”
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.
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).
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.