Issue 3-10, March 11, 1998

Be Engineering Insights: File Types, the Tracker, and You (Plus Some Unrelated Tracker Rants)

By Pavel Cisler

BeOS Release 3 uses file types much more extensively than Preview Release 2. Since file types have been a mild source of confusion to developers in the past, and could be more so in the future, this article is an attempt at reducing the mystery factor.

Application developers need to make sure that file types are set up correctly. As you know, file types are represented by MIME strings, and applications have a list of all the types they support in their application info structure.

Dragging and dropping documents onto application icons now works only if the corresponding application supports the given type or the group (super-type) the type belongs to. If you drag a document onto an app that doesn't support it, the icon doesn't highlight. If you drop it, it's the same as moving or copying it.

The Tracker's new "Open With" feature relies entirely on file types and the information in the MIME database to do its job. "Open With" lets you choose from all the applications that support a given document, displaying them in a hierarchically ordered menu or Tracker window.

Say you have an application that can edit text files. Let's take my favorite, StyledEdit, as an example. The native document format of StyledEdit is 'text/plain', which is listed as a supported type in StyledEdit's application info.

However, StyledEdit can open just about any text file, including source code, HTML, etc., some of which have a different type than 'text/plain'. Instead of trying to enumerate all the different types of text files in the list of supported types, StyledEdit lists 'text'—in other words it lists the entire MIME group as its supported type.

Note, though, that listing 'text/*' does not do the job, since '*' is, in fact, a valid string representing a sub-type, and it is definitely not interpreted as a wild card. Several applications out there list similarly confused file types. You know who you are, and now is your chance to fix it.

Using the super-type as a supported type this way is convenient for applications that, for example, use an add-on architecture to open video files and you can't tell ahead of time which add-ons your application will end up with.

Back to our StyledEdit example. Even though 'text/plain' is a subset of 'text', you still want to include 'text/plain' in the supported types list. The new "Open With" feature sorts applications so that those listing specifically 'text/plain' appear before those that only list 'text'. Also, you may want distinct icons for different file types. In other words, by listing a specific file type you are claiming to be able to handle that particular file type.

As a rule of thumb, you should include all the types that your application can generate and all the types your application can treat in a special way, even though it already lists the corresponding super-type.

To add a super-type as a supported type in an application, use the good old FileTypes application. Open your application or the .rsrc file you use to build it, press the Add button and select the super-type that you want to add (e.g., 'text'). You can also drag the super-type from the System File Types window and drop it into the list of supported types.

If you have an application that can open any type of document, such as a ResEdit-style app or a file hex editor, you could enumerate all the existing file type groups (MIME super-types). Instead though, you should include 'application/octet-stream' (Generic file) as a supported type. Applications that list this type are super-handlers that the Tracker lets you drop any type of document onto.

FileTypes and the new DiskProbe are both examples of super-handler applications. You'll notice that both show up at the bottom of the list of suggestions every time you ask to open a document using the "Open With" feature.

Your application should not include the wild card 'application/octet-stream' unless it can handle any file, and it also makes sense for it to handle any type. Say you wrote a paint program that listed 'application/octet-stream', but when you actually dropped a file of any type other than an image, you got a dialog that "Document 'bla' is not an image but it has 14359 bytes ..."

Not only would you sooner or later get arrested by the MIME Police for misusing the super-handler privilege, but users would also despise you for polluting their "Open With" menu/window with a false claim that your application can handle the type 'audio/tube-sound' when in fact it doesn't (don't you just hate all those gizmos that claim to sound like tubes but don't even come close?). In other words, think twice before you decide that your application is a super-handler and before you include 'application/octet-stream' as its supported type.

It goes without saying that if you specify a super-type (such as "audio") as a supported type, you need to make sure your application does in fact handle any type of that super-type gracefully, and that it won't crash if, for instance, your application gets to open an audio file for which there is no add-on. In fact, you should make sure every application can handle every document gracefully and not rely on the file type mechanism for factoring out the documents that could make your application crash or otherwise misbehave. Here are two of the many reasons why:

Therefore, make sure your app can deal with file types, that it can, for example, bring up a clear explanation when it is asked to open a document it doesn't understand instead of crashing.

More About File Types

We've tightened the screws on some of the file type API calls. The MIME strings used for file types and application signatures can only contain lower ASCII printable characters. /\<>@,;:"()[]?= are illegal characters. The API does not let you set the application signature to anything that does not start with 'application/' and you won't be able to set it to any of the common file types of an application or executable (seems obvious but there were apps out there that had it all confused).

All the file type handling code in Release 3 is case insensitive in conformance with the MIME standard.

Text Clippings

Enough about file types. Here's a new Tracker feature that you can support in your application:

The Tracker now handles drag-and-drop text and creates text clipping files. Try dragging some text from StyledEdit and dropping it onto the desktop. Note that the name of the resulting clipping file has the name of the originating document in it. This is done by adding a hint to the drag message that Tracker interprets as the suggested clipping file name. When building the drag message, you can add the hint as follows:

BMessage dragMessage(B_MIME_DATA);

// add the actual text to the drag message
dragMessage.AddData("text/plain", B_MIME_TYPE,
  someText, someLength);

// add the suggested clipping file name
draggy("be:clip_name", "my favorite text clipping");

StyledEdit uses BTextView, which has a convenient new call GetDragParameters() that lets you do just that. So if you are already using BTextView, you just add the suggested name in the overridden GetDragParameters().

Why Is My Tracker Add-on Crashing?

Finally, here's a little update to Victor's cool article, "Fiddling With Tracker Add-ons," from the February 4 Newsletter: http://www.be.com/aboutbe/benewsletter/volume_II/Issue5.html#Workshop

Some developers have run into this problem. Say you have a Tracker add-on with a user interface that consists of classes that you wrote/overrode, for example, you added some special features to BWindow in a MySuperTrackerAddonWindow class. If you use the window in a Tracker add-on like this, your add-on will most likely crash mysteriously, each time probably in a different place:

void process_refs(entry_ref someRef, BMessage *someMessage,
                  void *unused)
{
  ...
  BWindow *window = new MySuperTrackerAddonWindow(...);
  window->Show();
}

What happens is not quite as mysterious as it may seem. You created your own window and launched it with the Show() function. The window goes about its business in its own thread while your process_refs returns. The Tracker assumes the add-on is finished and unloads its code while MySuperTrackerAddonWindow is still running: The rug got pulled right from under its feet, er, I mean the program counter.

Obviously, the Tracker could keep the add-on in memory, making it launch a bit faster the next time. However the benefit of being able to add/remove/change add-ons on the fly is great, and after all it's the behavior you expect from the BeOS, right?

The correct way of dealing with this is to wait for any and all of the threads that your add-on spawned to terminate, in our example:

void process_refs(entry_ref someRef, BMessage *someMessage,
                  void *unused)
{
  ...
  BWindow *window = new MySuperTrackerAddonWindow(...);
  window->Show();
  long dummy;
  wait_for_thread(window->Thread(), &dummy);
}

It's a little trickier with multiple threads (for instance more dialogs open, one for each entry_ref) but I'm sure you get the picture and will be able to deal with the more advanced cases.


Slap Up my beOs

By Baron Arnold

you know why i like using bEos at home?
i don't have to press F1.

there's a bug in PR2.
but we fixed it for Release 3.

it's not rocket science,
but it is work.
it's about drivers before applications.
it's about strength before style.
you can front,
but you will be confronted.

when a director feels that the very last take
is the last grand stab at genius the camera will record,
he says,
"It's a wrap."

Release 3 is a wrap.
it's done.
it's shipping.

take your heart,
restarted,
into our labor of love.

re-shuffle,
re-organize,
assess your strengths and weaknesses.
harness your revolutionary sprit and plow forward into the soil.
bEos is strong.

because money rains onto the valley,
and a river of vision has overrun its banks,
again.

we have coated a little plastic disc with sixes and nines.

install it.
run it.
forget the pronunciations.
don't worry about the title.
because you've been on this planet forever.
because you believe in fate.

i live in a rock and roll hovel.
reckless but free.

i pin, as many of you do, my tail on a rolling chair,
in front of a monitor i wish was bigger,

on a dream i wish was more useful,

in a wrestling match with focus.

because because because because because,
because of the wonderful things it does.

developers rool.
and baron drools.

see you at the conference.
i love you all.


Developers' Workshop: Welcome to x86

By Doug Wright

"Greetings, people of earth! You will all worship my band and buy my records!" Wouldn't it be great if you could tell everyone what to do? Well, with the BeOS' scripting architecture you can! In preparation for my upcoming BeDC presentation I needed to brush up on my scripting, so here's some beginning material to get you started while it's fresh in my mind.

Scripting on the BeOS uses BMessages. This should be no surprise, since BMessages are great containers for commands and data, and can be easily sent from one application to another. The receivers of BMessages are BHandlers. They respond appropriately to the message, either by handling it or passing it on to another BHandler.

Be uses three main terms in its scripting metaphor: Properties, Specifiers, and Suites.

Properties are the things in an app that you'd like to control from a script. Four messages control properties:

B_CREATE_PROPERTY
B_DELETE_PROPERTY
B_GET_PROPERTY
B_SET_PROPERTY

Any part of an object can be considered a property. Code handles scripting messages that SPECIFY that property.

You use specifiers to describe the property you are scripting. A specifier must name a property as well as an instance of that property when there is more than one. You can encapsulate specifier data inside another BMessage. There are seven defined specifier message types:

B_INDEX_SPECIFIER
B_REVERSE_INDEX_SPECIFIER
B_RANGE_SPECIFIER
B_REVERSE_RANGE_SPECIFIER
B_NAME_SPECIFIER
B_ID_SPECIFIER
B_DIRECT_SPECIFIER

The simplest specifier is B_DIRECT_SPECIFIER. "Direct" means that the property name is all that's needed to determine what to return.

Here's a direct specifier message in the making:

BMessage name_msg(B_GET_PROPERTY);
name_msg.AddSpecifier("Name");

There are special functions for adding specifiers to a message. If you try to use BMessage::AddMessage, the specifier will not be understood for who it is, and we all know what that's like. ;-)

So how do we figure out what properties an object has and what messages it understands? This is where suites come in. A suite is the description of the properties an object understands. There are several suites defined by Be and built into the Be kits. The suites are named in MIME style, for example:

suite/vnd.Be-handler
suite/vnd.Be-application
suite/vnd.Be-control

The suite 'suite/vnd.Be-handler' is the most basic. All BeHandler-derived objects inherit this basic understanding of scripting messages.

To demonstrate some basic scripting messages, I cooked up a little app that gets a list of supported suites for all running applications. All the work happens in the FillList() function.

void
SuiteListView::FillList()
{
  char *name;
  char count_str[25];
  int32 count;
  BMessage reply;
  BMessage name_msg(B_GET_PROPERTY);
  name_msg.AddSpecifier("Name");
  BMessage suite_msg(B_GET_PROPERTY);
  suite_msg.AddSpecifier("Suites");
  BMessage count_msg(B_COUNT_PROPERTIES);
  count_msg.AddSpecifier("Window");

  BList teams;
  be_roster->GetAppList(&teams);
  int n = teams.CountItems();
  printf("%d apps running\n", n);

  for(int i=0; i<n; i++){
    team_id id = (team_id)teams.ItemAt(i);
    BMessenger msgr(NULL, id);

    /* ask for Name and Suites */
    if(msgr.SendMessage(&name_msg, &reply, 10000, 10000)
      == B_OK){
      printf("waiting for reply\n");
      reply.PrintToStream();
      if(reply.FindString("result", 0, &name) == B_OK){
        printf("%s\n", name);
        BStringItem *app = new BStringItem(name);
        AddItem(app);

        /* ask for supported Suites */
        if(msgr.SendMessage(&suite_msg, &reply, 10000, 10000)
          == B_OK){
          BStringItem *label =
            new BStringItem("Supported Suites");
          AddUnder(label, app);

          int x = 0;
          while(reply.FindString("suites",x,&name) == B_OK){
            printf("%s\n", name);
            BStringItem *suite = new BStringItem(name);
            AddUnder(suite, label);
            x++;
          }
        }

        /* ask for Window count */
        if(msgr.SendMessage(&count_msg, &reply,10000,10000)
          == B_OK){
          printf("waiting for reply\n");
          reply.PrintToStream();
          if(reply.FindInt32("result", 0, &count) == B_OK){
            sprintf(count_str,"WindowCount = %d", count);
            BStringItem *wincount =
              new BStringItem(count_str);
            AddUnder(wincount, app);
          }
        }
      }
    }
  }
  printf("finished with apps\n");
  return;
}

I use the global be_roster to get messengers for the running applications. Then I send each app three messages: one to get the name of the app, another to get the supported suites, and a third to get a count of the windows.

In a future article I'll turn this into a useful app that lets you create and send messages to a particular app handler.

In the meantime, check out the Be Book chapter on scripting: http://www.be.com/documentation/be_book/app/scripting.html

Stay tuned, and remember, worship my band and buy my records!


Murphy Works at Be

By Jean-Louis Gassée

Or so it seems. A few days ago, we thought we had Release 3 (the shorthand designation for our upcoming Intel release) nailed. Instead, we got nailed, and how!

It started with a member of our board of directors. Two weeks ago, after our first post-financing Board of Directors meeting, we gave Garrett Gruener a handmade Release 3 CD. He went home happily and proceeded to install the BeOS on one of his systems. It didn't work. We don't support the graphics card on his system, yet.

So, the trial changed venues—to a machine graciously proffered by Garrett's spouse. It turned out that we support her configuration but, alas, not her data. Somehow, the partitioning tool we used (silently) set the Windows partition to the new and improved FAT 32 file system, thereby rendering the partition unreadable by Windows 95 OSR1, which only understands the august DOS FAT 16 file system.

We know this now, and also how to set a partition back to FAT 16, but when trouble struck, we thought we had destroyed the data on our director's wife's hard drive. How to make a good impression and succeed in the software business. Dominic Giampaolo, a Be engineer who bravely ventured his technical support services, ended up working through most of the night and finally succeeded in recovering the data, understanding the cause of the problem, and feeding the appropriate information to the people in charge of the partitioning software we bundle on the BeOS CD.

Our "luck" was in testing the installation process thoroughly. I was an installation guinea pig myself, to make sure the process was "executive-proof," but even this extreme form of QA wasn't pragmatic enough. We made an invisible assumption, we used OSR2, the latest Win 95 OEM Service Release, and we got caught. Realizing this, we called the 200 or so recipients of similar CDs. Please, please, if you have OSR1, proceed with extreme caution. We appreciate the positive feedback we got by e-mail, phone, and newsgroup posts—many of you obviously "felt our pain."

But we weren't done yet. I got a brand new "Ming Special," proceeded to carve out a BeOS partition and install the newly improved Release 3. The installation stalled. Engineers were called, looked at the system, looked again, hooked it to a life support system to watch serial port output during the boot process, fiddled with the BIOS. Nothing.

Then Windows wouldn't even boot. We suspected a hardware problem. As it turns out, one of our engineers has an identical system and does not experience the same symptoms. Eventually we discovered that my system issues a non-maskable interrupt when first reading the real-time clock at boot time and the kernel doesn't expect it and things gets stuck. Why on my system? We should count ourselves lucky we caught that situation inside, and now.

These are all useful reminders of the complexities and the humbling experiences ahead of us.


BeDevTalk Summary

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.

NEW

Subject: declspec...???????

AKA: BeOS carries on M$ legacy...

The Release 3 BeOS strongly recommends the use of the PC industry-standard __declspec() directive to import/export symbols. Is this a sell out (to Microsoft rule)? Fighting words, and shoving ensued. In a flame abatement effort, young Ficus Kirkpatrick had this to add...

Why are things being done the Microsoft way? Metrowerks is Be's compiler vendor, and that's what they had available... [furthermore] by using a format that is supported by tons of compilers already (Borland, Symantec, Microsoft, the list goes on), it makes the porting effort easier, should some tools vendor want to start supporting BeOS.

Cutting through the thicket, Sean Gies asked a practical question:

Will either of these methods [declspec or pragma] compile and link on both PPC and x86 versions? I would like to be able to write the source code _once_ and only change compile and link options for the different platforms.

And Jon Watte answered:

“Both #pragma and __declspec() works on both PPC and Intel. And, while __declspec(import) is not strictly necessary for code on Intel, it IS for data (globals and class statics).

If you want to go with text files, you can create a .exp file for PPC, and a COM file for Intel. Yes, you need to create two different files, but then you touch no source.”

In performance land, Marco Nelissen observed that...

...the BeOS [uses] a compiler that I have not once seen listed among the best, and for good reason. Performance of the BeOS on a P100 is pitiful, it feels *much* slower than Windows 95 on the same machine.

To which Dominic Giampaolo replied:

We are aware that performance isn't what it could be. That is partly due to the compiler and partly due to the fact that we haven't started tweaking out things on Intel yet. If we made you guys wait until we squeezed every last ounce of performance out of the software then you wouldn't be receiving CD's any time in the next 6 months.

Subject: B_MOUSE_MOVED, bug or feature ?

Does the system really generate only one B_MOUSE_MOVED event for each four (or so) pixels of movement, as Joerg Stegemann claims? Yes, and for the reason pointed out by many of our listeners: To avoid overflowing the message queue.

Listeners wrote in to suggest other MouseMoved() implementations and work-arounds.

THE BE LINE: As mentioned in the Be Book: "MouseMoved() should not be used to track the cursor inside a view. Use the GetMouse() function instead."

Subject: Standardized resource formats

I'm curious to know how other developers are handling the lack of resource format standards, especially as they relate to the development of an application's GUI.

Also sprach Chris Russo. Und...

I've found Be's lack of something similar [to the Mac's ResEdit] to be a bit disconcerting.

But shouldn't GUI elements be created by typing code, the way the ancients did it? Nope—archives are the way to go, according to Tyler Riti:

The advantage of using resources to build your GUI is that the compiled code will be much smaller because you're not having to build the entire interface in C++. Twenty or so function calls suddenly becomes just one function call to completely build an entire window, interface and all.

But that's not the only way it can be done,” said Dr. Peter Kittel, emerging from the shadows with a fresh drink and a helpful smile, “A shareable library with the GUI configuration routines can take this load from the programmer, so that he also has to issue just one call to get a whole GUI. If done right, he even doesn't need external resources, perhaps only for localization of his text strings.

Also,” said Marco Nelissen, agreeably, “although the *compiled code* may be smaller when using a resource, the actual size of the executable file shouldn't differ that much.

Another plus of coding it all is that your linker automatically tracks the relation between the code and the classes it uses,” said Mark-Jan, joining his compatriots in the atrium. “It throws out the ones you didn't use. An 'object-factory' links itself to all classes it can generate, bloating the code.

They adjourned to cigars and brandy in the study, where they found Kevin Hendrickson slowly rotating the large globe in the corner. “But one huge advantage of resources is the ease of customization and localization. If I want my app to have a bigger default window or a purple background, I (or anyone else) can just edit the resource. If I want my dialog box to say 'Don't do that!' in Japanese instead of (or in addition to) English, I just edit a resource. I shouldn't have to recompile for such a simple thing. This becomes increasingly important as apps get bigger and more complex with users spread around the world.

The contestants agreed, or nearly so, that localization strings could be stored in external files, which, of course, is not without its problems (multiple files—precisely the bugaboo that resources attempts to avoid).

A number of respondents wrote in to offer their own resource/localization tools.

Subject: Release 3 and SCSI on Intel

Does Release 3 support SCSI?

THE BE LINE: Sorry, but no. This will be fixed in R4 (for Adaptec cards, at least).

Subject: Raw CD read/write

A question from Jonathon Turner:

How would one do raw read and write from a CD, like for instance if it is a video CD (or a PlayStation CD or a Saturn CD).

Dominic Giampaolo's reply: “Just open() or BFile::SetTo() the path name of the CD. You can poke around in /dev/disk to find what devices are available.

Subject: rgb_color

Naveen Michaud-Agrawal thinks that rgb_color should have a constructor. Not everyone agrees. Brian Stern suggests that initializing the struct is good enough; for example...

rgb_color color = { 255, 255, 255, 255 };

But, Mr. Stern continues, the struct COULD benefit from equivalence operators.

Jason Gosnell rebuts the initialization method...

You can't declare [an initialization] on the fly as a temporary:

foo( rgb_color(255, 255, 255, 255) ); // can't do this bar( BRect(10.0, 10.0, 60.0, 60.0) ); // but w/ BRect, we can.

Also, says Mr. Michaud-Agrawal, you can't re-initialize; if you want to reuse an rgb_color struct, you have to reset each component separately. (Mr. Michaud-Agrawal then conceded that what he really wants is a SetTo() method).

Unconvinced, Mr. Stern replied...

rgb_colors are 4 bytes each. It makes more sense (to me) to allocate as many on the stack as you need rather than setting the value of a single variable back and forth to several different colors.

Jon Watte suggested that an in-line pseudo-constructor would be even better than a real constructor:

inline rgb_color rgb(uint8 r, uint8 g, uint8 b, uint8 a = 255) {
  rgb_color ret;
  ret.red = r;
  ret.green = g;
  ret.blue = b;
  ret.alpha = a;
  return ret;
}

Subject: Query-Bug

Dirk Olbertz indexed an attribute, and then wrote, to a node, a longish string as the attribute's value. A subsequent query seemed to ignore the file. Why? Dominic Giampaolo:

Indexed attributes have to be less than 256 bytes in length. It may be that this is not properly documented but this is definitely true.

Is this limit too low (ask our listeners)? If an attribute value exceeds the limit, should the first 255 bytes (at least) be indexed? (Currently, the entire attribute is tossed out of the indexed). How about storing multiple values per attribute?

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.