home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-06-21 | 17.5 KB | 439 lines | [TEXT/CWIE] |
- // File "filter.c" -
-
- #include <A4Stuff.h>
- #include "filter.h"
- #include "main.h"
- #include "layers.h"
-
- // ***********************************************************************************
- // ***********************************************************************************
-
- /*
- The Not-So-Simple FAT jGNEFilter
- Original Code by Matt Slot (fprefect@umich.edu), 6/2/95
- with help and criticism (lots!) from Ed Wynne (arwyn@umich.edu).
-
- Quick Intro
- Since jGNEFilters are nasty things in 68k, and pretty much impossible
- in PPC, writing simple and cross-compiling handler code is also. To
- facilitate use of this, I have written some interface routines to hide
- some of the complexity from the application programmer.
-
- / Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData);
- | Installs a jGNEFilter which can properly call a callback from within
- | the application. The callback handles incoming events wither either
- | 68k or PPC code, and gets passed the data pointer you send from here.
- | Install() returns a pointer the jGNEFilter (which has been loaded from
- | a resource) installed in the system heap -- or NIL to indicate an error.
-
- / Ptr ReleaseEventFilter(Ptr filterProc);
- | Pass in the pointer to the jGNEFilter, and this routine will disable
- | the handling and try to dispose of its storage in the System Heap.
- | You *must* do this if your helper function is going to disappear when
- | the application closes.
-
- / Code Resource 'jGNE', #128: the jGNEFilter
- | Since there is no way to get the same routine to compile on both 68K
- | and PPC, I have compiled the jGNEFilter into an executable resource.
- | The real code of this resource has been compiled from the additional
- | source file you can find in the project folder. Note that the code simply
- | calls the ProcPtr for the Helper function blindly... whether 68k or PPC.
- | See below for more details as to how the filter is loaded, unloaded,
- | and called.
-
- / void EventFilterHelper(EventRecord *theEvent, Ptr helperData);
- | This callback is the workhorse of the event filter. Once installed, this
- | routine sees every event that gets harvested and has an opportunity to
- | modify the record before the front application gets to see it. Ideally
- | this function can do the necessary work itself or pass off the event info
- | to the home application.
- | WARNING: For 68k code, the routine will be called from the current app's
- | context (A5/Globals, Rsrc File, HeapZone). PPC code will have a valid RTOC
- | (Globals access) but not Rsrc File, HeapZone, etc.
-
-
- How this all works:
-
- Since I didn't want to write the jGNEFilter in C (OK, I couldn't figure out
- a good way), the code is carried around in a resource. Originally, I had
- saved the routine as a constant string that was StuffHex()'ed into the System
- Heap pointer... but I got so many questions and comments I decided to do it
- the normal way.
-
- The jGNEFilter keeps 3 pieces of data inline: the next filter in the chain,
- a pointer to the helper routine, and some extra data to pass to the helper.
- Most importantly, we may not be able to remove the filter from the calling
- chain... the architecture just doesn't permit it! If we are able to safely
- pull the filter out, we do. Otherwise the next best solution is to keep a flag,
- that we can clear when we want to disable the functionality -- in fact, we
- set or clear the pointer to the Helper Proc as the flag.
-
- Finally, the helper function is the meat of our jGNEFilter; it does the work
- of the active filter. In the case of a 68K helper, it is accessed via a
- simple ProcPtr. In the case of a PPC helper, the installer sets up a valid
- RoutineDescriptor (in the System Heap with the jGNEFilter) to invoke a
- MixedMode switch between the 68K caller (filter) and PPC routine (helper).
- Again, when releasing the filter the handler disposes the descriptor and
- clear the inline ProcPtr/flag, since the helper function will probably be
- disappearing when the application quits.
-
- If you are picking out events to handle within your app, my suggestion is to
- keep a secondary Queue of events in the System Heap -- remember, you must
- allocate new EventRecords (since the current event belongs to the calling
- app) into the System Heap (you need a heap that both the current process and
- your own process can access). Given these events, your main event loop can
- suck out clicks or keydowns for dispatching internally and safely within your
- own context.
-
- Also, Text Service windows don't receive Activate or Update events... you
- must check for those manually within your own event loop and handle them.
-
- */
-
- // ***********************************************************************************
- // ***********************************************************************************
-
- Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData) {
- Handle filterRsrc;
- Ptr filterProc, data;
-
- // Create a duplicate function in the System Heap (so its *alway* there) and
- // copy the data across. Note: even though it is technically a function, we
- // can still treat it as data safely until it has been installed and invoked.
- filterRsrc = Get1Resource(kJGNEFilterResType, kJGNEFilterResID);
- filterProc = NewPtrSys(GetHandleSize(filterRsrc));
- // If either is nil, there is no need to free.. we are quitting anyway
- if (! filterRsrc || ! filterProc) return(0);
- BlockMove(*filterRsrc, filterProc, GetHandleSize(filterRsrc));
- ReleaseResource(filterRsrc);
-
- // Get and install the current filter as the next filter in the chain.
- data = (Ptr) LMGetGNEFilter();
- BlockMove(&data, filterProc + kNextFilterOffset, sizeof(data));
-
- // Get and install the Helper function to do the real work (and as a flag to
- // indicate we are in business and accepting events). Remember that if we
- // generating PPC code, it is necessary to establish a Routine Descriptor.
- SetZone(SystemZone());
- data = (Ptr) NewFilterHelperProc(helperProc);
- if (! data) return(0);
- BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
- SetZone(ApplicationZone());
-
- // If the caller wants to pass data to the jGNEFilter Helper function. This
- // pointer (or handle if desired) *must* be allocated in the System Heap
- // if you don't plan on releasing the Filter before quitting. If you plan
- // on releasing the filter, then either the App or Sys heap will suffice.
- data = helperData;
- BlockMove(&helperData, filterProc + kEventHelperDataOffset, sizeof(data));
-
- // Install us, we are ready to do some work!
- LMSetGNEFilter((GNEFilterUPP) filterProc);
-
- return(filterProc);
- }
-
- // ***********************************************************************************
- // ***********************************************************************************
-
- Ptr ReleaseEventFilter(Ptr filterProc) {
- Ptr data;
-
- if (! filterProc) return(0);
-
- // Clear the Helper location as an indicator that we have closed up shop. The
- // filter itself may lingers in the System Heap until shutdown unless we can
- // find a way to extract it from the chain (see below). On the other hand, the
- // filter has been written so that if the Helper function pointer is NIL, the
- // filter will do nothing at all. Let's zero it out for that (hopeful) case.
- BlockMove(filterProc + kEventHelperOffset, &data, sizeof(data));
- if (data) DisposeRoutineDescriptor((UniversalProcPtr) data);
- data = 0;
- BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
-
-
- // If the installed filterProc is the first one in the chain, then we should
- // be able remove it and replace it with the next one (the one we would
- // normally jump to). If we can dispose the filterProc buffer, then we can
- // can recover those 50 bytes that remain in the System Heap.
- // Thanks to HoverBar's Guy Fullerton (hedgeboy@realm.net) for the suggestion.
- if (filterProc == (Ptr) LMGetGNEFilter()) {
- // Remove our filterProc from the chain.
- BlockMove(filterProc + kNextFilterOffset, &data, sizeof(data));
- LMSetGNEFilter((GNEFilterUPP) data);
-
- BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
- DisposePtr(filterProc);
- }
- else {
- // Grab the data that was passed when initialized or as set in the Helper
- // function. The caller can then deallocate it if desired or necessary.
-
- BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
- }
-
- return(data);
- }
-
- // ***********************************************************************************
- // ***********************************************************************************
-
- #pragma mark -
-
- #define modKeysDown(modifiers) ((modifiers & glob.prefs.activateModifiers) == glob.prefs.activateModifiers)
-
- void EventFilterHelper(EventRecord *event, Ptr helperData) {
- // This only does something in 68K code. We now have access to globals,
- // which PPC get for free from CFM; however, we won't have access to
- // our application's Resource file/chain or HeapZone. Be careful!
- long saveA4 = SetA4((long) helperData);
- WindowPeek i;
- Boolean modsDown = modKeysDown(event->modifiers);
-
- /*
- This switch statement eliminates two redraw problems. One is that if we do anything with
- the spotlight on an updateEvt, it is possible to invalidate the updateEvt and so some
- little areas do not get drawn correctly. mouseUp events are also ignored because the
- event->where point can be quite far from where the mouse currently is (because it's set to
- where the *mouseUp* occurred, and sometimes the event doesn't come through until a bit after
- it happens). So, when mouseUp events are not ignored, sometimes the spotlight can "jump" back
- to where the mouseUp happened, and then back to where the cursor currently is.
-
- In addition, it eliminates a bizarre problem where sometimes when a high level event came
- through, the modifiers field would not correctly represent the true state of the modifiers.
- What kept happening was, whenever I launched MW Debug, the spotlight would immeditely
- activate. This proved incredibly annoying because it switched to the finder and sent all
- other apps to the background, and it happened sometimes when stepping through code, too. So
- you think you're debugging your program, then poof, you're in the finder. :) Anyway, I have
- no idea why the modifiers field should be invalid -- IM:Toolbox Essentials mentions nothing
- about this -- so perhaps it's something in IM:IAC, something undocumented, a bug in MW
- Debug...whatever the reason, this fixes it.
- */
-
- switch (event->what) {
- case updateEvt:
- case mouseUp:
- case kHighLevelEvent:
- goto end;
- break;
- }
-
- if (glob.active) {
- if (modsDown) ShowSpotlight(event->where, false);
- else {
- for (i = (WindowPeek)LMGetWindowList(); i != nil; i = i->nextWindow) {
- if (i->visible) RestoreWindow(i);
- }
- CalcVisBehind(LMGetWindowList(), glob.spotlightRgn);
- PaintBehind(LMGetWindowList(), glob.spotlightRgn);
-
- glob.active = false;
- MoveSpotlightOffscreen();
- }
- }
- else if (glob.activating) {
- ProcessSerialNumber psn;
- Boolean inTheFinder;
-
- GetCurrentProcess(&psn);
- SameProcess(&psn, &glob.finderPSN, &inTheFinder);
- if (inTheFinder) {
- glob.activating = false;
- StartSpotlight(event->where);
- }
- }
- else if (modsDown) {
- // don't show the spotlight if there is a dialog open
- WindowPtr window = FrontWindow();
- if (window != nil && window == glob.lastModalDialog) goto end;
-
- if (!(glob.activating = HideAllButFinder())) StartSpotlight(event->where);
- }
-
- end:
-
- if (glob.active) {
- event->modifiers &= ~glob.prefs.disableModifiers;
- }
-
- // Restore the (68K) context before leaving
- SetA4(saveA4);
- }
-
- void StartSpotlight(Point where) {
- glob.active = true;
- ShowSpotlight(where, false);
- }
-
- /*
- ShowSpotlight displays the spotlight centered on a point passed to it. It determines which
- windows need modifying and updating.
-
- Why, you ask, do I loop through the entire window list *twice*? Well, it has to do with how the
- WDEF calculates where to draw the close box, grow box, windowshade box, etc. In the past, these
- two loops were combined. The result was that by the end of the loop, some windows had the
- spotlight region cut out of them. Then, PaintBehind() was called to redraw areas which had been
- vacated by the spotlight. If a titlebar was involved, the WDEF would be called to draw it. The
- WDEF draws the close box relative to the leftmost part of the window, and the grow box relative
- to the rightmost part of the window. If the spotlight completely cut off one side of the titlebar
- -- that is, if it did not divide the titlebar into two smaller pieces -- then what the WDEF saw
- as the "leftmost" or "rightmost" part of the window would not be the right place to draw the
- close box and grow box, and so, in summary, it would look messed up. :-) The way I have it now,
- when PaintBehind() is called, all windows are in their "normal" state, not their "spotlighted"
- state, so everything works correctly.
- */
-
- /*
- void ShowSpotlight(Point where, Boolean override) {
- if (where.h != glob.lightCenter.h || where.v != glob.lightCenter.v) {
- WindowPeek i;
- Boolean anyWindows = false;
- Boolean anyOldWindows = false;
-
- CopyRgn(glob.spotlightRgn, glob.oldlightRgn);
- OffsetRgn(glob.spotlightRgn, where.h - glob.lightCenter.h, where.v - glob.lightCenter.v);
- DiffRgn(glob.spotlightRgn, glob.oldlightRgn, glob.newlightRgn);
- DiffRgn(glob.oldlightRgn, glob.spotlightRgn, glob.oldlightRgn);
-
- for (i = (WindowPeek)LMGetWindowList(); i != nil; i = i->nextWindow) {
- if (i->visible) {
- RestoreWindow(i);
-
- SectRgn(i->strucRgn, glob.oldlightRgn, glob.scratchRgn);
- if (!EmptyRgn(glob.scratchRgn)) anyOldWindows = true;
- }
- }
-
- if (anyOldWindows) {
- CalcVisBehind(LMGetWindowList(), glob.oldlightRgn);
- PaintBehind(LMGetWindowList(), glob.oldlightRgn);
- }
-
- for (i = (WindowPeek)LMGetWindowList(); i != nil; i = i->nextWindow) {
- if (i->visible) {
- SectRgn(i->strucRgn, glob.newlightRgn, glob.scratchRgn);
- if (!EmptyRgn(glob.scratchRgn)) {
- DiffRgn(i->strucRgn, glob.spotlightRgn, i->strucRgn);
- DiffRgn(i->contRgn, glob.spotlightRgn, i->contRgn);
- anyWindows = true;
- }
- }
- }
-
- if (anyWindows) {
- CalcVisBehind(LMGetWindowList(), glob.newlightRgn);
- PaintOne(nil, glob.newlightRgn);
- }
- else if (override) PaintOne(nil, glob.newlightRgn);
-
- glob.lightCenter = where;
- }
- }
- */
-
- void ShowSpotlight(Point where, Boolean override) {
- LayerPtr layer;
- Boolean anyWindowsAtAll = false;
-
- if (where.h == glob.lightCenter.h && where.v == glob.lightCenter.v) return;
-
- CopyRgn(glob.spotlightRgn, glob.oldlightRgn);
- OffsetRgn(glob.spotlightRgn, where.h - glob.lightCenter.h, where.v - glob.lightCenter.v);
- DiffRgn(glob.spotlightRgn, glob.oldlightRgn, glob.newlightRgn);
- DiffRgn(glob.oldlightRgn, glob.spotlightRgn, glob.oldlightRgn);
-
- for (layer = CurrentLayer(); layer != nil; layer = GetNextLayer(layer)) {
- WindowPeek i;
- WindowPtr windowList = ((LayerPeek)layer)->windowList;
- Boolean anyWindows = false;
- Boolean anyOldWindows = false;
-
- if (!LayerVisible(layer)) continue;
-
- for (i = (WindowPeek)windowList; i != nil; i = i->nextWindow) {
- if (!i->visible) continue;
-
- RestoreWindow(i);
-
- SectRgn(i->strucRgn, glob.oldlightRgn, glob.scratchRgn);
- if (!EmptyRgn(glob.scratchRgn)) anyOldWindows = true;
- }
-
- if (anyOldWindows) {
- CalcVisBehind(windowList, glob.oldlightRgn);
- PaintBehind(windowList, glob.oldlightRgn);
- }
-
- for (i = (WindowPeek)windowList; i != nil; i = i->nextWindow) {
- if (!i->visible) continue;
-
- SectRgn(i->strucRgn, glob.newlightRgn, glob.scratchRgn);
- if (!EmptyRgn(glob.scratchRgn)) {
- DiffRgn(i->strucRgn, glob.spotlightRgn, i->strucRgn);
- DiffRgn(i->contRgn, glob.spotlightRgn, i->contRgn);
- anyWindows = anyWindowsAtAll = true;
- }
- }
-
- if (anyWindows) {
- CalcVisBehind(windowList, glob.newlightRgn);
- PaintOne(nil, glob.newlightRgn);
- }
-
- }
-
- //if (anyWindowsAtAll) PaintOne(nil, glob.newlightRgn);
- //else if (override) PaintOne(nil, glob.newlightRgn);
-
- glob.lightCenter = where;
- }
-
- /*
- RestoreWindow tells to WDEF to recalculate the strucRgn and contRgn and so eliminates the hole
- created by the spotlight. Do not try to simplify it -- it *will* crash unless windowDefProc is
- locked and window is the current port.
-
- I used to restore the previous port, but this slowed things down by a fair amount. So, then I
- tried getting the port, restoring all the windows, and *then* restoring the port -- only doing
- it once, instead of once for every window. This ended up messing things up, so by restoring
- the windows the "right" port must have changed. For whatever reason, it seems to work better
- if I *don't* restore the port.
- */
-
- void RestoreWindow(WindowPeek window) {
- char state = HGetState(window->windowDefProc);
- HLock(window->windowDefProc);
-
- SetPort((WindowPtr)window);
- CallWindowDefProc((WindowDefUPP)*window->windowDefProc, GetWVariant((WindowPtr)window),
- (WindowPtr)window, kWindowMsgCalculateShape, nil);
-
- HSetState(window->windowDefProc, state);
- }
-
- Boolean HideAllButFinder(void) {
- ProcessSerialNumber psn;
- LayerPtr layer;
- Boolean activating = false, currentProcess;
-
- GetCurrentProcess(&psn);
-
- for (layer = CurrentLayer(); layer != nil; layer = GetNextLayer(layer)) {
- LayerInfoPtr info = GetLayerInfo(layer);
- if (info->creator == 'MACS' && glob.prefs.switchToFinder) {
- glob.finderPSN = info->layerPSN;
- SetFrontProcess(&glob.finderPSN);
- SameProcess(&glob.finderPSN, &psn, &activating);
- activating = !activating;
- }
- else if (glob.prefs.hideApps) {
- SameProcess(&info->layerPSN, &psn, ¤tProcess);
- if (!glob.prefs.switchToFinder && currentProcess) continue;
-
- HideLayer(layer);
- currentProcess = false;
- }
- }
-
- return activating;
- }