[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Need to insert keydown events



To:     Matthew Cornell    cornell@unix1.cs.umass.edu
cc:     info-mcl
From:   Steve Mitchell
Date:   4/19/92
 
Sub:    Need to insert keydown events into MCL
 
> In trying to make incremental searches playback correctly in my
> fred-macros.lisp contribution I seem to need a new mechanism other
> than my current one, which uses RUN-FRED-COMMAND.
 
run-fred-command is intentionally not documented so getting away
from it asap is a good idea.
 
 
> Specifically, I want a function INSERT-KEY-EVENT that is passed
> a character and a list of modifiers, and makes the Mac or MCL
> think the user just hit those keys. For example:
> (defun TEST-PLAYBACK ()
>   (dolist (event-item '((#\r :control) (#\m) (#\a) (#\c) (#\g :control)))
>     (insert-key-event (first event-item) (rest event-item))))
> I looked at #_PostEvent but IM II-68 warns the modifiers will be the
> current ones, so I can't pass the saved ones.
 
MCL provides full access to event processing; it's really a question
of where and at what level you want to break in.
 
You don't seem to have an interest in saving context information, such
as which window and which key-handler is active, so I assume the aim is
simply to record sequences of keystrokes for playback in any window.
Therefore, your recording mechanism should just save off a copy of (the
relevant components of) each keystoke event. Playback would involve
injecting each keystoke event back into the event mechanism in place of
an idle event.
 
 
> The Journaling Mechanism described starting on IM I-261 requires
> writing a journaling device driver, setting global vars, etc. Seems
> too much work.
 
Apple deprecates the Journaling Mechanism anyway. (That didn't stop
them using it a couple of times)
 
 
> An MCL-specific hack would be just fine, something that might involve
> setting-up properly variables like *current-event*,
> *current-keystroke*, etc. then calling do-event or whatever.
> So I'd appreciate any ideas anyone might have. I'm using MCL 2.0f3.
 
*current-keystroke*, event-keystroke, etc, pre-suppose the existence
of a keystroke event, so using them begs the question of playback.
 
ccl::do-event is not documented, and will clobber *current-keystroke*.
 
*eventhook* and *current-event* are provided by MCL for the purpose of
custom event handling, and are documented in the Reference. An example:
 
(defparameter *key-events-list*
  '((134930 . 4224) (142957 . 128) (131169 . 128) (133219 . 128) (132359 .
4224))
  "Event msg & modifier flds for ((:control #\r) (#\m) (#\a) (#\c) (:control
#\g))")
 
;;Visual check:
(dolist (cons *key-events-list*)
  (format t "~S " (keystroke-name (event-keystroke (car cons) (cdr cons)))))
 
(defun set-pseudo-event (cons)
  "Sets *current-event* to a pseudo keystroke event"
  (rlet ((pseudoEvent eventRecord))
    (rset pseudoEvent eventRecord.what            3)  ;$keyDown
    (rset pseudoEvent eventRecord.message (car cons)) ;keyCode and/or charCode
    (rset pseudoEvent eventRecord.when            0)  ;irrelevant
    (rset pseudoEvent eventRecord.where           0)  ;irrelevant
    (rset pseudoEvent eventRecord.modifiers (cdr cons))
    (setf *current-event* pseudoEvent)))
 
(defun playback-eventhook ()
  "Install a temporary eventhook to process keystrokes from
*key-events-list* during idle time, at the current cursor position
until either done or the user presses the mouse button or a key."
  (let ((event-type (rref *current-event* eventRecord.what)))
    (cond ((null *key-events-list*)
           (setq *eventhook* nil)
           T)
          ((equalp event-type 0)  ;$nullEvent
           (set-pseudo-event (pop *key-events-list*))
           nil)
          ((equalp event-type 1)  ;$mouseDown
           (setq *eventhook* nil)
           T)
          ((equalp event-type 3)  ;$keyDown
           (setq *eventhook* nil)
           T))))
 
;;Play the sequence (this is your "insert-key-event"):
(setq *eventhook* '(playback-eventhook))
;;*key-events-list* will now be nil
 
;;; For recording, a pair of functions similar to the above
;;; could push a copy of *current-event* keystroke events
;;; onto *key-events-list*.
 
If you decide that side-effecting *current-event* is not ok, you
could instead try advise'ing get-next-event to return the next
pseudo keystroke-event if any, whenever it gets an idle from WNE.
Then you wouldn't need an eventhook either. For recording macros,
the advise'ing function would push a copy of keystroke events onto
*key-events-list*.
 
 
- - - - - - - - - -
EDITABLE KEYSTROKE MACROS
 
If you're recording keystroke events for playback, they don't have to
be in keystroke-code form, eg '(:control #\r) unless the user can edit
macros; not necessarily a desirable feature anyway.
 
The dolist form above translates *key-events-list* into keystroke-code
form and displays it. event-keystroke does the translation by (among
other things) applying #_KeyTrans to the installed KCHR resource.
There is no inverse trap, and MCL does not provide the inverse
translation, so converting from '(:control #\r) -> (134930 . 4224)
will have to be ad hoc.
 
If you have to be able to edit macros, and hence translate from
keystroke-code form to events, simplest and safest is to embed a
table of key Codes (a=0, b=11, c=8, ...) as MCL (ie event-keystroke)
will accept either key Code (* 256), or char Code, or both in the
message field, and does not need the ADB code. The modifiers field
can be readily reconstructed from a keystroke-code.
- - - - - - - - - -
 
_Steve