Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
PatchPowerOff.c
/* |
** Apple Macintosh Developer Technical Support |
** |
** Sample code demonstrating how to patch PowerOff key |
** |
** by Brian Bechtel, Apple Developer Technical Support |
** |
** File: PatchPowerOff.c |
** |
** Copyright © 1995 Apple Computer, Inc. |
** All rights reserved. |
** |
** You may incorporate this sample code into your applications without |
** restriction, though the sample code has been provided "AS IS" and the |
** responsibility for its operation is 100% yours. However, what you are |
** not permitted to do is to redistribute the source as "DTS Sample Code" |
** after having made changes. If you're going to re-distribute the source, |
** we require that you make it clear in the source that the code was |
** descended from Apple Sample Code, but that you've made changes. |
*/ |
#include <Gestalt.h> |
#include <Resources.h> |
#include <Errors.h> |
#include <OSUtils.h> |
#include <Traps.h> |
#include <SetupA4.h> |
#include <A4Stuff.h> |
/* from technote 1017, "System 7.5.3" Thanks to John Montbriand for pointing |
* out that I'd used a seed release of the technote, which didn't include in |
* the UPP definition any space for the result of the call. |
*/ |
#ifdef PowerPC |
enum { |
uppPowerOffProcInfo = kPascalStackBased |
| RESULT_SIZE(SIZE_CODE(sizeof(OSErr))) /* space for result */ |
| STACK_ROUTINE_PARAMETER(1, kTwoByteCode) |
| STACK_ROUTINE_PARAMETER(2, kTwoByteCode) |
}; |
#endif |
/* constants used as parameters to the power off key routine */ |
enum { |
kPowerKey = 0x7F, |
kDissablePwKy = 0x6B, |
kEnablePwKy = 0x00, |
kShutdownDlog = 0x7E, |
kNoDismiss = 0x00, |
kOneSecond = 0x70, |
kTenSeconds = 0x400 |
}; |
/* declaration for the power off key routine */ |
typedef pascal OSErr (*PwrKeyProc)(short item, short action); |
/* for debugging */ |
#define assert(i) if ((i) DebugStr("\p;printf \"assertion failed file %s line %d\" __FILE__, __LINE__") |
#define kINITid 0 /* matches the resID in the 68K Project preferences */ |
#define kICONid 128 |
pascal void ShowIcon7(short iconId, Boolean advance); |
// local function declarations |
void InitAlertPatch(void); |
void ClearAlertPatch(void); |
void asm AlertPatch(void); |
OSErr DisablePowerOffKey(void); |
void main(void); |
static long gOriginalTrapPtr = 0; |
static Boolean gAlertPatchIsInstalled = false; |
/* |
* InstallAlertPatch |
* This routine patches the Alert() trap so that we can check for |
* the PowerOff alert and dismiss it before it comes up. |
*/ |
void InitAlertPatch(void) |
{ |
if (gAlertPatchIsInstalled == false) |
{ |
gOriginalTrapPtr = (long) NGetTrapAddress( _Alert, ToolTrap); |
NSetTrapAddress( (UniversalProcPtr)(AlertPatch), _Alert, ToolTrap); |
gAlertPatchIsInstalled = true; |
} |
} |
/* |
* ClearAlertPatch |
* This routine clears out the patch we installed in InstallAlertPatch. |
* Note: Never call this routine from within an extension; you don't know |
* what other patches may have been put on Alert() after you installed |
* your patch, and bad things would happen if you eliminate yourself from |
* the chain of patches... |
*/ |
void ClearAlertPatch(void) |
{ |
if (gAlertPatchIsInstalled == true) |
{ |
NSetTrapAddress( (UniversalProcPtr)(gOriginalTrapPtr), _Alert, ToolTrap); |
gAlertPatchIsInstalled = false; |
} |
} |
/* |
* AlertPatch |
* This patch checks to see if we are about to display alert -16500. This |
* is the ALRT id used by the dialog which asks if you want to shutdown, etc. |
* If we are about to show that dialog, fake things up as if we have already |
* shown the dialog, and the user has pressed the Cancel button. The result |
* is that the dialog is never shown, and no action is taken. Just as if |
* the PowerOff code was never added to the system... |
* Call this routine only if the programmatic method (using the 'pwky' Gestalt |
* selector) fails. |
* This patch trashes register a0 and possibly register d0, but |
* these registers are trashed by Alert anyway. |
*/ |
void asm AlertPatch(void) |
{ |
// Check for proper alert. If it's the trap we want to avoid, |
// return to caller without actually executing the Alert trap. |
// Set up the stack so it looks as if the user hit okay. |
PatchPowerOff: |
// test for specific PowerOff dialog id. If we find it, return as if we hit cancel. |
cmpi.w #-16500,8(sp) // we want to avoid -16500 |
bne exitPatchPowerOff |
// if it's the alert we want, return to caller without |
// executing the alert. |
// FUNCTION Alert ( alertID: INTEGER; filterProc: ProcPtr) : INTEGER; |
// means that the stack looks like this: |
// sp + 0 -> return address (4 bytes) |
// + 4 -> filterProc (4 bytes) |
// + 8 -> alertID (2 bytes) |
movea.l (sp)+, a0 // the caller's return address |
lea 6(sp), sp // clear off the parms put on by caller |
move.w #2, (sp) // tell the system we hit cancel. (2nd button = cancel) |
jmp (a0) // return to caller without actually calling Alert |
exitPatchPowerOff: |
// call the original trap. |
//First, set up a4 to access the old trap address |
jsr SetUpA4 // puts old a4 into register d0 |
lea gOriginalTrapPtr, a0 |
exg d0, a4 // restore old value of a4 |
movea.l (a0), a0 |
jmp (a0) |
} |
/* |
* DisablePowerOffKey |
* This routine will either call the routine pointed to by the Gestalt |
* selector 'pwky', telling it to disable the power off key, or this |
* routine will return an error (usually telling you the Gestalt selector |
* is not installed.) |
*/ |
OSErr DisablePowerOffKey(void) |
{ |
OSErr err; |
PwrKeyProc pPwrKey; |
err = Gestalt('pwky', (long*) &pPwrKey); |
if ( (long) pPwrKey == nil ) |
err = gestaltUndefSelectorErr; // no proc ptr means no selector |
if ( err == noErr ) |
#ifdef PowerPC |
err = CallUniversalProc((UniversalProcPtr) pPwrKey, |
uppPowerOffProcInfo, kPowerKey, kDissablePwKy); |
#else |
err = pPwrKey(kPowerKey, kDissablePwKy); |
#endif |
return err; |
} |
/* main */ |
void main(void) |
{ |
long oldA4; |
Handle h; |
OSErr err = noErr; |
#ifdef USE_DEBUGGER_CALLS |
Debugger(); |
#endif |
/* set up our A4 context for _this file_ */ |
oldA4 = SetCurrentA4(); |
RememberA4(); |
/* First try to disable the power key programmatically. |
* If that doesn't work, do a skanky hack. |
*/ |
if (DisablePowerOffKey() != noErr) |
{ |
/* detach ourselves */ |
h = Get1Resource('INIT', kINITid); |
if (h) DetachResource(h); |
InitAlertPatch(); |
} |
ShowIcon7(kICONid, true); |
/* restore the a4 world */ |
SetA4(oldA4); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14