mikeoro@hubcap.UUCP (Michael K O'Rourke) (02/13/89)
I have a question about VBL Tasks. I looked in IM-2 and read about using VInstall and i understand about setting up a VBLTask record. However, what if the procedure I install needs to be called only when a certain key combo is hit? Does it have to scan the event queue or something? How can it be called only when this key combo is hit? I am assuming I must install it as a VBL Task because I want it running all the time in the background. Also, what attributes must I set on the CODE? I would assume system heap and locked. Is that correct? Thanx, Michael O'Rourke
sl161022@silver.bacs.indiana.edu (02/13/89)
"VInstall and i understand about setting up a VBLTask record. However, what "if the procedure I install needs to be called only when a certain key combo "is hit? Does it have to scan the event queue or something? "I want it running all the time in the background. Actually, another way to do this (and the way I use to do a similar thing) is to install a patch for _GetOSEvent, which is called quite frequently. In fact, whenever the Finder, the running application, or any DA is ready to respond to an event, it will call either _GetNextEvent (which goes through the Toolbox Event Manager) or _GetOSEvent (which goes directly through the Operating System.) Since _GetNextEvent calls _GetOSEvent, you can patch _GetOSEvent and you'll be essentially guaranteed access to any keypress, and you can then do whatever you want. Of course, I do program in assembly language, where one has direct control of the registers. (_GetOSEvent passes and returns parameters in A0 and D0.) I am not certain how a high level language gets access to the same parameters. Anyway, in assembly language, it looks something like. . . ; the initial code to install your background routine: push.l A4 ;we'll be using this, so save it push.l #'blah' ;fetch the background routine push.w #0 ;(push = move ?,-(SP)) _GetResource ;resource handle still on stack move.l (SP),A4 ;let A4 point to our routine, and move.l (A4),A4 ; detach resource, just to keep it safe _DetachResource move.w #$A031,D0 ;$A031 is the trap code of _GetOSEvent _GetTrapAddress ;A0 <- address of "old" GetOSEvent routine move.l A0,OldGetOS(?) ;save this address, we'll need it later move.w #$A031,D0 ;and now we'll redirect _GetOSEvent to move.l A4,A0 ; my replacement routine _SetTrapAddress ;ta da pop.l A4 ;restore old value of A4 . . . ; the replacement routine, which is a resource ('blah' #0) with its ; resSysHeap and resLocked attributes set (important!!) Beginning push.l A0 ;we'll need pointer to event record later move.l OldGetOS(?),A1 ;call original routine (it's as if we're jsr (A1) ; not even here) pop.l A0 ;we now have access to event push.l D0 ;better save D0, caller is expecting it . . . ;do whatever you want based on the event pop.l D0 ;restore D0 rts The chore of translating this into your language of choice is up to you, but I think this will accomplish what you're after. __________________________________________________________________ "May the forces of evil become confused on the way to your house." Sincerely, -- George Carlin Phaedrus (aka Colin Klipsch) sl161022@silver.bacs.indiana.edu Indiana University at Bloomington
morourk@hubcap.UUCP (michael orourke) (02/14/89)
In article <99000007@silver>, sl161022@silver.bacs.indiana.edu writes: > > "VInstall and i understand about setting up a VBLTask record. However, what > "if the procedure I install needs to be called only when a certain key combo > "is hit? Does it have to scan the event queue or something? > > "I want it running all the time in the background. > > Actually, another way to do this (and the way I use to do a similar thing) > is to install a patch for _GetOSEvent, which is called quite frequently. > In fact, whenever the Finder, the running application, or any DA is ready > to respond to an event, it will call either _GetNextEvent (which goes > through the Toolbox Event Manager) or _GetOSEvent (which goes directly > through the Operating System.) Since _GetNextEvent calls _GetOSEvent, > you can patch _GetOSEvent and you'll be essentially guaranteed access > to any keypress, and you can then do whatever you want. Sounds great. Only one problem. There followed after this response a lot of assembly language code examples. But I don't know assembly language so I couldntmake heads or tail of what to do. How do I install a patch to a routine like GetOSEvent? Michael O'Rourke
dwb@Apple.COM (David W. Berry) (02/14/89)
In article <4418@hubcap.UUCP> morourk@hubcap.UUCP (michael orourke) writes: >In article <99000007@silver>, sl161022@silver.bacs.indiana.edu writes: >> >> "VInstall and i understand about setting up a VBLTask record. However, what >> "if the procedure I install needs to be called only when a certain key combo >> "is hit? Does it have to scan the event queue or something? >> >> "I want it running all the time in the background. >> >> Actually, another way to do this (and the way I use to do a similar thing) >> is to install a patch for _GetOSEvent, which is called quite frequently. >> In fact, whenever the Finder, the running application, or any DA is ready >> to respond to an event, it will call either _GetNextEvent (which goes >> through the Toolbox Event Manager) or _GetOSEvent (which goes directly >> through the Operating System.) Since _GetNextEvent calls _GetOSEvent, >> you can patch _GetOSEvent and you'll be essentially guaranteed access >> to any keypress, and you can then do whatever you want. > >Sounds great. Only one problem. There followed after this response a lot of >assembly language code examples. But I don't know assembly language so I couldntmake heads or tail of what to do. > >How do I install a patch to a routine like GetOSEvent? By taking your life in your hands. Routines like GetOSEvent, GetNextEvent, etc should be patched only if absolutely necessary. In this case it isn't, Apple has thoughtfully provided a hook through which all events are filtered. What you want to do can be done by installing a pointer to a routine in jGNEFilter (0x????). For more details see Tech Note #85. > >Michael O'Rourke Opinions: MINE, ALL MINE! (greedy evil chuckle) David W. Berry (A/UX Toolbox Engineer) apple!dwb@sun.com dwb@apple.com 973-5168@408.MaBell
alexis@ccnysci.UUCP (Alexis Rosen) (02/17/89)
There is no way to do this patch stuff without access to registers, which in practice means assembler (you could do in-line asm from pascal, but that's the same thing). If you intend to muck around on this low level, though, you have to at least be able to read assembler. It's really not all that difficult (though coding significant programs in it ain't my idea of fun). If you're not willing, give it up. You won't be able to do it, I think. Anyway, the amount of assembly required is truly minimal. The assembly code posted for patching GetOSEvent is great except for one little problem- it is a tail patch. Tail patches are expressly forbidden, and this routine seems more likely than most to be susceptible to major trouble. On the other hand, my little scheme for legal tail patches would probably make it work. Another problem with this is that it might not work with apps which call WaitNextEvent (I don't know if WNE calls GetOSEvent, but I sort of doubt it). In any event (:-) there is no need to patch the trap. There is a hook already in place. I think it is a low-mem global called jGNEFilter or some such which takes a pointer to a routine. Check IM for more info. There has also been a tech note or two on the subject. I seem to recall that apple now discourages all use of this hook in the latest relevant technote, but that no alternatives are offered. Given this, I would say that the legal tail patch is the most future- compatible way of doing things, but that it may well impair performance (doing four traps + misc. stuff for every GetOSEvent). jGNEFilter will probably stick around for awhile (at least within an app's multifinder partition) so it's probably safe to use it for now. On the other hand, who the hell knows? Not me, that's for damn sure... Alexis Rosen alexis@ccnysci.uucp
cheshire@neon.Stanford.EDU (Stuart David Cheshire) (05/13/91)
Quick question / plea for help: Does anybody know how to stop Multifinder turning off my VBL task when my application is switched to the background? The technical notes proudly explain this wonderful feature of Multifinder which means you don't have to set up A5 to point to your globals -- but I am using the VBL task for timing and I do set up A5 correctly and I don't want it to slow down whenever my app is in the background (major OR minor context switches have this effect, so even when the app is 'in the fore- ground' the speed is erratic). I tried doing as they say and installing the vbl task in the system heap which is supposed to stop this, but it doesn't seem to work: task = (ex_VBLTask*) NewPtrSysClear(sizeof(ex_VBLTask)); task->task.qType = vType; task->task.vblAddr = (ProcPtr)Tick; task->task.vblCount = 1; task->regA5 = Application_A5; VInstall(task); Please e-mail respones to cheshire@cs.stanford.edu
jmunkki@hila.hut.fi (Juri Munkki) (05/13/91)
In article <1991May13.014725.5238@neon.Stanford.EDU> cheshire@neon.Stanford.EDU (Stuart David Cheshire) writes: >Does anybody know how to stop Multifinder turning off my VBL task when my >application is switched to the background? > >I tried doing as they say and installing the vbl task in the system heap >which is supposed to stop this, but it doesn't seem to work: You got it almost right. The task procedure has to be in the system heap, the queue entry can be anywhere. I use a resource with the system heap bit set and a single JMP instruction that I patch to point to my code in the application. ____________________________________________________________________________ / Juri Munkki / Helsinki University of Technology / Wind / Project / / jmunkki@hut.fi / Computing Center Macintosh Support / Surf / STORM / ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cheshire@neon.Stanford.EDU (Stuart David Cheshire) (05/15/91)
Many thanks to everyone who responded to my question. The answer is that the vblAddr has to point into the System heap AS WELL as the queue element itself residing in the system heap. I found an easy way to put a piece of code in the system heap which calls my routine. It may look dangerous, but it seems to work as long as you are not stupid and deliberately call UnLoad Seg to throw away the code it is trying to call. Sample code follows: /* define a structure which is a VBLTask queue element with four bytes to stash A5, our application global pointer, appended to the end */ typedef struct { VBLTask task; long *regA5; } ex_VBLTask; local ex_VBLTask *task_qelem; /* define a 'jump instruction' structure to put on the system heap. It is six bytes -- a two byte opcode and a four byte absolute address. The code that address points to must already be in memory and we must not call UnLoadSeg or it will vanish under us. */ typedef struct { short jmp_opcode; void (*codeptr)(void); } jumpcode; local jumpcode *task_code; /* code starts here*/ { /* allocate structures in System Heap */ task_qelem = (ex_VBLTask*) NewPtrSysClear(sizeof(ex_VBLTask)); task_code = (jumpcode *) NewPtrSysClear(sizeof(jumpcode)); /* fill in opcode for jump and the address to jump to */ task_code->jmp_opcode = 0x4EF9; task_code->codeptr = &MacTick; /* set up the queue element and install it */ task_qelem->task.qType = vType; task_qelem->task.vblAddr = (ProcPtr)task_code; task_qelem->task.vblCount = 1; task_qelem->regA5 = Application_A5; VInstall(task_qelem); } local void MacTick(void) { asm { move.l a5, -(sp) move.l OFFSET(ex_VBLTask,regA5)(a0), a5 } /* do stuff here. Globals may be accessed if required. */ asm { move.l (sp)+, a5 } }
ech@cbnewsk.att.com (ned.horvath) (05/15/91)
From article <1991May15.081149.4579@neon.Stanford.EDU>, by cheshire@neon.Stanford.EDU (Stuart David Cheshire): > Sample code follows: > > /* define a structure which is a VBLTask queue element with four bytes to > stash A5, our application global pointer, appended to the end */ > > typedef struct { VBLTask task; long *regA5; } ex_VBLTask; > local ex_VBLTask *task_qelem; > > /* define a 'jump instruction' structure to put on the system heap. It is > six bytes -- a two byte opcode and a four byte absolute address. The code > that address points to must already be in memory and we must not call > UnLoadSeg or it will vanish under us. */ > > typedef struct { short jmp_opcode; void (*codeptr)(void); } jumpcode; > local jumpcode *task_code; > > /* code starts here*/ > > { > /* allocate structures in System Heap */ > task_qelem = (ex_VBLTask*) NewPtrSysClear(sizeof(ex_VBLTask)); > task_code = (jumpcode *) NewPtrSysClear(sizeof(jumpcode)); > > /* fill in opcode for jump and the address to jump to */ > task_code->jmp_opcode = 0x4EF9; > task_code->codeptr = &MacTick; > > /* set up the queue element and install it */ > task_qelem->task.qType = vType; > task_qelem->task.vblAddr = (ProcPtr)task_code; > task_qelem->task.vblCount = 1; > task_qelem->regA5 = Application_A5; > VInstall(task_qelem); > } > > local void MacTick(void) > { > asm { move.l a5, -(sp) > move.l OFFSET(ex_VBLTask,regA5)(a0), a5 > } > > /* do stuff here. Globals may be accessed if required. */ > > asm { move.l (sp)+, a5 } > } Perhaps you didn't receive my email on this subject. While your code will work, and work correctly, it will create problems on a Mac running under VM: there is no guarantee that your MacTick() routine, or your below-A5 (globals) area will be in memory. If they aren't, you are going to page-swap at VBL-interrupt time (not cool). I haven't given the VM section of IM-VI a careful reading, but I'd guess that what you propose will WORK. However, it's quite possible that the jerkiness it introduces will be perceptible to the user... A way to avoid the problem is to check for VM enabled (using Gestalt) and make the appropriate locking calls to keep your routine and the vars it needs in memory. Another way is to load the MacTick code, and the variables it needs, into the System heap (which doesn't swap). Your app, since it does the allocation, can continue to access the vars needed by the VBL task. Finally, as always with a VBL task, remember that you are running at interrupt time: all the memory-moving traps are off limits, and you should do the minimum possible processing in the VBL itself. -- =Ned Horvath= ehorvath@attmail.com