From [email protected] Sat Sep 11 18:54:18 1993
Path: news.itd.umich.edu!destroyer!sol.ctr.columbia.edu!spool.mu.edu!olivea!apple.com!claris!outpost.SF-Bay.org!peirce
From: [email protected] (Michael Peirce)
Newsgroups: comp.sys.mac.programmer
Subject: "System" menu code
Message-ID: <[email protected]>
Date: 11 Sep 93 19:56:12 GMT
Reply-To: [email protected] (Michael Peirce)
Organization: Peirce Software
Lines: 487
X-Mailer: uAccess - Macintosh Release: 1.6v2

Earlier this week I asked about how to add a "system" menu to the menu bar, one that
will stick around all the time.  I got a number of very helpful responses.  Here is
a bare bones implementation I worked up with real code.

I though I'd post it for two reasons (one altruistic, one selfish).
(1) This info isn't well documented and by posting it, people can see how it's done.
(2) Others can point out flaws in my approach.

A couple of notes:  This is written in MPW C and ASM.  It requires System 7 (and doesn't
check - it's only example code :-).  It uses a static menu - if you want a changing menu,
you have to do more work messing with SelectMenu; that's why I have the empty patch to
SelectMenu in there.  And finally, it uses a fixed menu id. I'm told this is evil and
that I should walk the menu list and make sure I use an unused id.

Alsothus uses Icons.h, you can get this from the latest E.T.O.

Thanks for the pointers!

-- michael

---------- StarMenuINIT.c ---------------------------------------

/*
       StarMenuINIT.c

       Copyright )1993 Peirce Software.
       All rights reserved

       Change History:

               09-11-93        Michael Peirce          Add this.

*/

#include "Icons.h"
#include "StarMenu.h"
#include "Traps.h"

#pragma segment Main

pascal OSErr DetachIcons(ResType theType, Handle *theIcon, void *yourDataPtr);

//
//      This is our INIT code.  It does all the setup and installation required to patch
//      our traps and setup the storage for doing what we want to do.
//
//      NOTE: Remove the DebugStr() stuff in production code.
//
pascal void STARMENUENTRY()
{
       Handle  patchCode;
       long            patchDrawMenuBar,patchSystemMenu,patchMenuSelect;
       long            oldDrawMenuBar,oldSystemMenu,oldMenuSelect;
       long            storageAddr;
       STORAGEH        storage;
       OSErr           stat;
       Handle          iconSuite;


       // Install resident code resource
       SetZone(SystemZone());
       patchCode = Get1Resource('rCod',1);
       if (!patchCode) {
               DebugStr("\pNo patch code found");
               return;
       }

       DetachResource(patchCode);
       HNoPurge(patchCode);
       HLock(patchCode);


       // Setup the vectors
       //      *** Be VERY careful this matches the layout of StarMenuPatch.a.
       patchDrawMenuBar        = ((long)*patchCode)    + 0;
       patchSystemMenu         = patchDrawMenuBar              + 2;
       patchMenuSelect         = patchSystemMenu               + 2;

       oldDrawMenuBar          = patchMenuSelect               + 4;
       oldSystemMenu           = oldDrawMenuBar                + 6;
       oldMenuSelect           = oldSystemMenu                 + 6;

       storageAddr                     = oldMenuSelect                 + 4;


       // allocate some storage and store a reference to it into our block
       storage = (STORAGEH) NewHandle(sizeof(STORAGERec));
       if (storage == nil)
               return;
       *((long*) storageAddr)  = (long) storage;


       // load up our icon suite for the menu "title"
       stat = GetIconSuite(&iconSuite,128,svAllAvailableData);
       if (stat != noErr)
               DebugStr("\p Error in NewIconSuite");
       stat = ForEachIconDo(iconSuite,svAllAvailableData,DetachIcons,0);
       if (stat != noErr)
               DebugStr("\p Error in ForEachIconDo");
       _MenuIconFamily = iconSuite;

       _OurMenuHandle = 0;
       // We should make this dynamic, so as not to conflict, but for now...
       // Must be in the range < -16384
       _MenuID = -19061;



       // Patch the traps
       *((long*) oldDrawMenuBar) = NGetTrapAddress(_DrawMenuBar, ToolTrap);
       NSetTrapAddress(patchDrawMenuBar,_DrawMenuBar,ToolTrap);

       *((long*) oldSystemMenu) = NGetTrapAddress(_SystemMenu, ToolTrap);
       NSetTrapAddress(patchSystemMenu,_SystemMenu,ToolTrap);

       *((long*) oldMenuSelect) = NGetTrapAddress(_MenuSelect, ToolTrap);
       NSetTrapAddress(patchMenuSelect,_MenuSelect,ToolTrap);



       SetZone(ApplicZone());
}

//
//      DetachIcons - is called to detach each icon resource from its file and make
//              it into a regular handle so it will stick around after INIT time.
//
pascal OSErr DetachIcons(ResType theType, Handle *theIcon, void *yourDataPtr) {
#pragma unused(theType)
#pragma unused(yourDataPtr)

       if (*theIcon != nil) {
               DetachResource(*theIcon);
               HNoPurge(*theIcon);
               return ResError();
       } else {
               return noErr;
       }
}
---------- StarMenuPatch.a ---------------------------------------

;
;       StarMenuPatch.a
;
;       Copyright )1993 Apple Computer, Inc.
;       All rights reserved
;
;       Change History:
;
;               09-11-93        Michael Peirce          Set us this header
;
;       This code must be the first code in the segment.  The first few lines are
;       our patch vectors.  They are kept there so we can easily calculate their
;       location as we load them in.
;
;       After the patch vectors and storage space, we have our actual patches.  These
;       only do as much as we need in assembler, then call C routines to do the real work.
;
                       BLANKS          ON

                       INCLUDE         '::AIncludes:SysEqu.a'
                       INCLUDE         '::AIncludes:SysErr.a'
                       INCLUDE         '::AIncludes:Traps.a'

                       IMPORT          CDRAWMENUBAR
                       IMPORT          CSYSTEMMENU
                       IMPORT          CMENUSELECT

;------------------------------------------------------------------------------------

                       STRING          ASIS


PATCHES         PROC            EXPORT

                       BRA.S           @ADRAWMENUBAR           ; Entry for DrawMenuBar patch
                       BRA.S           @ASYSTEMMENU            ; Entry for SystemMenu patch
                       BRA.S           @AMENUSELECT            ; Entry for MenuSelect patch
@oldDMB         DC.W            $4EF9                           ; JMP.L
                       DC.L            0                                       ; Original address of DrawMenuBar
@oldSM          DC.W            $4EF9                           ; JMP.L
                       DC.L            0                                       ; Original address of SystemMenu
@oldMS          DC.W            $4EF9                           ; JMP.L
                       DC.L            0                                       ; Original address of MenuSelect

@STORAGEH       DC.L            0                                       ; This is were we stash our storage
                       NOP
                       NOP                                                             ; Lets Jasik display things better

;------------------------------------------------------------------------------------
;       Our DrawMenuBar patch
;
@ADRAWMENUBAR

                       MOVE.L          @STORAGEH,A0            ; get handle to our storage
                       MOVE.L          A0,-(A7)                        ; pass it on the stack
                       JSR                     CDRAWMENUBAR            ; call our routine

                       BRA.S           @oldDMB                         ; go to original code

;------------------------------------------------------------------------------------
;       Our DrawMenuBar patch
;
@ASYSTEMMENU

                       LINK            A6,#0
                       MOVE.L          8(A6),-(A7)                     ; put the SystemMenu paramter on the stack
                       MOVE.L          @STORAGEH,A0            ; get handle to our storage
                       MOVE.L          A0,-(A7)                        ; pass it on the stack
                       JSR                     CSYSTEMMENU                     ; call our routine
                       UNLK            A6

                       BRA.S           @oldSM                          ; go to original code

;------------------------------------------------------------------------------------
;       Our DrawMenuBar patch
;
@AMENUSELECT

                       BRA.S           @oldMS                          ; go to original code

                       END
---------- StarMenuPatch.c ---------------------------------------

/*
       StarMenuPatch.c

       Copyright )1993 Peirce Software.
       All rights reserved

       Change History:

               09-11-93        Michael Peirce          Add this.

*/

#include <Memory.h>
#include <Traps.h>
#include "Icons.h"
#include <SysEqu.h>
#include "StarMenu.h"

#pragma segment Main

//
//      CDRAWMENUBAR - The C routine that is our patch to DrawMenuBar.  It is called
//              by our assembly language patch.  We can do all our work here and have all
//              the ease of using a "high level language" instead of assembler.
//
//              In this routine we always insert our menu into the bar.  The first time
//              through, we construct the menu since we couldn't at INIT time (the menu
//              manager might not have been intialized).
//
pascal void CDRAWMENUBAR(STORAGEH storage)
{
       MenuHandle      ourMenu;

       ourMenu = _OurMenuHandle;


       if (ourMenu == nil) { // initialize our menu
               THz             currentHeapZone;
               char    newMenuName[] = "\p12345";

               currentHeapZone = GetZone();
               SetZone(SystemZone());

               newMenuName[1] = 1;
               BlockMove(&_MenuIconFamily,&newMenuName[2],4);
               ourMenu = NewMenu(_MenuID,newMenuName);
               AppendMenu(ourMenu,"\pBeep Once");
               AppendMenu(ourMenu,"\pBeep Twice");
               AppendMenu(ourMenu,"\pBeep Thrice");
               AppendMenu(ourMenu,"\pBeep Four Times");

               _OurMenuHandle = ourMenu;

               SetZone(currentHeapZone);
       }

       InsertMenu(ourMenu,0);
}

//
//      CSYSTEMMENU - The C routine that is our patch to SystemMenu  It is called
//              by our assembly language patch.  We can do all our work here and have all
//              the ease of using a "high level language" instead of assembler.
//
//              In this routine, we check for a hot on our menu.  If so we handle it.
//
pascal void CSYSTEMMENU(short menuItem, short menuID, STORAGEH storage)
{
#pragma unused(storage)

       short   i;

       if (menuID == _MenuID) {
               for (i=0;i<menuItem;i++)
                       SysBeep(5);
       }
       HiliteMenu(0);
}

pascal void CMENUSELECT(STORAGEH storage)
{
#pragma unused(storage)

}
---------- StarMenu.h ---------------------------------------
/*
       StarMenu.h

       Copyright )1993 Peirce Software.
       All rights reserved

       Change History:

               09-11-93        Michael Peirce          Add this.

*/
#include <Types.h>
#include <Menus.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <Dialogs.h>
#include <Events.h>
#include <Devices.h>
#include <Packages.h>
#include <Fonts.h>
#include <GestaltEqu.h>
#include <Folders.h>
#include <Resources.h>
#include <ToolUtils.h>


//
//      STORAGERec is where we stash all our global variables used
//              throughout the various patches that make up this extension.
//
typedef struct STORAGERec
{
       MenuHandle              ourMenuHandle;
       Handle                  menuIconFamily;
       short                   menuID;
} STORAGERec, *STORAGEPtr, **STORAGEH;

//
//      Easy access macros for accessing our globals through the handle.
//
#define _OurMenuHandle          ((**storage).ourMenuHandle)
#define _MenuIconFamily         ((**storage).menuIconFamily)
#define _MenuID                         ((**storage).menuID)
---------- StarMenuPatch.r ---------------------------------------

/*
       StarMenuPatch.r

       Copyright )1993 Peirce Software.
       All rights reserved

       Change History:

               09-11-93        Michael Peirce          Add this.

*/

#include "types.r"

resource 'ics4' (128) {
       $"0000 FFFF FFFF 0000 000F FE88 88EF F000"
       $"00AF 8888 8888 FF00 0AF8 AA88 88AA 8FF0"
       $"FF8A FFA8 8AFF A8FF FE8A FFA8 8AFF A8EF"
       $"F888 AA88 88AA 888F F888 8888 8888 888F"
       $"F88E FFFF FFFF E88F F88F DDDD DDDD F88F"
       $"FE8F DDDD DDDD F8EF FF8E FFFF FFFF E8FF"
       $"0FF8 8888 8888 8FF0 00FF 8888 8888 FF00"
       $"000F FE88 88EF F000 0000 FFFF FFFF"
};

resource 'ics#' (128) {
       {       /* array: 2 elements */
               /* [1] */
               $"0FF0 1818 300C 6C36 D24B 9249 8C31 8001"
               $"8FF1 9009 9009 CFF3 6006 300C 1818 0FF0",
               /* [2] */
               $"0FF0 1FF8 3FFC 7FFE FFFF FFFF FFFF FFFF"
               $"FFFF FFFF FFFF FFFF 7FFE 3FFC 1FF8 0FF0"
       }
};

resource 'ics8' (128) {
       $"0000 0000 FFFF FFFF FFFF FFFF 0000 0000"
       $"0000 00FF FFA5 E3E3 E3E3 A5FF FF00 0000"
       $"0000 FDFF E3E3 E3E3 E3E3 E3E3 FFFF 0000"
       $"00FD FFE3 FDFD E3E3 E3E3 FDFD E3FF FF00"
       $"FFFF E3FD FFFF FDE3 E3FD FFFF FDE3 FFFF"
       $"FFA5 E3FD FFFF FDE3 E3FD FFFF FDE3 A5FF"
       $"FFE3 E3E3 FDFD E3E3 E3E3 FDFD E3E3 E3FF"
       $"FFE3 E3E3 E3E3 E3E3 E3E3 E3E3 E3E3 E3FF"
       $"FFE3 E3A5 FFFF FFFF FFFF FFFF A5E3 E3FF"
       $"FFE3 E3FF 3333 3333 3333 3333 FFE3 E3FF"
       $"FFA5 E3FF 3333 3333 3333 3333 FFE3 A5FF"
       $"FFFF E3A5 FFFF FFFF FFFF FFFF A5E3 FFFF"
       $"00FF FFE3 E3E3 E3E3 E3E3 E3E3 E3FF FF00"
       $"0000 FFFF E3E3 E3E3 E3E3 E3E3 FFFF 0000"
       $"0000 00FF FFA5 E3E3 E3E3 A5FF FF00 0000"
       $"0000 0000 FFFF FFFF FFFF FFFF"
};

data 'sysz' (0) {
       $"0000 5000"                                          /* ..P. */
};
---------- StarMenu.make ---------------------------------------

#
#       StarMenu.make
#
#       Copyright )1993 Peirce Software.
#       All rights reserved
#
#       Change History:
#
#               09-11-93        Michael Peirce          Add this.
#
# make -f StarMenu.make > StarMenu.make.out; StarMenu.make.out; Beep

COptions = -d MPW3 -r -sym full -b2 -mbg ch8    # define MPW3, turn on strict prototyping (-r option)

ObjDir                  =       ":Objects:"


InitObjs                =       {ObjDir}StarMenuInit.c.o 6
                                       {ObjDir}StarMenuPatch.c.o 6
                                       "{Libraries}"Interface.o


InitpObjs               =       {ObjDir}StarMenuPatch.a.o 6
                                       {ObjDir}StarMenuPatch.c.o 6
                                       "{Libraries}"Interface.o




StarMenu                ff      {InitObjs} StarMenu.make
               echo '#.# Linking StarMenu INIT'
               Link -o {Targ} -t INIT -c STAR -rt INIT=0 -m STARMENUENTRY {InitObjs} && 6
                       Setfile StarMenu -a B && 6
                       Duplicate -y StarMenu "{SystemFolder}Extensions:"


StarMenu                ff      {InitpObjs} StarMenu.make
               echo '#.# Linking StarMenu Patches'
               Link -o {Targ} -t INIT -c STAR -rt rCod=1 -m PATCHES {InitpObjs} && 6
                       Setfile StarMenu -a B && 6
                       Duplicate -y StarMenu "{SystemFolder}Extensions:"


StarMenu                ff      StarMenu.r StarMenu.make
               echo '#.# Rezzing StarMenu.r'
               Rez -o {Targ} StarMenu.r -t INIT -c STAR -rd -append && 6
                       Setfile StarMenu -a B && 6
                       Duplicate -y StarMenu "{SystemFolder}Extensions:"


{ObjDir}StarMenuPatch.c.o       f       StarMenuPatch.c StarMenu.h StarMenu.h
               echo '#.# Compiling StarMenuPatch.c'
               c {COptions} StarMenuPatch.c -o {ObjDir}StarMenuPatch.c.o


{ObjDir}StarMenuInit.c.o        f       StarMenuInit.c  StarMenu.h
               echo '#.# Compiling StarMenuInit.c'
               c {COptions} StarMenuInit.c -o {ObjDir}StarMenuInit.c.o


{ObjDir}StarMenuPatch.a.o       f       StarMenuPatch.a  StarMenu.h
               echo '#.# Assembling StarMenuPatch.a'
               asm  StarMenuPatch.a -o {ObjDir}StarMenuPatch.a.o

---------- end ---------------------------------------


--
--  Michael Peirce      --   [email protected]
--  Peirce Software     --   Suite 301, 719 Hibiscus Place
--                      --   San Jose, California USA 95117
--  Makers of:          --   voice: +1.408.244.6554 fax: +1.408.244.6882
--             Smoothie --   AppleLink: peirce & America Online: AFC Peirce