From peirce@outpost.SF-Bay.org 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: peirce@outpost.SF-Bay.org (Michael Peirce) Newsgroups: comp.sys.mac.programmer Subject: "System" menu code Message-ID: Date: 11 Sep 93 19:56:12 GMT Reply-To: peirce@outpost.SF-Bay.org (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 #include #include "Icons.h" #include #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 #include #include #include #include #include #include #include #include #include #include #include #include // // 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 -- peirce@outpost.sf-bay.org -- 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