FKEY for FKEYs
Volume Number: | | 4
|
Issue Number: | | 6
|
Column Tag: | | Assembly Language Lab
|
FKEY that runs other FKEYs!
By John Holder, Contributing Editor
[John Holder is a previous contributor to MacTutor and is well known for his numerous FKEY shareware products. In this article, John shows us how to create an FKEY that runs other FKEYS without installing them in the system folder! Hence this FKEY has an event loop, menu bar and all the other goodies normally found in a regular application. The source code disk for this month also includes two of his more popular FKEYS to use as launch samples, along with a shareware application so you can contact John about other goodies he might have for you. -Ed]
Whats an FKEY?
FKEYs (Function Keys) are resources containing executable code that are called upon by hitting the keys Command-Shift-# (any number 0-9) at the same time. In the standard System file there are two FKEYs; with an ID#3 and #4 that are used to dump the current screen or window onto disk or printer.
There are also many other FKEYs out in the Mac world, some Public Domain or Shareware, and others are sold commercially. They can be installed into the System file by using ResEdit or Fkey Installer (this application was created by Dreams of the Phoenix, Inc and released into the public domain to encourage people to make Fkeys). The Fkey presented in this article can be used to access other Fkeys which are not in the System file!
Whats this all about?
This article will show you how to create an FKEY with an interface similar to a standard applications (menus with command key equivalents, windows, and an event loop). It does not support DAs nor does it have an EDIT menu, but if you need to add this function to your own Fkey it would be very simple.
This Fkey was created with the Macintosh 68000 Development System. All applications are on a disk titled MDS1 and all .D files are on a disk titled MDS2. First assemble the file FkeyTemp.Asm, then run the linker on the file FkeyTemp.Link, and finally run RMaker on the file FkeyTemp.R. You will now have an FKEY ready to put into your System file!
Fig. 1 Our FKEY launches other fkeys!
Fkey Sampler, How it Works
First, we create an offset table that is used to store the FKEYs many global variables. This is done by naming the offsets that it will need such as sfReply, WindPtr, etc., and calculating how many bytes each will take (explained under How to set up the offset Table).
When the Fkey starts, there is a short jump over its header, then the applications current port and all registers are saved, next it brings up the standard arrow cursor with _InitCursor. We now check the global variable MenuList to see if the application which is currently running has a menu, if it doesnt have one (like the MiniFinder) then we cant let the Fkey run! If it does have a menu, we try to allocate enough space for the Fkeys global variables by making a non-relocatable block with _NewPtr. If there is not enough memory for the globals (there has been an error if _NewPtr returns a zero in register A0) it beeps the speaker and returns to the Application. Otherwise, a pointer to the storage area is put into register A4.
If the storage was allocated, we go on and get a handle to a copy of the applications menu bar with _GetMenuBar, and clear the menu so that we may replace it with our own. After the Fkeys menus are created with _NewMenu, _AppendMenu, and _InsertMenu, we create a window that will simulate the standard desktop. Creating a new window generates an update event, so when it gets to the main event loop, the window will be filled with the current desktop pattern.
The Main Event
Now its off to the main loop to look for events. This Fkey uses only keydown, mousedown & update events. Upon a useable event we jump to the appropriate routine to handle it, if its an update event the background window is filled with the current desktop pattern (found in the system global variable DeskPattern). Other events are handled just like a standard application by using a jump table to jump to the appropriate routines.
Doing the About...
When the mouse is clicked in the About Fkey Sampler... menu item under the Apple menu, a window is created and set as the current port. Then the windows text font and size is set to 9 point Monaco with _TextFont and _TextSize. Now the stack is set up for _TextBox (a very handy routine that displays text in a given rectangle with left, right or center justification!) Then we jump to a routine that uses _GetOSEvent to watch for a mousedown or key down event. When a key or the mouse button has been clicked we dispose of the window, unhighlight the menu and return to the main loop to get the next event!
Fig. 2 A useful FKEY running under our FKEY
Opening the Fkey
The purpose of this particular Fkey is to open and use other FKEYs (of Type FKEY) that are not in the System file. When this routine is called, the standard get file dialog is displayed with _Pack3. If a name is chosen, the volume the file is on is set as the current volume with _SetVol. Now we try to open the resource fork of the file (_OpenResFile will return a #-1 if it cant open a files resource fork). If the resource file opened with no problems we want to get the Fkey resource which is inside this file and execute it (run it, not kill it!).
Now we want to get the first Fkey resource in memory by using _GetIndResource (the newly opened res file is searched first). We check to make sure the returned Fkey is from the newly opened resource file by using _HomeResFile (when given a handle to a resource, this routine returns its resource files reference number). If it is from the Fkey file just opened, the Fkey resource is locked in memory (we dont want the Fkey moving!), the handle is made into a pointer and the Fkey is executed by jumping to it (with JSR). When that Fkey is done, we close the resource file and return for the next event.
Doing the Quit!
When an Fkey quits it must dispose of any memory it created. The first thing dumped is the window used as the desktop, then the menus it made are released from memory with _DisposMenu and the menu bar is cleared. Now the applications menu bar is restored, the memory we allocated for the globals is released, all events are flushed, the applications current port and registers are restored, and a return from subroutine RTS returns command to the application!
Fig. 3 Map for how the FKEY is constructed Use ResEdit to copy the FKEY into the system file
Making your own FKEY
All Fkeys must start with the FKEY header shown in the code listing. The only thing you will need to change in the header for your own Fkey is the ID number. You can use this Fkey to test all of your own Fkeys without installing them into the System file!
To make your own Fkey using this one as a template should be very simple. Just make your own offset table, or use this one and add your own needed offsets (explained below), then add your own menus and the appropriate routines to handle them. Also, change the contents of the text between MyTextBegin and MyTextEnd to whatever you want for your Fkeys About.. window.
A complete jump table for events and a jump table for where the mouse was clicked (returned by _FindWindow) is included to help in making your own Fkeys. Just remember to always check if there is enough memory before loading a resource or creating storage and to dispose of any storage that the Fkey created before returning control to the application!
How to set up the Offset Table
Lets say you need room for one handle (4 bytes) and two separate words (2 bytes each) of storage.
For the this storage you would need to put #8 into register D0 before calling _NewPtr (8 bytes are needed)
;Your Offset table (example)
AHandle equ 0 ;4 bytes
AWord equ 4 ;2 bytes
NextWordequ 6 ;2 bytes
Register A4 points to your global storage area . To store a word into the global AWord just use:
move #99, AWord(A4)
To put the value contained in AWord(A4) into Register D2 , use:
move AWord(A4), D2
Thats all folks...
Well, thats it, I hope youve learned something, and I hope to see lots of new Fkeys around!
;File: FkeyTemp.Asm
;------------------------------------------------
;An Fkey sampler Fkey
;By John Holder 2/8/87
;Revised 3/29/88
;------------------------------------------------
IncludeTraps.D
IncludeToolequ.D
IncludeSysequ.D
;------------------------------------------------
;Macros to save & restore the registers
;------------------------------------------------
MACRO SaveRegs =
movem.lA0-A4/D0-D7,-(SP)
|
MACRO RestoreRegs =
movem.l(SP)+,A0-A4/D0-D7
|
;------------------------------------------------
;All equates go here
;------------------------------------------------
;these are offsets into the global storage.
;(a non-relocatable block we created pointed to by A4)
;------------------------------------------------
sfReply equ 0 ;sfreply record
GetVRef equ 6
GetVers equ 8
GFileName equ 10
IOParamBlkequ 76;IOparamblock (80 bytes)
WindPtr equ 156 ;for our background ;window
EventBlkequ 160 ;Event Block
what equ 160 ;Event number
message equ 162 ;Additional information
when equ 166 ;Time event was posted
where equ 170 ;Mouse coordinates
modify equ 174 ;State of keys and button
wWindow equ 176 ;Window pointer
Menu1Hndl equ 180 ;handle to our Apple menu
Menu2Hndl equ 184 ;handle to our File menu
oldMenuBarequ 188 ;handle to applications ;menu
TheWindow equ 192 ;for _FindWindow
;event masks for _GetOSEvent
mDownMask equ 2
keyDownMask equ 8
;Menu equates
AppleMenu equ 1 ;Apple Menu
AboutItem equ 1
FileMenuequ 2 ;File Menu
OpenFkeyItemequ 1
QuitItemequ 3
;------------------------------------------------
;All FKEYs must have this FKEY resource header!
;------------------------------------------------
bra.s StartIt ;branch over header
dc0 ;flags (not used)
dc.b FKEY ;resource type
dc9 ;Resource ID #
dc0 ;version # (not used)
;------------------------------------------------
StartIt
SaveRegs ;save regs MACRO
subq #4,SP
move.l SP,-(SP)
_GetPort ;save Port for later
_InitCursor;standard arrow cursor
;does application have a menu??? if not, then quit!
move.l MenuList,D0
beq NoMenuJump ;no menu, beep & quit!
move.l #200,D0 ;200 bytes of storage
_NewPtr,CLEAR ;for our globals
move.l A0,A4 ;pointer in A4
cmp.l #0,A4
bne.s StorageOk;if not zero, ptr is ok!
NoMenuJump
bsr Mem_Error;otherwise beep
bra No_Mem ;and quit
StorageOk
bsr SetUpMenus ;set up our menus
bsr SetUpWindow;set up background ;window
;------------------------------------------------
MainLoop;standard event loop
_SystemTask
clr -(SP)
move #$0FFF,-(SP) ;event mask
pea EventBlk(A4) ;Place to return results
_GetNextEvent ;Look for an event
move (SP)+,D2
beq MainLoop ;zero if we shouldnt
move What(A4),D0;respond
add D0,D0
move EventTable(D0),D0
jmp EventTable(D0) ;jump to appropriate ;routine
EventTable
dcMainLoop-EventTable ;null event
dcMouseDown-EventTable ;mouse down
dcMainLoop-EventTable ;mouse up
dcKeyDown-EventTable;key down
dcMainLoop-EventTable ;key up
dcKeyDown-EventTable;auto key
dcUpDateJump-EventTable ;update event
dcMainLoop-EventTable ;disk event
dcMainLoop-EventTable ;activate event
dcMainLoop-EventTable ;abort
dcMainLoop-EventTable ;network event
dcMainLoop-EventTable ;I/O driver event
;------------------------------------------------
SetUpWindow
clr.l -(SP) ;for returned pointer
clr.l -(SP) ;let Mac make storage
pea WindowRect ;windows rectangle
clr.l -(SP) ;no title
move.b #1,-(SP) ;it is visible
move #plainDBox,-(SP) ;Type of window
move.l #-1,-(SP);in front
move.b #0,-(SP) ;no close box
clr.l -(SP) ;refcon zero
_NewWindow ;make window!
move.l (SP),WindPtr(a4) ;pointer into global
_SetPort ;set as the port
rts
;------------------------------------------------
SetUpMenus
clr.l -(SP) ;get applications menu
_GetMenuBar;bar handle for later
move.l (SP)+,oldMenuBar(A4) ;restoration!
_ClearMenuBar ;clear Apps menu bar
;make all of our menus
;the apple menu
clr.l -(SP)
move #1,-(SP)
pea AppleSymbol
_NewMenu
move.l (SP)+,Menu1Hndl(A4)
move.l Menu1Hndl(A4),-(sp)
pea About Fkey Sampler...
_AppendMenu
move.l Menu1Hndl(A4),-(sp)
move #2,-(SP)
_InsertMenu
;the File menu
clr.l -(SP)
move #2,-(SP)
pea File
_NewMenu
move.l (SP)+,Menu2Hndl(A4)
move.l Menu2Hndl(A4),-(sp)
pea Open FKEY/F
_AppendMenu
move.l Menu2Hndl(A4),-(sp)
pea (-
_AppendMenu
move.l Menu2Hndl(A4),-(sp)
pea Quit/Q
_AppendMenu
move.l Menu2Hndl(A4),-(sp)
clr -(SP)
_InsertMenu
_DrawMenuBar ;draw the new menu bar
rts
UpDateJump
bsr UpDateWindow
bra MainLoop
;------------------------------------------------
;This routine keeps our background window (The DeskTop!)
;the same pattern as the current standard desktop!
;------------------------------------------------
UpDateWindow
move.l WindPtr(A4),-(SP) ;our background
;window
_BeginUpdate ;is kept in WindPtr(A4)
move.l WindPtr(A4),-(SP)
_SetPort
pea PaintRect;the rectangle to paint
pea DeskPattern;global var, contains
_FillRect;pattern used for desktop
move.l WindPtr(A4),-(SP)
_EndUpdate
rts
;------------------------------------------------
;A key down event occured, check if the command key was
;down, if not, get the next event!
;------------------------------------------------
KeyDown
move Modify(A4),D1
btst #8,D1 ;check command key bit
beq MainLoop ;zero if command key
;is not down!
clr.l -(sp)
move message+2(A4),-(sp) ;put char onto stack
_MenuKey ; for _MenuKey
move.l (sp)+,D4 ;put result in D4
beq MainLoop ;if zero, no menu equivalent
bra WhatMenu
;------------------------------------------------
;A mousedown event occured, we only check if its in a menu, if
;not then get the next event!
;------------------------------------------------
MouseDown
clr -(SP) ;word result
move.l where(A4),-(SP) ;where field of ;event record
pea TheWindow(A4);returns the window ;pntr the
_FindWindow;mouse was clicked in,
move (SP)+,D0 ;if it was in one.
add D0,D0
move WindowTable(D0),D0
jmp WindowTable(D0) ;jump to appropriate ;routine
;We only use InMenu event!
WindowTable
dcMainLoop-WindowTable ;In Desk
dcInMenu-WindowTable ;In Menu Bar
dcMainLoop-WindowTable ;In System Window
dcMainLoop-WindowTable ;in Content
dcMainLoop-WindowTable ;in drag
dcMainLoop-WindowTable ;in grow
dcMainLoop-WindowTable ;in go away
;------------------------------------------------
;The mouse was clicked in the menu bar.
;------------------------------------------------
InMenu
clr.l -(SP)
move.l where(A4),-(SP)
_MenuSelect
move.l (SP)+,D4
beq MainLoop ;zero if no item selected
;jump to here after command key down event
WhatMenu
move.l D4,D6 ;D6 will now have the
swap D4; item # D4 will have menu #
WhichMenuWasIt
cmp #AppleMenu,D4;is it in the apple menu?
beq InAppleMenu;if so, go see which item!
cmp #FileMenu,D4 ;is it in the File menu?
beq InFileMenu
bra MainLoop ;where else could it be!
InAppleMenu ;in the Apple menu
cmp #AboutItem,D6
beq DoAbout ;its in the About... Item!
bra MainLoop
InFileMenu;in the File menu
cmp #OpenFkeyItem,D6 ;in the OpenFkey Item?
beq Open_Fkey;if so go open it!
cmp #QuitItem,D6 ;Quit item?
beq QuitRoutine;if so go quit
bra MainLoop
;------------------------------------------------
;Show the About Fkey Sampler... window
;------------------------------------------------
DoAbout
saveregs ;save the registers
clr.l -(SP)
clr.l -(SP)
pea AboutRect
move.l #0,-(SP)
move.b #1,-(SP)
move #1,-(SP)
move.l #-1,-(SP)
move.b #0,-(SP)
clr.l -(SP)
_NewWindow ;make a window
move.l (SP),A2 ;put pointer into A2
_SetPort ;make it the port
move #Monaco,-(SP);Use the Monaco Font
_TextFont
move #9,-(SP) ;make it 9 point (size)
_TextSize
pea MyTextBegin;address of About Text
;calculate how many chars in text for TextBox
move.l #MyTextEnd-MyTextBegin,-(SP)
pea TextRect ;rectangle for text
move #1,-(SP) ;center justification
_TextBox
bsr Wait_For_Event ;wait for button/key down
move.l A2,-(SP)
_DisposWindow ;get rid of the window
bsr UnHiliteIt ;go unhilight the menu!
move.l WindPtr(A4),-(sp)
_SetPort ;reset our old port
restoreregs;restore registers
bra MainLoop ;go get another event
;------------------------------------------------
;Wait for mouse or key down event
;------------------------------------------------
Wait_For_Event
link A6,#-evtBlkSize ;link, event block size
WaitLoop
lea -evtBlkSize(A6),A0 ;ptr to event block in A0
moveq #mDownMask!KeyDownMask,D0 ;Event masks in D0
_GetOSEvent
beq Clicked ;if zero, time to leave!
bra WaitLoop ;loop until event
Clicked ;mouse or key clicked!
unlk A6;unlink A6
rts
;------------------------------------------------
;Quitting, dispose of all our storage and menus and return to
;the application that the Fkey was called from!
;------------------------------------------------
QuitRoutine
move.l WindPtr(A4),-(SP) ;dump background
_DisposWindow ;window
move.l Menu1Hndl(A4),-(SP) ;dump our #1
_DisposMenu ;menu
move.l Menu2Hndl(A4),-(SP) ;dump our #2
_DisposMenu ;menu
_ClearMenuBar ;clear the menu
move.l oldMenuBar(A4),-(SP)
_SetMenuBar;restore apps menu bar!
move.l oldMenuBar(A4),A0 ;dump copy of
_DisposHandle ;apps menu handle!
_DrawMenuBar ;draw it
move.l A4,A0
_DisposPtr ;dump our global ;storage
No_Mem
move.l #$0000FFFF,D0 ;flush all events
_FlushEvents
_SetPort ;set original port!
restoreregs;restore the registers
rts ;return to the application!
;------------------------------------------------
;Couldnt allocate space for our global storage area
;just beep & quit!
;------------------------------------------------
Mem_Error
move #10,-(SP)
_SysBeep
rts
;------------------------------------------------
;Unhilight menu bar after mouse down in a menu item
;------------------------------------------------
UnHiliteIt
move #0,-(SP)
_HiliteMenu
rts
;------------------------------------------------
;Get a file of type FKEY, open its resource fork, and
;execute the Fkey (if there is one) contained in it!
;------------------------------------------------
Open_Fkey
saveregs ;save registers
move #82,-(SP);coordinates for GetFile
move #100,-(SP) ;dialog box
clr.l -(SP) ;prompt
clr.l -(SP) ;File Filter Proc Pointer
move #1,-(SP) ;number of file types
pea TypeList ;our type list
clr.l -(SP) ;Dialog Hook Proc Pointer
pea sfReply(A4);sfreply record
move #2,-(SP) ;#2 for SFGetFile
_Pack3 ;Call the Get File Pack
cmp.b #0,sfReply(A4)
beq Cancel ;if user hit cancel, quit!
bsr UpDateWindow ;update window before
;opening the Fkey
lea IOParamBlk(A4),A2 ;the IO param block
clr.l 12(A2) ;zero ioCompletion
lea GFileName(A4),A0 ;put a pointer to the files
move.l A0,18(A2);name into ioNamePtr
move GetVRef(A4),22(A2) ;ioVRefNum
move.l A2,A0 ;put param block ptr in A0
_SetVol;set as current volume
clr -(SP) ;space for refnum
pea GFileName(A4);name of res file to open
_OpenResFile ;Open that resource file
move (SP)+,D7 ;put result in D7
cmp #-1,D7 ;will be -1 if any
;errors!
beq Cancel;if error go to cancel
;this stops resources from being loaded with GetIndResource,
;GetResource or GetNamedResource
move.w #0,-(sp)
_SetResLoad
clr.l -(sp)
move.l #FKEY,-(sp)
move #1,-(sp)
_GetIndResource ;get first FKEY resource
move.l (sp)+,A3 ;handle into A3
;Turns Automatic resource loading back on!
move.b #1,-(sp)
_SetResLoad
clr.l -(sp)
move.l A3,-(sp)
_SizeRsrc
move.l (sp)+,D2 ;how big is the resource?
cmp.l #-1,D2 ;-1 = no resource
beq NotEnoughMem ;no resource error so quit
;do we have enough memory to load the FKEY
move.l D2,D0
_NewHandle
move.l A0,A1
cmp.l #0,A1 ;was zero returned?
beq NotEnoughMem ;not enough room, so quit!
_DisposHandle ;otherwise dispose the new
;handle, and go on..
move.l A3,-(sp)
_LoadResource ;Load resource into memory!
clr -(sp)
move.l A3,-(SP)
_HomeResFile ;what resource file
move (sp)+,D2 ;is this resource from?
cmp D2,D7 ;is it from new file?
beq YepItsFromNewFile ;yep!
bra NotEnoughMem ;if not, close the res
;file and quit!
YepItsFromNewFile
move.l A3,A0
_Hlock ;Lock it before jumping to it
move.l (A3),A2 ;put pointer to FKEY in A2
SaveRegs ;save the regs
jsr (A2);Run the Fkey!!
RestoreRegs;restore the regs
move.l A3,A0
_HUnlock ;UnLock FKEY handle
bsr CloseIt ;go close res file
Cancel
restoreregs;restore the regs
bsr UnHiliteIt ;unhilight the menu
bra MainLoop ;get the next event
NotEnoughMem
bsr CloseIt
bra Cancel
CloseIt
move D7,-(SP)
_CloseResFile ;Close the res file
rts
;------------------------------------------------
;All constants go here
;------------------------------------------------
;rect of background (desktop) window
;make it giant to cover entire background!
WindowRectdc0,0,4000,4000
PaintRect dc0,0,4000,4000
TypeListdc.bFKEY
.ALIGN 2
;rect of About... window
AboutRect dc130,100,190,412
;rect text will be displayed in for _TextBox
TextRectdc10,10,90,302
AppleSymbol dc.b 1,$14 ;apple symbol for menu #1
.ALIGN 2
MyTextBegin dc.b Fkey Sampler 1.0,$0D,$0D
dc.b Written by John Holder
.ALIGN 2
MyTextEnd
END
;The Linker file (file name FkeyTemp.Link)
]
FKEYTemp
/Output FkeyTemp.Code
/Type TEMP
$
*The RMaker file (file name FkeyTemp.R)
Fkey Sampler.Fkey
FKEYQD15
TYPE FKEY = PROC
Fkey Sampler,9
FkeyTemp.Code