Writing Programs for Multifinder

If you're writing a new Mac application or revising an old one, it pays to make it MultiFinder aware


December 01, 1988
URL:http://www.drdobbs.com/database/writing-programs-for-multifinder/184408039

Figure 1

Figure 1

DEC88: WRITING PROGRAMS FOR MULTIFINDER

WRITING PROGRAMS FOR MULTIFINDER

If you're writing a new Mac application or revising an old one, it pays to make it MultiFinder aware

Chris Derossi

Chris Derossi is a member of the Apple Developer's Technical Support Team and can be reached at 20525 Mariani Ave.m MS-51T, Cupertino, CA 95014.,


MultiFinder, Apple's multitasking extensions to the Mac OS, was first introduced with System 4.2 and was later enhanced with System 6.0. Although the use of MultiFinder is now familiar to most, programmers writing for the MultiFinder environment still find themselves faced with some confusion. In this article, I'll address some of the issues relating to programming for MultiFinder and hopefully clear up much of the confusion.

Sometimes programming for the Macintosh is like trying to shoot at a moving target. As the Mac system software changes, so do the rules for writing " well-behaved" programs. This is part of the reason why programming the Mac today seems to be extra-complicated.

Prior to System 4.2 (pre-MultiFinder), each application for the Mac effectively owned the entire machine during its execution. There were guidelines then for writing programs that were considered well-behaved and compatible, but they were just that --guidelines. In the interest of getting just a little bit more functionality or a little bit more performance, these rules were sometimes bent or simply ignored.

Enter MultiFinder. Not only did MultiFinder tend to break programs that broke the rules, but also the rules themselves changed. Programs had to learn to run in a world in which everything -- such as the file system, memory, events, and the screen --was shared.

At the same time as programs were being revised to become MultiFinder compatible, a new class of programs was born. These MultiFinder-aware applications were designed to take advantage of a multitasking environment. Such programs could do background processing and deal intelligently with being suspended and resumed.

With System 6.0 some new features were added to MultiFinder. Applications being written or revised today have a different set of guidelines to follow than did programs written for MultiFinder and System 4.2. Such is the price we pay to have software that evolves with the times. Of course, the changes between System 4.2 and System 6.0 are less dramatic than the changes that went along with MultiFinder' s introduction.

One of the things that has changed is the SIZE resource. It was originally defined so that Switcher could know something about applications, such as the amount of memory they need. MultiFinder adopted the SIZE resource, ignoring the Switcher flags and defining new flags. Each flag in the SIZE resource tells MultiFinder something about how the application wants to be executed or how much the application understands.

The most recent SIZE resource definition is shown in Figure 1, page 47; the corresponding Rez type description is in Example 1, page 47.

What the Flags Mean

The bits in the flags field have been cause for some confusion for several reasons. First, it isn't always clear exactly what each flag means or what the results are of setting each one. Also, even though each bit can be set independently of the others, there is a relationship between some of the flags that may not be obvious. Finally, some of these flags have had different names at different times.

Here is a description of each flag along with the effects of setting or not setting that flag. These descriptions are in the order that you would most likely want to consider them for your application.

acceptSuspendResumeEvents - This flag tells MultiFinder whether or not your application understands and will handle suspend and resume events. If this flag is set, MultiFinder will issue your application a suspend event before you are switched into the background and a resume event once you have been switched to the foreground.

On a suspend event, MultiFinder will expect you to convert your private scrap to the Clipboard. You don't have to do anything if your private scrap hasn't changed since the last resume (or since your application was started). On a resume event, your application has to convert the Clipboard to your private scrap. You only have to do this if the Clipboard has changed while you were suspended. If the Clipboard has changed, bit 1 will be set in the resume event's message field.

If this flag is not set, then MultiFinder acts somewhat differently. Your application won't get suspend and resume events, so it won't know that it has been switched out. It also won't know when it should convert its private scrap to the Clipboard. Because of this, MultiFinder has to fool your application into converting its scrap.

Because your application would normally convert its scrap only when a desk accessory was opened, MultiFinder pretends to open a desk accessory. This is done by feeding a mouse-down event in the menu bar to the application. The application then calls MenuSelect and is passed back the item number of the About MultiFinder... item in the Apple menu. Thinking it's a DA, your application calls OpenDeskAcc on About MultiFinder... and converts its scrap.

multiFinderAware -- This flag is used only if the acceptSupendResumeEvents flag is also set. Unlike Switcher, MultiFinder keeps all application windows on the screen. This means that the front window of suspended applications needs to be deactivated to present the user with a consistent interface. If this flag is set, MultiFinder will expect the application to deactivate and activate its front window when it receives suspend and resume events. This is why this flag used to be called doOwnActivate.

Activating and deactivating windows entails highlighting/unhighlighting any selected text, showing/hiding scroll bars, calling TEActivate/TEDeactivate, and so on. These are the same actions as would be done in response to normal activate and deactivate events.

If this flag is not set, then MultiFinder must fool your application into activating and deactivating its front window. MultiFinder does this by feeding your application the appropriate activate or deactivate event.

Most straightforward applications written today should have both the acceptSuspendResumeEvent; and multiFinderAware flags set. These two flags basically determine whether the responsibility for handling suspension and resumption rests with the application or with MultiFinder. Remember that if these flags are not set, MultiFinder has to fool your application into converting the scrap and activating/deactivating its front window.

Your application is doing the real work anyway. The difference is that if MultiFinder has to trick your application, several extra steps are involved. This takes time. if your application handles everything with a single suspend or resume event, switching between applications is much faster.

canBackground -- if this flag is set, MultiFinder will give null events to your application while it is in the background. The frequency and number of null events you receive is dependent on the sleep value that you pass to WaitNextEvent and the amount of time given up by the foreground application.

MultiFinder's first priority is the foreground application's responsiveness. Because some older applications relied on getting frequent null events, if the application in the foreground is calling GetNextEvent instead of WaitNextEvent, it will get many of the null events. On she other hand, if the foreground application is polite and calls WaitNextEvent with a high sleep value, background applications will get plenty of time.

You should set this bit only if you really have something to do in the background. if your application doesn't do anything when it gets a null event (or if it just changes the cursor, for example), then don't set this bit. Asking for null events when your application doesn't use them steals time needlessly from other applications.

Figure 1: The SIZE resource definition

Example 1: The corresponding Rez type description

   type 'SIZE'  {

     boolean   dontSaveScreen           /* No longer used */
               saveScreen;

     boolean   ignoreSuspendResumeEvents,
               acceptSuspendResumeEvents;

     boolean   enableoptionSwitch,      /* No longer used */
               disableoptionSwitch;

     boolean   cannotBackground,
               canBackground;

     boolean   notMultiFinderAware,
               multiFinderAware;

     boolean   backgroundAndForeground,
               onlyBackground;

     boolean   dontGetFrontClicks,
               getFrontClicks;

     unsigned bitstring[9] = 0;         /* reserved    */
          /* Memory sizes are in bytes */
     unsigned longint;                  /* preferred mem size */
     unsigned longint;                  /* minimum mem size */
   };

getFrontClicks -- If the user brings your application to the front by clicking on one of its windows, the mousedown event is normally used to resume your application only. If this bit is set, though, your application will get the mouse-down (and corresponding mouse-up) event that brought you to the foreground. This can be used to provide better responsiveness to the user.

For example, the Finder accepts these mouse-down events so you can click on an icon when the Finder is in the background. The Finder will come to the foreground and select the icon immediately, without your having to click on the icon a second time.

This type of action is not appropriate for all kinds of applications, however. If a paint program had this bit set for example, just the act of bringing the application to the front might result in a dot being painted onto the document. The key here is to do what the user expects so that the user never realizes there are two alternatives.

onlyBackground -- If this bit and the canBackground bit are both set, your application will be launched directly into the background. It cannot come to the foreground because it can't have any windows, its name will not show up in the Apple menu, and its icon will not be available in the menu bar.

Most applications will not have this bit set. Only if you are writing an unusual program should you have to deal with this flag. An example of the kind of application that uses this feature is Backgrounder. Backgrounder is launched by MultiFinder and stays in the background. Its sole job is to look for the existence of a spool file and launch PrintMonitor.

MultiFinder Tricks

In addition to tricking your application into converting its scrap and activating/ deactivating its front window, there are two other times when MultiFinder will fool your application into doing something. One of these happens when the user chooses Restart or Shutdown from the Finder's Special menu. MultiFinder goes through all the currently executing applications and makes them think that the user has chosen Quit. This causes each application to terminate, asking the user to save documents as appropriate for each application.

The other instance of MultiFinder tricking the application happens when an application is already running and the user launches a document for that application from the Finder. MultiFinder switches to that application and posts a mouse-down in the menu bar, selecting the O.n... item from the File menu. Then, when the application calls SFGetFile in response to the Open..., MultiFinder simply returns a reply for the launched document.

This could fail for a couple of reasons. If your application doesn't call SFGetFile or SFPGetFile in response to the Open..., the user will get your application but the document won't have opened. If for some reason a document cannot be opened, your application should gray the Open... item (for example, you support only one document at a time and one is already open).

A far more common reason why MultiFinder's quit or open tricks would fail is nonstandard Quit or Open ... menu items. MultiFinder looks for a menu with the title "File" and items named Quit for quitting and Open (with ellipses), ... (with three periods), ..., or Open Stack for opening documents. If these are not present, the user will have to quit or open documents manually from the application.

If you have nonstandard menus m your application that perform the same action as the standard ones, there is a way to tell MultiFinder what your menu items are called. MultiFinder looks for certain resources in your program that contain the names of your Quit and Open menu items. These resources are of type 'mstr' and are equivalent to the standard 'STR' resource type. Because your Quit and Open items may be in different menus, each of them have a 'mstr' resource for their menu title and a 'mstr' resource for their item name. MultiFinder expects the menu title for the Quit item to be in 'mstr' #100 and the menu item to be in 'mstr' #101. Similarly, the 'mstr' resources for the Open item are #102 and #103.

Figure 2: A nonstandard File menu

     File
     New
     Open Document
     _____________
     Save
     Save As...
     Print
     _____________
     Quit MyProgram

Figure 2, this page, shows a nonstandard File menu, Example 2, this page, shows the Rez source for the corresponding 'mstr' resources.

Example 2

     resource 'mstr' (100) ("File");

     resource 'mstr' (101) ("Quit MyProgram");

     resource 'mstr' (102) ("File");

     resource 'mstr' (103) ("Open Document");

If, for some reason, your application uses multiple strings for the Quit or Open menu items, then you can list all the possibilities using 'mst#' resources instead of 'mstr' resources. The 'mst#, resource type is the same as the 'STR#' resource type, which contains a list of strings. Example 3, this page, shows the Rezsource using 'mst#' resources.

Example 3

resource 'mst#' (101) {
   {
     "File";
     "Files"
   }
 };

resource 'mst#' (102) {
   {
     "Quit MyProgram";
     "Quit to Finder"
   }
 };

Background Only Applications

Historically, programs that needed to do some work periodically but that didn't have any user interaction would have to be written as drivers that had the dNeedsTime flag set. Examples of this kind of program are daemons, servers, and spoolers. But drivers have to be installed and can't have globals or multiple segments, so writing them could be cumbersome and difficult. MultiFinder now provides a better alternative.

By setting both the canBackground and onlyBackground bits in the SIZE resource, you can write an application that launches directly into the background and never comes to the front. This type of program is ideal for simple, periodic, background tasks. And backgroundonly applications are easier to write than normal applications because they can't have any real user interface. This means that there are no windows, menus, controls, or dialogs to worry about. It also means that the only event that has to be dealt with is the null event; no user events are ever posted to this kind of application.

Plus these programs get all the advantages of being complete applications. Because they run normally and not at interrupt time, they can allocate memory and use handles. They have their own application partition, so they can have globals and multiple segments. And because they're not drivers, they don't have to be installed in the unit table; they can have icons and be launched just like any other application.

A background-only application doesn't have a layer of its own because it doesn't have any windows. Even if it tried to put up a window, dialog, or alert, it wouldn't succeed. But there is a way that such a program can communicate with the user if it needs to. This is one of the responsibilities of the Notification Manager.

Notification Manager

The Notification Manager allows a background application to display an alert, make a sound, or put an icon in the menu bar. In this rudimentary way, a backgroundonly application can alert the user to error conditions, changes in the environment, or tasks completed.

Listing One, page 96, shows complete MPW Pascal source code for a background-only application. Listing Two, page 98, is the MPW C version, and Listing Three, page 100, is the Rez source for both versions. The main job that this application performs is comparing the current time to a specific time stored in the resource fork of the application. When the set time has been reached, an alert is displayed for the user. This program could be the beginning of a more general alarmclock or appointment-reminder utility.

The flow of this sample program is simple. The first thing it does is initialize its world. Notice that the familiar Macintosh initialization calls (InitGraf, InitFonts, InitWindows, and so on) are missing. Because this program is not going to use any of the user interface managers, it doesn't have to initialize them. The init code sets two global flags to false and loads the alarm time and the text for the alert from the resource fork.

The main event loop is almost nonexistent because there are no events to handle. Instead, WaitNextEvent is used just to regulate background time. A large sleep value is passed to WaitNextEvent because this program doesn't need to get time very often and can afford to be polite. As soon as the current time (in minutes) has reached or passed the alarm time, the user needs to be notified.

While the alert is being displayed, the application will continue to get time in the background. For this reason, it has to be careful not to notify the user more than once. The first thing that the Telluser procedure does is check a global Boolean variable to see if the pert has already been requested. If the global toldUseris false, the Notification Manager is used to display an alert.

The Notification Manager queue element variable, myNMBlock, has to continue to exist after the Telluser procedure terminates, so it's a global variable. This is a background-only program that doesn't have an icon in the Apple menu, so the nmMark field is meaningless. For simplicity, only an alert is requested, and neither a sound -r an icon in the menu bar is used. Because the program needs to know when the alert has been dismissed, the address of a completion routine is gassed in the nmResp field. After the fields have been filled in, NMInstall is called to initiate the notification.

At this point, the Notification Manager puts up an alert. This alert will come up on top of just about anything, including modal dialogs or other alerts that may currently be up.

As soon as the user dismisses the dialog, the completion routine, Alertdone, is called. Because this can happen at any time, the A5 world may belong to any running application when he completion routine is called. For his reason, the completion routine can't directly access any global variables or make intersegment calls.

The completion routine needs to do two things: It has to remove the notification element from the queue with NMRemove, and it has to set the global variable doneFlag to true so the application will terminate. Because of the restrictions on accessing global variables, a pointer directly to the doneFlag variable was saved in the nmRefCon field of the notification queue element. By using this pointer, the completion routine sets doneFlag to true.

The next time the program gets background time, it quietly terminates. A more comprehensive program might start looking for the next alarm time, or it might reset some flags and start waiting for the same alarm time the next day.

In Summary

If you are writing an application today, or revising an old application, it pays to make it MultiFinder aware. Very little extra code needs to be added, and the benefit of better speed during context switching is well worth it. Similarly, the inclusion of a SIZE resource, and 'mstr' resources if appropriate, will help ensure that MultiFinder works well with your program.

Additionally, many programs will be able to take advantage of processing in the background. Background processing may be the edge your program needs to let your users get the most out of their Macs. Finally, the ability to have background-only applications opens the door for a whole new set of powerful spoolers, servers, and other background tasks.

Bibliography

Programmer's Guide to MultiFinder. APDA product #KMB017. Renton, Wash.: APDA.

MultiFinder Miscellanea. Macintosh Technical Note #180. Renton, Wash.: APDA.

Notification Manager. Macintosh Technical Note #184. Renton, Wash.: APDA.

MultiFinder Revisited. Macintosh Technical Note #205. Renton, Wash.: APDA.

[LISTING ONE]




THIS LISTING IS CURRENTLY UNAVAILABLE



[LISTING TWO]


THIS LISTING IS CURRENTLY UNAVAILABLE



[LISTING THREE]


THIS LISTING IS CURRENTLY UNAVAILABLE








Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.