TweetFollow Us on Twitter

Oct 93 Think 10
Volume Number:9
Issue Number:10
Column Tag:Think Top 10

Related Info: Event Manager

Support’s Solutions

By Kevin Irlen, THINK Technical Support, Symantec Corp.

This is a monthly column written by Symantec's Technical Support Engineers intended to provide you with information on Symantec products. This month we cover a single commonly asked question of Symantec’s THINK group.

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

A jGNE Starter Kit for THINK C

[This month’s THINK Top 10 is actually not a list of the top ten questions to Symantec. Instead, it handles a single commonly asked question of the THINK group. What we’d like to know is do you like this type of article, or would you prefer us to return to the Top 10 Q&A style? Send me your thoughts - my editorial e-mail addresses are on page 2 of this issue. - Ed.]

While all the latest and greatest stuff rushes relentlessly at us - C++, AppleScripting, Bedrock, PowerPC, etc. - there remain the numerous loose ends and open questions concerning Macintosh programming that Symantec Tech Support folks get asked for help with every day. Often, dealing with such questions is a straightforward matter of steering a programmer toward the appropriate sections of Inside Macintosh, the odd Tech Note, or to one of the programming forums online. Certain problems seem to fall between the cracks, however; too general to be solved by a couple of Toolbox calls, yet too Macintosh-specific to be readily solved with no clear documentation path to follow. The more often a given problem of this kind crops up, the more it cries out for a generic solution - a tech support solution.

Writing and installing a GetNextEvent filter is one such recurring problem. Essential to the functioning of many extensions, as well as applications, this oddity is mentioned only in a tiny Tech Note, with scant information about how actually to implement it.

From Macintosh Tech Note #85:

GetNextEvent uses a filter (GNE filter) which allows for a routine to be installed which overrides (or augments) the behavior of the system. The GNE filter is installed by pointing the low-memory global jGNEFilter (a long word at $29A) to the routine. After all other GNE processing is complete, the routine will be called with A1 pointing to the event record and D0 containing the boolean result. The filter may then modify the event record or change the function result by altering the word on the stack at 4(A7). This word will match D0 initially, of course.

Sounds good. This is exactly what you’ll need to blink the Apple menu, intercept disk insert events to check disks for viruses, trap special key sequences system-wide, etc. But this is where many a THINK user throws up his or her hands and calls us, wanting to what in the world to do with this information! How do you write such a routine? Do you have to do the whole thing in assembly? How exactly do you install it?

Clearly, given the register-based calling mechanism, “some assembly is required,” and naturally, installation will need to be done by an INIT. But why isn’t there some standard way of doing this, and shouldn’t you be able to write your filter in C? Why ask why? The jGNE Starter Kit, included in full on the companion disk, asks why, and answers by providing a jGNE installer INIT that can be used and re-used simply by replacing resources with ResEdit, along with everything else needed to develop and test custom jGNE’s. In this article, I’ll explain how the INIT works, and what it expects from the ‘CODE’ resource you drop in.

In solving the jGNE problem, there are essentially two hurdles to be cleared, the first relatively low, the second a bit higher. First, we must have a scheme for dealing with the jGNE calling mechanism described in Tech Note 85; and second, we must allow for multiple jGNE’s to coexist, i.e. to be chained. Credit for the snippet of assembly language that follows belongs, as far as I know, to Steve Stockman, a frequent contributor to the Macintosh Programming and Symantec Development Tools forums on CompuServe. It leaps the first hurdle, and alludes to clearing the second. I discovered it while browsing one day, in a message to a programmer in need, and it is what Mr. Stockman left out of his message that inspired me to create this Kit.

Anyway, here’s the only assembly necessary:

/* 1 */

main()
{  
 asm  
 {   
  move.l   A1   ,-(SP)       ; A1    holds theEvent   
  move.w   8(SP),-(SP)       ; 8(SP) holds hasEvent 
 ; (4(SP) + 4 we just pushed)   
  jsr      myjGNEFilterFunc   
  move.w   D0,10(SP)         ; put return value back 
 ; at what will be 4(SP)   
  addq.l   #2,SP             ; pop hasEvent     (10 - 2 = 8)
  move.l   (SP)+,A1          ; pop theEvent back into A1 
 ; ( 8 - 4 = 4)
  movea.l  #0xFFFFFFFF,A0    ; this gets overwritten at 
 ; INIT time with either the   
  jmp      (A0)              ; next jGNE's address, or an 
 ; RTS if there isn't one!  
 }
}

There are two essential things to note about this code. First, the GetNextEvent call mechanism has been kept completely separate from the filter function itself, which may now be a simple C function of the form:

/* 2 */
            
short myjGNEFilterFunc(short hasEvent,EventRecord *anEvent);  

This function must return zero if it eats the event, non-zero if the event still needs to be handled.

The second feature of this code is something that no doubt looks a bit strange. As the comment indicates: the movea.l line is not in its final form. It is going to be tampered with at INIT time! The reason for this is that you inevitably want to be able to install multiple jGNE’s, and have them chain from one to the next. The problem is that a jGNE has no way of knowing in advance whether or not it will be last in the chain; and therefore no way of knowing whether to RTS when finished (allowing GetNextEvent to return to the current application) or to JMP to the next jGNE in line. This order isn’t determined until INIT time. A jGNE could be “loaded” simply by floating its code in the system heap and storing a pointer to it in the low-memory global jGNEFilter (0x29A). But in order for multiple jGNE’s to coexist, they must cooperate at INIT time by checking to see whether or not someone has loaded before them - and if so, store the previous jGNE’s address to jump to instead of simply returning. In this way, jGNE’s can chain in last-to-load, first-to-execute order.

Now if you just want to use the jGNE Starter Kit, you don’t have to worry about any of this. All you have to do is use the above code in conjunction with the generic jGNE INIT. The latter part of this article contains a brief discussion on building your jGNE project, which should be more than enough to get you “started.” For those of you in the mood for a hack, read on!

There are any number of strategies for stashing a “special” value so that a piece of code can find it later, but the jGNE Starter Kit’s strategy here is to go to the extreme and actually overwrite the machine instruction that depends on the value of the previously loaded jGNE, once, at INIT time, and then not to worry about it again. No finding the stashed value, or testing it, or nuthin. Here’s the entire code for the INIT (excluding the sacred ShowInitIcon routine), and an explanation to follow:

/* 3 */

#define rMyINITIcon  -4094     // ID of icon to be shown 
 // at INIT time
#define rMyjGNE        128     // ID of jGNE 'CODE' resource

#define kBRAoffset  0x0018     // offset of bra to main in a 
 // std header CODE resource
#define kLoadOffset 0x0014     // offset in "main" of the 
 // line to be changed!

Ptr jGNEFilterPtr : 0x029A;   // low-memory global: 
 // jGNEfilter

OSErr ShowInitIcon(short icon_num,short move_x_by);

main()
{
 Handle  myjGNE;
 Ptr     branInstruction,loadInstruction;
 OSErr   iErr;

 SetZone(SysZone);

 myjGNE = Get1Resource('CODE',rMyjGNE);

 if (myjGNE)
 {
  DetachResource(myjGNE);

  branInstruction = *myjGNE         + kBRAoffset;
  loadInstruction = branInstruction + *((short *) 
 (branInstruction + 2)) +kLoadOffset;

  if (jGNEFilterPtr) *((long *) (loadInstruction + 2)) = 
 (long) jGNEFilterPtr;
  else               *loadInstruction = 0x4E75;

  jGNEFilterPtr = *myjGNE;
 }
 else SysBeep(10);

 iErr = ShowInitIcon(rMyINITIcon,-1);

 if (iErr != noErr) SysBeep(10);
}

OK, this is code may be short, but it does have a couple of rather obscure lines, and a couple of unfamiliar constants. We are attempting to locate in memory, within the loaded ‘CODE’ resource that is our jGNE, the exact word where the “movea.l” instruction begins, and this depends on two things. First, since our assembly snippet comprises the main() function of the jGNE resource, one key constant is the offset of the “movea.l” instruction from the beginning of main(). Disassembly (crude, but effective) reveals that this offset is 20 words (0x0014), and we’ll name the constant kLoadOffset since it’s the instruction that loads the next jGNE address to be jumped to.

This is only halfway toward locating the instruction, though, because where, after all, is main()? The answer is that in a code resource, main() can wind up anywhere. Fortunately, the entry point to a THINK C code resource is a standard header (unless you make it otherwise), and this standard header contains an BRA.S instruction whose sole purpose is to branch to main(). Examine a code resource (using MacsBug, or a code editor), and you will see that this branch instruction is 24 words (0x0018) from the beginning (segment loader info and other no-op stuff). Hence the constant kBRAOffset.

So here’s the deal: load the code resource, dereference the handle, add kBRAOffset to its value, and you’re pointing at the BRA.S instruction. The second word of that instruction is the number of bytes to branch over to get to main(). So add kLoadOffset to that and you’re pointing at your load instruction:

/* 4 */

branInstruction = *myjGNE         + kBRAoffset;
loadInstruction = branInstruction + *((short *) 
 (branInstruction + 2)) + kLoadOffset;

Now we’re home free. All we do now is decide whether to change the second word of that load instruction to the address of the next jGNE, or just replace the instruction with an RTS (0x4E75):

/* 5 */

if (jGNEFilterPtr) *((long *) (loadInstruction + 2)) = 
 (long) jGNEFilterPtr;
else               *loadInstruction = 0x4E75;

Now we can safely replace whatever was in jGNEFilterPtr with a pointer to our jGNE filter, and chaining will take care of itself (assuming the other jGNE’s you’ve installed are just as polite).

That’s all there is to it. Although this technique may be a bit unorthodox, it provides an extremely high degree of modularity to an aspect of Mac programming that is usually shrouded in mystery - one of those tasks that is more often abandoned than solved, or else solved so painfully that one shudders at the thought of having to go through it again.

An additional windfall is that this installation procedure does not in any way depend upon being executed at INIT time. It can be run from a regular THINK C project, allowing you to install a jGNE filter on the fly, as you’re developing it. If you install a faulty filter, removing it is as easy as restoring the previous value to the low-memory global jGNEFilter. Just be sure to note the value of jGNEFilter before you do your install, then, in MacsBug for example, it’s just “SM jGNEFilter oldval” to restore it. Of course the detached resource will just lay there in your system heap, but why worry?

To wrap things up, let’s look briefly at the process of writing the jGNE filter itself. The Starter Kit comes with a generic jGNE code resource project that contains a single file, “generic jGNE.c”. If you build this project, you’ll get a resource of type ‘CODE’ and id 128 that can be dropped into the generic jGNE INIT with ResEdit (in fact, it’s already been put there, but you can do it again just to see how fun and easy it is). Once installed, this filter demonstrates the awesome power of the jGNE mechanism by beeping whenever the user types cmd-shift j, g, n, or e (author is not responsible if this interferes with the proper functioning of any other applications). Your filter will hopefully do something more useful, but the basic structure will be essentially the same. Here’s the code:

/* 6 */

main()
{  
 asm  
 {   
  move.l   A1   ,-(SP)       ; A1    holds theEvent   
  move.w   8(SP),-(SP)       ; 8(SP) holds hasEvent 
 ; (4(SP) + 4 we just pushed)   
  jsr      myjGNEFilterFunc   
  move.w   D0,10(SP)         ; put return value back at 
 ; what will be 4(SP)   
  addq.l   #2,SP             ; pop hasEvent   
  move.l   (SP)+,A1          ; pop theEvent back into A1   
  movea.l  #0xFFFFFFFF,A0    ; this gets overwritten at INIT 
 ; time with either the   
  jmp      (A0)              ; next jGNE's address, or an 
 ; RTS if there isn't one!  
 }
}

#include <SetUpA4.h>         // DO NOT move this before main! 
 // (It generates code.)

short myjGNEFilterFunc(short hasEvent,EventRecord *theEvent)
{
 char theChar;

 RememberA0(); SetUpA4();

 if (hasEvent)
 {
  switch (theEvent->what)
  {
   case keyDown:
   case autoKey:
    theChar = theEvent->message & charCodeMask;

    if  ((theEvent->modifiers & cmdKey) && 
 (theEvent->modifiers & shiftKey))
     if ((theChar == 'j') || (theChar == 'g') ||
         (theChar == 'n') || (theChar == 'e'))
     {
      SysBeep(10);

      hasEvent = 0;
     }

   default:
    break;
  }
 }
 RestoreA4();

 return hasEvent;
}

Just your basic event-handling switch block, plus RememberA0, SetupA4, and RestoreA4 to allow for global and static variables. Of course, you can handle any events you like and in as sophisticated a manner as you’d like, with additional function calls, multiple source files and segments if necessary, MacTraps, ANSI-A4, etc.

With the jGNE Starter Kit, you’ll have that Apple menu blinking in no time!

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

OmniGraffle Pro 7.19.3 - Create diagrams...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
OmniGraffle 7.19.3 - Create diagrams, fl...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
Hopper Disassembler 5.3.3- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
calibre 5.35.0 - Complete e-book library...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Sound Studio 4.10.0 - Robust audio recor...
Sound Studio lets you easily record and professionally edit audio on your Mac. Easily rip vinyls and digitize cassette tapes, or record lectures and voice memos. Prepare for live shows with live... Read more
Sparkle Pro 4.0 - Visual website creator...
Sparkle Pro will change your mind if you thought building websites wasn't for you. Sparkle is the intuitive site builder that lets you create sites for your online portfolio, team or band pages, or... Read more
Dropbox 140.4.1951 - Cloud backup and sy...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
FotoMagico 6.0.5 - Powerful slideshow cr...
FotoMagico lets you create professional slideshows from your photos and music with just a few, simple mouse clicks. It sports a very clean and intuitive yet powerful user interface. High image... Read more
Remotix 6.4.2 - Access all your computer...
Remotix is a fast and powerful application to easily access multiple Macs (and PCs) from your own Mac. Features: Complete Apple Screen Sharing support - including Mac OS X login, clipboard... Read more
Microsoft Office 365, 2019 16.57 - Popul...
Microsoft Office 365. The essentials to get it all done. Unmistakably Office, designed for Mac Get started quickly with new, modern versions of Word, Excel, PowerPoint, Outlook and OneNote-... Read more

Latest Forum Discussions

See All

A House Full of Covid – The TouchArcade...
It’s been a rough week as both of our young children tested positive for Covid, and since recording this early on Friday my wife has tested positive now too. Thankfully the kids seemed to recover fairly quickly and are mostly back to normal, and I... | Read more »
TouchArcade Game of the Week: ‘Krispee S...
Krispee Street is a new hidden object game from Frosty Pop that is based on their popular and almost painfully sweet webcomic Krispee. This is one of the latest titles to be added to the Netflix Games catalog, which means you’ll need to log into... | Read more »
SwitchArcade Round-Up: ‘Escape Lala’, ‘B...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 21st, 2022. In today’s article, we’ve got a lot of new releases. A lot. There were eight on the schedule when I went to bed last night. There were twenty-four when I woke up... | Read more »
Beta Testers Needed for Huge Version 2.0...
Ya’ll remember Dungeon Raid, right? The phenomenal matching RPG hybrid that launched on mobile more than a decade ago, but was more or less abandoned by its developer only to die a slow death on the App Store before the 32-bit Appocalypse finally... | Read more »
‘Ark Legends’ Gives Players a Chance to...
It’s Airpods and Amazon gift cards galore as Melting Games opens pre-registration for Ark Legends. The upcoming mobile RPG is giving away tons of in-game goodies such as gold, energy, iron core, hero summon chest and rare iron core to players who... | Read more »
‘Nickelodeon Extreme Tennis’ Out Now on...
Nickelodeon Extreme Tennis () from Old Skull Games and Nickelodeon is this week’s new Apple Arcade release. Nickelodeon Extreme Tennis features characters from old and new Nickelodeon shows including SpongeBob, TMNT, and many more. The tennis game... | Read more »
SwitchArcade Round-Up: ‘RPGolf Legends’,...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 20th, 2022. In today’s article, we’ve got a massive amount of new releases to check out. We’ve got summaries of all of them, from heaven to hell. We also have the lists of... | Read more »
‘Zed Blade ACA NEOGEO’ Review – Well, It...
SNK’s NEOGEO platform played host to a great many classics, both famous and under-the-radar. The Metal Slug games. The King of Fighters series. Magician Lord. Shock Troopers. Sengoku 3. NEO Turf Masters. Fatal Fury. Samurai Shodown. Twinkle Star... | Read more »
‘Inua – A Story in Ice and Time’ is a Un...
One thing I know about ARTE from their output on mobile over the years is that they love collaborating with really interesting and unique studios to put out really interesting and unique gaming experiences. This is true yet again with the latest... | Read more »
Out Now: ‘Angry Birds Journey’, ‘RPG Dic...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »

Price Scanner via MacPrices.net

Amazon lowers prices on select 13″ M1 MacBook...
Amazon has select Apple 13″ M1 MacBook Airs on sale for $150 off MSRP this weekend, starting at only $849. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so... Read more
Apple has 13″ M1 MacBook Airs back in stock s...
Apple has restocked a full line of 13″ M1 MacBook Airs, Certified Refurbished, starting at only $849 and up to $190 off original MSRP. These are the cheapest M1-powered MacBooks for sale today at... Read more
In stock and on sale! 16″ 10-Core M1 Pro MacB...
Amazon has new 16″ 10-Core/512GB M1 Pro MacBook Pros in stock today and on sale for $50 off MSRP including free shipping. Their prices are the lowest available for new M1 Pro 16″ MacBook Pro from any... Read more
Deal Alert!: 14″ M1 Pro with 10-Core CPU in s...
Amazon has the new 14″ M1 Pro MacBook Pro with a 10-Core CPU and 16-Core GPU in stock today and on sale for $2299.99 including free shipping. Their price is $200 off Apple’s standard MSRP, and it’s... Read more
Apple has 24-inch M1 iMacs (8-Core CPU/8-Core...
Apple has restocked a wide array of 24-inch M1 iMacs with 8-Core CPUs and 8-Core GPUs in their Certified Refurbished store. Models are available starting at only $1269 and range up to $260 off... Read more
Select 24″ M1 iMacs are on sale for $100 off...
Sales of Apple’s new 24″ M1 iMacs have been rare since its introduction, perhaps due to global supply issues. However, B&H is offering a $100 discount on select 24″ iMacs, and they’re in stock... Read more
M1 Mac minis are back in stock today at Apple...
Apple has M1-powered Mac minis available in their Certified Refurbished section starting at only $589 and up to $140 off MSRP. Each mini comes with Apple’s one-year warranty, and shipping is free: –... Read more
B&H has M1-powered Mac minis on sale for...
B&H Photo has Apple’s Mac minis with M1 Apple Silicon CPUs in stock today and on sale for $50-$100 off MSRP, starting at $649. Free 1-2 shipping is free to many US addresses. Their prices are... Read more
New Amazon sale: Apple’s 13″ M1 MacBook Airs...
Amazon has Apple 13″ M1 MacBook Airs on sale for $100 off MSRP, starting at only $899. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so check their site... Read more
Get an Apple Watch Series 7 for $50 off MSRP,...
Amazon has Apple Watch Series 7 models on sale for $50 off MSRP including free shipping. Their prices are the lowest available for Apple Watch Series 7 models today: – 41mm Apple Watch Series 7 GPS... Read more

Jobs Board

Registered Nurse (RN) Employee Health PSJH -...
…is calling for a Registered Nurse (RN) Employee Health PSJH to our location in Apple Valley, CA.** We are seeking a Registered Nurse (RN) Employee Health PSJH to be Read more
Systems Administrator - Pearson (United State...
…and troubleshoot Windows operating systems (workstation and server), laptop computers, Apple iPads, Chromebooks and printers** + **Administer and troubleshoot all Read more
IT Assistant Level 1- IT Desktop Support Anal...
…providing tier-1 or better IT help desk support in a large Windows and Apple environment * Experience using IT Service Desk Management Software * Knowledge of IT Read more
Human Resources Business Partner PSJH - Provi...
…**is calling a** **Human Resources Business Partner, PSJH** **to our location in Apple Valley, CA.** **Applicants that meet qualifications will receive a text with Read more
Manager Community Health Investment Programs...
…is calling a Manager Community Health Investment Programs PSJH to our location in Apple Valley, CA.** **Qualified candidates will be invited to do a self-paced video Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.