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: <D2150035.berar3@outpost.SF-Bay.org>
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 <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      --   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