[comp.sys.mac.programmer] best way to detect command-. interrupt?

science@nems.dt.navy.mil (Mark Zimmermann) (12/27/89)

Help?!  What is the 'officially approved' way to sense a command-period
(cmd-.) so that a user can interrupt a long operation from the keyboard?
I would like something that doesn't slow down my inner loop too much,
and that would work on all Macs.  A code fragment in any language would
be appreciated -- I'll be working in Think C 4.0 on code segments (for
HyperCard XFCNs), specifically.  Many thanks, and Happy New Year!  ^z

oster@dewey.soe.berkeley.edu (David Phillip Oster) (12/29/89)

In article <694@nems.dt.navy.mil> science@nems.dt.navy.mil (Mark Zimmermann) writes:
>What is the 'officially approved' way to sense a command-period
>(cmd-.) so that a user can interrupt a long operation from the keyboard?

The best way is to run your event handler, so that other applications can
run, and your other windows will update while you are calculating.

Assuming you don't wish to do _that_, many people do a GetKeys(), and look
for the command and period in the resulting binary key map. This technique
may have internationalization problems, so make sure to put the bits you
are checking into a resource and let your tech support people know how to
customize it.


--- David Phillip Oster            --  No, I come from Boston. I just work
Arpa: oster@dewey.soe.berkeley.edu --  in cyberspace.
Uucp: {uwvax,decvax}!ucbvax!oster%dewey.soe.berkeley.edu

tim@hoptoad.uucp (Tim Maroney) (12/29/89)

In article <694@nems.dt.navy.mil> science@nems.dt.navy.mil (Mark Zimmermann)
writes:
>>What is the 'officially approved' way to sense a command-period
>>(cmd-.) so that a user can interrupt a long operation from the keyboard?

In article <33392@ucbvax.BERKELEY.EDU> oster@dewey.soe.berkeley.edu.UUCP
(David Phillip Oster) writes:
>The best way is to run your event handler, so that other applications can
>run, and your other windows will update while you are calculating.

Granted, but you're likely to make a long operation even longer.  Also,
the standard event handler is probably not appropriate; you want to mask
out most mouse and keyboard operations.  But since you're checking the
keyboard, you have to throw away keyboard events (except Cmd-Period).
This zaps typeahead, which is probably not what people will expect.

>Assuming you don't wish to do _that_, many people do a GetKeys(), and look
>for the command and period in the resulting binary key map. This technique
>may have internationalization problems, so make sure to put the bits you
>are checking into a resource and let your tech support people know how to
>customize it.

It's worse than that.  You have to call the GetKeys when the user is
depressing the key.  This means you'd better be calling it very very
very frequently.  Again, your long operation is likely to get longer.

I'm not really sure that there is any good way, short of patching the
keyboard driver -- and even that would be an application-specific
patch, so if you *are* giving other applications time, it could fail to
be invoked when the actual keypress happens.  I suppose you could insert
a front-end to the driver into the unit table, but that seems like a
big mess for such a small operation.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

FROM THE FOOL FILE:
"The men promise to provide unconditionally for their wives.  The women in turn
 serve unconditionally to provide the other household services necessary for the
 men to fulfill their obligations to the women.  The women are satisfied because
 they have the men working for THEM." -- Colin Jenkins, soc.women

siegel@endor.harvard.edu (Rich Siegel) (12/29/89)

In article <9421@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:

>>The best way is to run your event handler, so that other applications can
>>run, and your other windows will update while you are calculating.
>
>Granted, but you're likely to make a long operation even longer.  Also,
>the standard event handler is probably not appropriate; you want to mask
>out most mouse and keyboard operations.  But since you're checking the
>keyboard, you have to throw away keyboard events (except Cmd-Period).
>This zaps typeahead, which is probably not what people will expect.
>
>>Assuming you don't wish to do _that_, many people do a GetKeys(), and look
>>for the command and period in the resulting binary key map. This technique
>>may have internationalization problems, so make sure to put the bits you
>>are checking into a resource and let your tech support people know how to
>>customize it.
>
>It's worse than that.  You have to call the GetKeys when the user is
>depressing the key.  This means you'd better be calling it very very
>very frequently.  Again, your long operation is likely to get longer.
>
>I'm not really sure that there is any good way, short of patching the
>keyboard driver -- and even that would be an application-specific
>patch, so if you *are* giving other applications time, it could fail to
>be invoked when the actual keypress happens.  I suppose you could insert
>a front-end to the driver into the unit table, but that seems like a
>big mess for such a small operation.

	You're getting warmer...

	If you wanted to get *really* skanky, you could bottleneck _PostEvent,
and set a global flag subject to whether the event was for a fan-period.
This involves saving the application's A5 in a safe place, since _PostEvent
can get called at interrupt time, and you will probably need to test KeyMap
to look for the Command key...

	Then in whatever operation you'd want to check, just test your global
flag each time through the loop, or at selective points during the computation.
It's pretty cheap to do all of this, and can be used in places where you
don't want to slow down by calling _GetNextEvent or _GetOSEvent...

R.

~~~~~~~~~~~~~~~
 Rich Siegel
 Staff Software Developer
 Symantec Corporation, Language Products Group
 Internet: siegel@endor.harvard.edu
 UUCP: ..harvard!endor!siegel

"When someone who makes four hundred and fifty dollars an hour wants to
tell you something for free, it's a good idea to listen."

~~~~~~~~~~~~~~~

freek@fwi.uva.nl (Freek Wiedijk) (12/29/89)

In article <9421@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
 >I'm not really sure that there is any good way, short of patching the
 >keyboard driver -- and even that would be an application-specific
 >patch, so if you *are* giving other applications time, it could fail to
 >be invoked when the actual keypress happens.  I suppose you could insert
 >a front-end to the driver into the unit table, but that seems like a
 >big mess for such a small operation.

Isn't it possible to patch PostEvent, to make it set a flag somewhere,
and have your "calculation" test this flag often?

Someone told me that this is what Mathematica does.

--
Freek "the Pistol Major" Wiedijk                  Path: uunet!fwi.uva.nl!freek
#P:+/ = #+/P?*+/ = i<<*+/P?*+/ = +/i<<**P?*+/ = +/(i<<*P?)*+/ = +/+/(i<<*P?)**

beard@ux1.lbl.gov (Patrick C Beard) (12/29/89)

In article <9421@hoptoad.uucp> tim@hoptoad.UUCP (Tim Maroney) writes:
>In article <694@nems.dt.navy.mil> science@nems.dt.navy.mil (Mark Zimmermann)
>writes:
>>>What is the 'officially approved' way to sense a command-period
>>>(cmd-.) so that a user can interrupt a long operation from the keyboard?
...
[discussion of various ways that degrade performance]
...
>
>I'm not really sure that there is any good way, short of patching the
>keyboard driver -- and even that would be an application-specific
>patch, so if you *are* giving other applications time, it could fail to
>be invoked when the actual keypress happens.  I suppose you could insert
>a front-end to the driver into the unit table, but that seems like a
>big mess for such a small operation.

How about a patch to PostEvent?  That way, when the keypress occurs the
patch can set an application global to take note of it, and all that is 
required in the inner loop of the application is polling that global variable.
This provides both the friendliness and the performance.

Another point, events such as keypresses and mouse clicks are only delivered
to the application in the front layer in MultiFinder.  Therefore, the
application applied patch to PostEvent would only see cmd-. if that
application is in front.  MultiFinder, as well, unpatches application
applied patches when context switching so you wouldn't interrupt the 
application if you happened to be in another layer.



-------------------------------------------------------------------------------
-  Patrick Beard, Macintosh Programmer                        (beard@lbl.gov) -
-  Berkeley Systems, Inc.  ".......<dead air>.......Good day!" - Paul Harvey  -
-------------------------------------------------------------------------------

tim@hoptoad.uucp (Tim Maroney) (12/30/89)

In article <3506@husc6.harvard.edu> siegel@endor.UUCP (Rich Siegel) writes:
>	If you wanted to get *really* skanky, you could bottleneck _PostEvent,
>and set a global flag subject to whether the event was for a fan-period.
>This involves saving the application's A5 in a safe place, since _PostEvent
>can get called at interrupt time, and you will probably need to test KeyMap
>to look for the Command key...

I pointed out the problem with this in the article you quoted.  If you
are giving time to other applications, then the PostEvent call at
interrupt time may get called in another application's context,
bypassing your application-specific patch.  Due to the way the Event
Manager works, you will still get the keyboard event, but PostEvent may
not be called in your application context. So you can drop Command-Dots
with this method too.  If all applications get equal time, then the
probability of correctly intercepting a Cmd-Dot is the reciprocal of
the number of applications running.

>	Then in whatever operation you'd want to check, just test your global
>flag each time through the loop, or at selective points during the computation.
>It's pretty cheap to do all of this, and can be used in places where you
>don't want to slow down by calling _GetNextEvent or _GetOSEvent...

As long as you're not giving time to other applications, meaning you're
not calling SystemTask, GetNextEvent, or WaitNextEvent, it should
work.  Otherwise not.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

If you vote for clowns, you have no right to complain that only clowns
make it to the ballot.

tim@hoptoad.uucp (Tim Maroney) (12/30/89)

In article <4539@helios.ee.lbl.gov> beard@ux1.lbl.gov (Patrick C Beard) writes:
>How about a patch to PostEvent?  That way, when the keypress occurs the
>patch can set an application global to take note of it, and all that is 
>required in the inner loop of the application is polling that global variable.
>This provides both the friendliness and the performance.

See message to Rich.  I already dealt with this issue in the message you
quoted, and in addition, I've dealt with it in some of the trap patching
messages last month.  Guess you both missed those messages.

>Another point, events such as keypresses and mouse clicks are only delivered
>to the application in the front layer in MultiFinder.  Therefore, the
>application applied patch to PostEvent would only see cmd-. if that
>application is in front.  MultiFinder, as well, unpatches application
>applied patches when context switching so you wouldn't interrupt the 
>application if you happened to be in another layer.

Again, PostEvent is not the *delivery* routine.  It simply puts the
events in the global queue.  The frontmost-only delivery is done by the
Toolbox Event Manager (GetNextEvent, EventAvail, WaitNextEvent), not by
the OS Event Manager (PostEvent, OSEventAvail, GetOSEvent).  PostEvent
is called when there is a device interrupt whatever the context may
happen to be at the time.
-- 
Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com

"Prisons are built with stones of Law, Brothels with bricks of Religion."
    - Blake, "The Marriage of Heaven and Hell"

oster@dewey.soe.berkeley.edu (David Phillip Oster) (12/30/89)

It is beginning to sound like what we need is an INIT that launches a
command-period driver. The command-period driver would patch the PostEvent
trap before multi-finder is installed.  Applications interested in
command-periods would register a callback address with the driver using a
control call, inform the driver of suspend and resume events, 
using a second control call, and de-register the callback before they
quit.

If a command period occurs, then the keyboard handler will call PostEvent,
the command-period driver will detect it. If the last suspend/resume
message was a "resume" the driver will assume that one of the registered
applications was frontmost, remove the command-period from the queue, and
call the callback address. Otherwise, it will assume an unknown
application is frontmost and do nothing.

Since the callback address is being called at interrupt time, there is
a problem accessing the application's globals. We'll have the
command-period driver cache the application's currentA5 when the
application  registers the callback, and the driver will setup A5 before
calling the callback, and restore it afterward.

Since the callback address is being called at interrupt time, the callback
is limited in what it can do: it can't move memory or make most system
calls. It could set a flag, or do a C longjump.  If you do a longJump,
your ongJump catcher should set a flag that your event loop should check,
so that when your event loop next runs, it can execute a cleanup routine
to cleanup any structures that were not properly deallocated by the
longjump.

--- David Phillip Oster            --  No, I come from Boston. I just work
Arpa: oster@dewey.soe.berkeley.edu --  in cyberspace.
Uucp: {uwvax,decvax}!ucbvax!oster%dewey.soe.berkeley.edu