Still, the Hack API is intended to be an evolving structure. If there is some feature that you would like your extension to be able to rely upon, feel free to email me at mistered@1stresource.com and I'll see if I can roll it into the next HackMaster revision.
Introduction
The Hack File Format
Compiling and Linking a Hack
Trap Patch Interaction Details
Control Panel Interaction Details
The Custom Procedure
Licensing Information
Conclusion
For instance, one could code up a Hack to replace the standard Keyboard dialog with a new input method, or insert a procedure into the event-handling code in order to perform special actions at the entry of a certain Graffiti character, or even patch the text field routines to allow hyperlinking between applications. The potential uses are even wider than the system software itself.
Unfortunately, there are some problems with implementation. Where in memory does one put the routine? How does one install it into the trap? How do you deal with multiple patches on the same trap, especially if they install and remove themselves in different orders? How do you gracefully recover from a system reset?
It is to solve these problems that HackMaster was written. HackMaster reads in special PalmPilot resource files of type 'HACK', which contain the code for the patch and also whatever user interface routines are necessary. Then HackMaster presents a standard interface to the user for installing and removing Hacks, and for changing their configurations. It handles all the dirty work of installing and removing the trap patches and keeping track of multiple Hacks on the same trap. And it saves the current extension configuration so that they can all be automatically (or optionally) reinstalled on a system reset.
This document, then, will take you through the process of writing a PalmOS system extension using the HackMaster protocol. In doing so, you will be bypassing a lot of effort in the details of trap patching, and you will be ensuring that your extensions will automatically coexist peacefully with all other possible extensions.
I assume a fairly high level of Pilot programming knowledge. You should definitely be able to code up a standalone PalmOS application before attempting to write a Hack, since the facilities for debugging are much less and the potential for mishaps is much greater. Some of the techniques employed here are not part of the standard PalmOS documentation, but with any luck Palm may adopt HackMaster for 'official' use in the future. (If you want to induce them to, send a little note to devsupp@palm.com...)
For more information on compiling and linking the necessary 'code' resources, skip to the next section. This section outlines the larger structure of the .prc file, specifying the resource IDs and types for the various parts of the Hack to be recognized and function properly.
The Hack file type is 'HACK', instead of that standard 'appl' for application files. Note that each Hack, just like each application, requires a unique creator code to identify it. These creator codes should be registered with Palm Computing just like applications to prevent conflicts. Anyway, here are the resources to include:
The 'code' resources will be called directly by the trap dispatcher, so be sure they are linked in the appropriate way (see the next section). It is possible to have multiple patches in the same file patching the same trap, but there isn't much purpose to this anyway. Almost any trap can be patched, with the major exception of the Feature Manager calls.
In general, unless you have a very good reason, you should almost always choose to supplement the HackMaster behavior rather than replace it. For more details on this custom routine, see a later section of this file.
The basic idea is that you want to link your code in such a way that HackMaster can call the procedure you want by just jumping to the beginning address of your code resource. To do this, you will need a separate C source file for each code resource, and a separate compile and link statement for each one. Only in the building of the resource file do all the parts come together.
Unfortunately, since you are not compiling a free-standing application, but just a code fragment, you will not be able to use the Pilot Simulator at all. All of the compiling is done in MPW, and all debugging will be on-Pilot. In the future, we may be able to figure out a way to write a trap patch shell to run on the simulator, but for now you're living on the bleeding edge.
#include <Pilot.h> //no global variables allowed! /* Put procedures called by the main routine here. */ Boolean MySysHandleEventTrap(EventPtr event) { // the trap patch routine here }
Quite simple, actually. For a control panel event handler, you do the exact same thing, except that the routine to be called should have the functional form of a standard event handler routine (the same as above, but just because we coincidentally picked an event-handler routine to trap).
"{OBJ_DIR}mypatch1.c.o" � MakeFile "{SRC_DIR}mypatch1.c" {CPP} -o "{OBJ_DIR}mypatch1.c.o" "{SRC_DIR}mypatch1.c" � {C_OPTIONS}
In the above sample, the directories and compiler options should already be defined by the makefile in the normal way. This step will get you the object files you need to link into the code resources.
LINK_OPTIONS = -single -coderesource -rt CODE=1 {LINK} {LINK_OPTIONS} -t rsrc -c RSED -m MyRoutine � "{OBJ_DIR}mypatch1.c.o" -o mypatch1.code
The first thing different is that the -custom
link type has been replaced with a -coderesource
tag, indicating that the code should be linked into a 'CODE' resource of ID 1 in the Macintosh resource output file. Secondly, the -m
tag is used in the link statement in order to specify what routine in the file should be put in the beginning of the resource. This is done in such a way that this routine will be run by just jumping to the first byte of the CODE resource, which is how HackMaster knows where your routines are.
#include <BuildRules.h> #include <SystemMgr.rh> include "mypatch1.code" 'CODE' 1 as sysResTAppCode 1000; #if LANGUAGE==LANGUAGE_ENGLISH include ":Rsc:mypatch.rsrc"; #else #error "The compiler variable LANGUAGE must be defined" #endif
Note that in this resource file, no mention is made of the CODE 0 and DATA 0 resources, which contain global variable information in a full Pilot program. Here, just the one CODE resource is compiled as a 'code' resource of ID 1000, exactly as a trap patch must be. If you had a control panel event handler, you would need a second include statement:
include "mypatch2.code" 'CODE' 1 as sysResTAppCode 2000;
The last step is to give MPW the command to build the .PRC file:
{CC} -d RESOURCE_COMPILER {C_OPTIONS} � -e "{SRC_DIR}mypatch.r" > mypatch.i PilotRez -v 1 -t HACK -c ???? -it mypatch.i -ot "My Patch" Duplicate -y "MyPatch" "mypatch.prc"
Note that the application type for a Hack file is 'HACK', intead of 'appl'. Other than that, you get a standard .prc file that can be installed and deleted in the usual way. However, it will not show up in the Applications dialog, but only in the HackMaster list.
Here is a sample code snippet showing how your trap routine on SysHandleEvent in ID 1000 should call the system routine:
#define mycreator '????' #define myresourceID 1000 Boolean MySysHandleEventTrap (EventPtr event) { Boolean (*oldtrap)(EventPtr); //procedure pointer to old trap; DWord temp; //for the feature manager call type checking Boolean handled; FtrGet(mycreator,myresourceID,&temp); //get old trap address from HackMaster oldtrap=(Boolean (*)(EventPtr))temp; //set procedure pointer handled=false; //do whatever you want to with the event, handle it or not if (!handled) { //if you didn't handle it, call the old routine handled=oldtrap(event); } return handled; }
Depending on the function of the Hack, the original routine can be used in different ways. It could be ignored entirely, in order to replace its functionality entirely with that of the Hack. Actions could be taken by the Hack, and then the original routine called, as for instance an event handler patch. The parameters of the routine could be altered and then the original called, or the original called and then its results manipulated. In general, unless you have a reason not to, the original system routine should be called at some point, if for no other reason than to allow other Hacks installed on the same trap to process the event too.
Hacks can be installed and removed at will, so be sure to get this address new at every function call, since it may have been changed to reflect a new trap patch.
Upon installation, the patch 'code' resource is locked in memory, and remains so for the duration of its use. It is in one of the static heaps, though, so memory protection needs to be kept in mind if you have some visions of self-modifying code.
HackMaster will trap frmLoadEvents with IDs in the range 2000-2999 and perform the necessary setup for them. It will open the Hack's resource file and call FrmInitForm for the form in question. It will also install the 'code' resource with the same ID as the event handler for that form using FrmSetEventHandler.
HackMaster will also trap frmCloseEvents. Upon doing so, it will immediately pass the event on to the Hack's event handler and/or the standard form event handler using FrmDispatchEvent. After this call returns, HackMaster will close the Hack's resource file again.
If other forms need to be brought up by the control panel, this can be done with FrmGotoForm commands. FrmPopupForm is not recommended because this will probably result in the resource file being opened twice by HackMaster.
The control panel must provide a means for the user to exit it, such as a 'Done' button. At such a close, the control panel should execute a FrmGotoForm(9000) command to jump back to the HackMaster main list.
The control panel event handler, just like any other 'code' resource discussed above, cannot rely on global variables. Feature manager calls possibly in combination with database or dynamic heap allocation should be used to save whatever state information is necessary between event handler calls.
Note that since the control panel handler is called by the form event dispatcher, system events will already have been handled at an earlier stage, so it is not currently possible for the control panel to respond to system-level events, such as hardware app button presses, for instance, without serious workarounds.
The control panel must be able to gracefully handle the case of being brought up when the extension is installed or not. If configuring of the extension is not possible while it is running, the control panel should detect this situation (with Feature Manager calls) and display a "sorry, can't do that" dialog.
Anyway, if you do want to distribute copies of HackMaster with other software you have written, we'll have to arrange some sort of license. Specifics vary on a case-by-case basis, but generally I would expect a licensing fee per copy of 25 cents or one percent of the cost of the product, whichever is less. This entitles you to distribute copies of HackMaster, but does not mean that your customers are automatically registered users with all the rights and privileges thereof (like technical support, email updates, etc.)
Anyway, just email me at mistered@1stresource.com and we'll talk specifics if this applies to you. If, on the other hand, you are a shareware/freeware author and want to distribute copies of HackMaster with your Hacks, go right ahead. Just be somewhat obvious in mentioning the fact that HackMaster is indeed shareware are should be registered with DaggerWare; the appropriate URL is in the program itself.
So have fun!