source: branches/1.11-appstore/source/cocoa-ide/hemlock/unused/archive/input.lisp

Last change on this file was 6567, checked in by Gary Byers, 18 years ago

Move lots of (currently unused, often unlikely to ever be used) stuff to an
archive directory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1;;; -*- Log: hemlock.log; Package: Hemlock-Internals -*-
2;;;
3;;; **********************************************************************
4;;; This code was written as part of the CMU Common Lisp project at
5;;; Carnegie Mellon University, and has been placed in the public domain.
6;;;
7#+CMU (ext:file-comment
8 "$Header$")
9;;;
10;;; **********************************************************************
11;;;
12;;; This file contains the code that handles input to Hemlock.
13;;;
14(in-package :hemlock-internals)
15
16;;;
17;;; INPUT-WAITING is exported solely as a hack for the kbdmac definition
18;;; mechanism.
19;;;
20
21
22;;; These are public variables users hand to the four basic editor input
23;;; routines for method dispatching:
24;;; GET-KEY-EVENT
25;;; UNGET-KEY-EVENT
26;;; LISTEN-EDITOR-INPUT
27;;; CLEAR-EDITOR-INPUT
28;;;
29(defvar *editor-input* nil
30 "A structure used to do various operations on terminal input.")
31
32(defvar *real-editor-input* ()
33 "Useful when we want to read from the terminal when *editor-input* is
34 rebound.")
35
36
37
38
39;;;; editor-input structure.
40
41(defstruct (editor-input (:print-function
42 (lambda (s stream d)
43 (declare (ignore s d))
44 (write-string "#<Editor-Input stream>" stream))))
45 get ; A function that returns the next key-event in the queue.
46 unget ; A function that puts a key-event at the front of the queue.
47 listen ; A function that tells whether the queue is empty.
48 clear ; A function that empties the queue.
49 ;;
50 ;; Queue of events on this stream. The queue always contains at least one
51 ;; one element, which is the key-event most recently read. If no event has
52 ;; been read, the event is a dummy with a nil key-event.
53 head
54 tail)
55
56
57;;; These are the elements of the editor-input event queue.
58;;;
59(defstruct (input-event (:constructor make-input-event ()))
60 next ; Next queued event, or NIL if none.
61 hunk ; Screen hunk event was read from.
62 key-event ; Key-event read.
63 x ; X and Y character position of mouse cursor.
64 y
65 unread-p)
66
67(defvar *free-input-events* ())
68
69(defun new-event (key-event x y hunk next &optional unread-p)
70 (let ((res (if *free-input-events*
71 (shiftf *free-input-events*
72 (input-event-next *free-input-events*))
73 (make-input-event))))
74 (setf (input-event-key-event res) key-event)
75 (setf (input-event-x res) x)
76 (setf (input-event-y res) y)
77 (setf (input-event-hunk res) hunk)
78 (setf (input-event-next res) next)
79 (setf (input-event-unread-p res) unread-p)
80 res))
81
82;;; This is a public variable.
83;;;
84(defvar *last-key-event-typed* ()
85 "This variable contains the last key-event typed by the user and read as
86 input.")
87
88;;; This is a public variable. SITE-INIT initializes this.
89;;;
90(defvar *key-event-history* nil
91 "This ring holds the last 60 key-events read by the command interpreter.")
92
93(declaim (special *input-transcript*))
94
95;;; DQ-EVENT is used in editor stream methods for popping off input.
96;;; If there is an event not yet read in Stream, then pop the queue
97;;; and return the character. If there is none, return NIL.
98;;;
99(defun dq-event (stream)
100 (hemlock-ext:without-interrupts
101 (let* ((head (editor-input-head stream))
102 (next (input-event-next head)))
103 (if next
104 (let ((key-event (input-event-key-event next)))
105 (setf (editor-input-head stream) next)
106 (shiftf (input-event-next head) *free-input-events* head)
107 (ring-push key-event *key-event-history*)
108 (setf *last-key-event-typed* key-event)
109 (when *input-transcript*
110 (vector-push-extend key-event *input-transcript*))
111 key-event)))))
112
113;;; Q-EVENT is used in low level input fetching routines to add input to the
114;;; editor stream.
115;;;
116(defun q-event (stream key-event &optional x y hunk)
117 (hemlock-ext:without-interrupts
118 (let ((new (new-event key-event x y hunk nil))
119 (tail (editor-input-tail stream)))
120 (setf (input-event-next tail) new)
121 (setf (editor-input-tail stream) new))))
122
123(defun un-event (key-event stream)
124 (hemlock-ext:without-interrupts
125 (let* ((head (editor-input-head stream))
126 (next (input-event-next head))
127 (new (new-event key-event (input-event-x head) (input-event-y head)
128 (input-event-hunk head) next t)))
129 (setf (input-event-next head) new)
130 (unless next (setf (editor-input-tail stream) new)))))
131
132
133
134
135;;;; Keyboard macro hacks.
136
137(defvar *input-transcript* ()
138 "If this variable is non-null then it should contain an adjustable vector
139 with a fill pointer into which all keyboard input will be pushed.")
140
141;;; INPUT-WAITING -- Internal
142;;;
143;;; An Evil hack that tells us whether there is an unread key-event on
144;;; *editor-input*. Note that this is applied to the real *editor-input*
145;;; rather than to a kbdmac stream.
146;;;
147(defun input-waiting ()
148 "Returns true if there is a key-event which has been unread-key-event'ed
149 on *editor-input*. Used by the keyboard macro stuff."
150 (let ((next (input-event-next
151 (editor-input-head *real-editor-input*))))
152 (and next (input-event-unread-p next))))
153
154
155
156
157;;;; Input method macro.
158
159(defvar *in-hemlock-stream-input-method* nil
160 "This keeps us from undefined nasties like re-entering Hemlock stream
161 input methods from input hooks and scheduled events.")
162
163(declaim (special *screen-image-trashed*))
164
165;;; These are the characters GET-KEY-EVENT notices when it pays attention
166;;; to aborting input. This happens via EDITOR-INPUT-METHOD-MACRO.
167;;;
168(defparameter editor-abort-key-events (list #k"Control-g" #k"Control-G"))
169
170#+clx
171(defun cleanup-for-wm-closed-display(closed-display)
172 ;; Remove fd-handlers
173 (hemlock-ext:disable-clx-event-handling closed-display)
174 ;; Close file descriptor and note DEAD.
175 (xlib:close-display closed-display)
176 ;;
177 ;; At this point there is not much sense to returning to Lisp
178 ;; as the editor cannot be re-entered (there are lots of pointers
179 ;; to the dead display around that will cause subsequent failures).
180 ;; Maybe could switch to tty mode then (save-all-files-and-exit)?
181 ;; For now, just assume user wanted an easy way to kill the session.
182 (hemlock-ext:quit))
183
184(defmacro abort-key-event-p (key-event)
185 `(member ,key-event editor-abort-key-events))
186
187;;; EDITOR-INPUT-METHOD-MACRO -- Internal.
188;;;
189;;; WINDOWED-GET-KEY-EVENT and TTY-GET-KEY-EVENT use this. Somewhat odd stuff
190;;; goes on here because this is the place where Hemlock waits, so this is
191;;; where we redisplay, check the time for scheduled events, etc. In the loop,
192;;; we call the input hook when we get a character and leave the loop. If
193;;; there isn't any input, invoke any scheduled events whose time is up.
194;;; Unless SERVE-EVENT returns immediately and did something, (serve-event 0),
195;;; call redisplay, note that we are going into a read wait, and call
196;;; SERVE-EVENT with a wait or infinite timeout. Upon exiting the loop, turn
197;;; off the read wait note and check for the abort character. Return the
198;;; key-event we got. We bind an error condition handler here because the
199;;; default Hemlock error handler goes into a little debugging prompt loop, but
200;;; if we got an error in getting input, we should prompt the user using the
201;;; input method (recursively even).
202;;;
203(eval-when (:compile-toplevel :execute)
204
205(defmacro editor-input-method-macro ()
206 `(handler-bind
207 ((error
208 (lambda (condition)
209 (when (typep condition 'stream-error)
210 (let* ((stream (stream-error-stream condition))
211 (display *editor-windowed-input*)
212 (display-stream
213 #+CLX
214 (and display (xlib::display-input-stream display))))
215 (when (eq stream display-stream)
216 ;;(format *error-output* "~%Hemlock: Display died!~%~%")
217 (cleanup-for-wm-closed-display display)
218 (exit-hemlock nil))
219 (let ((device
220 (device-hunk-device (window-hunk (current-window)))))
221 (funcall (device-exit device) device))
222 (invoke-debugger condition)))))
223 #+(and CLX )
224 (xlib:closed-display
225 (lambda(condition)
226 (let ((display (xlib::closed-display-display condition)))
227 (format *error-output*
228 "Closed display on stream ~a~%"
229 (xlib::display-input-stream display)))
230 (exit-hemlock nil)))
231 )
232; (when *in-hemlock-stream-input-method*
233; (error "Entering Hemlock stream input method recursively!"))
234 (let ((*in-hemlock-stream-input-method* t)
235 (nrw-fun (device-note-read-wait
236 (device-hunk-device (window-hunk (current-window)))))
237 key-event)
238 (loop
239 (when (setf key-event (dq-event stream))
240 (dolist (f (variable-value 'hemlock::input-hook)) (funcall f))
241 (return))
242 (invoke-scheduled-events)
243 (unless (or (hemlock-ext:serve-event 0)
244 (internal-redisplay))
245 (internal-redisplay)
246 (when nrw-fun (funcall nrw-fun t))
247 (let ((wait (next-scheduled-event-wait)))
248 (if wait (hemlock-ext:serve-event wait) (hemlock-ext:serve-event)))))
249 (when nrw-fun (funcall nrw-fun nil))
250 (when (and (abort-key-event-p key-event)
251 ;; ignore-abort-attempts-p must exist outside the macro.
252 ;; in this case it is bound in GET-KEY-EVENT.
253 (not ignore-abort-attempts-p))
254 (beep)
255 (throw 'editor-top-level-catcher nil))
256 key-event)))
257) ;eval-when
258
259
260
261
262;;;; Editor input from windowing system.
263#+clx
264(defstruct (windowed-editor-input
265 (:include editor-input
266 (get #'windowed-get-key-event)
267 (unget #'windowed-unget-key-event)
268 (listen #'windowed-listen)
269 (clear #'windowed-clear-input))
270 (:print-function
271 (lambda (s stream d)
272 (declare (ignore s d))
273 (write-string "#<Editor-Window-Input stream>" stream)))
274 (:constructor make-windowed-editor-input
275 (&optional (head (make-input-event)) (tail head))))
276 hunks) ; List of bitmap-hunks which input to this stream.
277
278#+clx
279;;; There's actually no difference from the TTY case...
280(defun windowed-get-key-event (stream ignore-abort-attempts-p)
281 (tty-get-key-event stream ignore-abort-attempts-p))
282
283#+clx
284(defun windowed-unget-key-event (key-event stream)
285 (un-event key-event stream))
286
287#+clx
288(defun windowed-clear-input (stream)
289 (loop (unless (hemlock-ext:serve-event 0) (return)))
290 (hemlock-ext:without-interrupts
291 (let* ((head (editor-input-head stream))
292 (next (input-event-next head)))
293 (when next
294 (setf (input-event-next head) nil)
295 (shiftf (input-event-next (editor-input-tail stream))
296 *free-input-events* next)
297 (setf (editor-input-tail stream) head)))))
298
299#+clx
300(defun windowed-listen (stream)
301 (loop
302 ;; Don't service anymore events if we just got some input.
303 (when (input-event-next (editor-input-head stream))
304 (return t))
305 ;;
306 ;; If nothing is pending, check the queued input.
307 (unless (hemlock-ext:serve-event 0)
308 (return (not (null (input-event-next (editor-input-head stream))))))))
309
310
311
312;;;; Editor input from a tty.
313
314(defstruct (tty-editor-input
315 (:include editor-input
316 (get #'tty-get-key-event)
317 (unget #'tty-unget-key-event)
318 (listen #'tty-listen)
319 (clear #'tty-clear-input))
320 (:print-function
321 (lambda (obj stream n)
322 (declare (ignore obj n))
323 (write-string "#<Editor-Tty-Input stream>" stream)))
324 (:constructor make-tty-editor-input
325 (fd &optional (head (make-input-event)) (tail head))))
326 fd)
327
328(defun tty-get-key-event (stream ignore-abort-attempts-p)
329 (editor-input-method-macro))
330
331(defun tty-unget-key-event (key-event stream)
332 (un-event key-event stream))
333
334(defun tty-clear-input (stream)
335 (hemlock-ext:without-interrupts
336 (let* ((head (editor-input-head stream))
337 (next (input-event-next head)))
338 (when next
339 (setf (input-event-next head) nil)
340 (shiftf (input-event-next (editor-input-tail stream))
341 *free-input-events* next)
342 (setf (editor-input-tail stream) head)))))
343
344;;; Note that we never return NIL as long as there are events to be served with
345;;; SERVE-EVENT. Thus non-keyboard input (i.e. process output)
346;;; effectively causes LISTEN to block until either all the non-keyboard input
347;;; has happened, or there is some real keyboard input.
348;;;
349(defun tty-listen (stream)
350 (loop
351 ;; Don't service anymore events if we just got some input.
352 (when (or (input-event-next (editor-input-head stream))
353 (editor-tty-listen stream))
354 (return t))
355 ;; If nothing is pending, check the queued input.
356 (unless (hemlock-ext:serve-event 0)
357 (return (not (null (input-event-next (editor-input-head stream))))))))
358
359
360
361;;;; GET-KEY-EVENT, UNGET-KEY-EVENT, LISTEN-EDITOR-INPUT, CLEAR-EDITOR-INPUT.
362
363;;; GET-KEY-EVENT -- Public.
364;;;
365(defun get-key-event (editor-input &optional ignore-abort-attempts-p)
366 "This function returns a key-event as soon as it is available on
367 editor-input. Editor-input is either *editor-input* or *real-editor-input*.
368 Ignore-abort-attempts-p indicates whether #k\"C-g\" and #k\"C-G\" throw to
369 the editor's top-level command loop; when this is non-nil, this function
370 returns those key-events when the user types them. Otherwise, it aborts the
371 editor's current state, returning to the command loop."
372 (funcall (editor-input-get editor-input) editor-input ignore-abort-attempts-p))
373
374;;; UNGET-KEY-EVENT -- Public.
375;;;
376(defun unget-key-event (key-event editor-input)
377 "This function returns the key-event to editor-input, so the next invocation
378 of GET-KEY-EVENT will return the key-event. If the key-event is #k\"C-g\"
379 or #k\"C-G\", then whether GET-KEY-EVENT returns it depends on its second
380 argument. Editor-input is either *editor-input* or *real-editor-input*."
381 (funcall (editor-input-unget editor-input) key-event editor-input))
382
383;;; CLEAR-EDITOR-INPUT -- Public.
384;;;
385(defun clear-editor-input (editor-input)
386 "This function flushes any pending input on editor-input. Editor-input
387 is either *editor-input* or *real-editor-input*."
388 (funcall (editor-input-clear editor-input) editor-input))
389
390;;; LISTEN-EDITOR-INPUT -- Public.
391;;;
392(defun listen-editor-input (editor-input)
393 "This function returns whether there is any input available on editor-input.
394 Editor-input is either *editor-input* or *real-editor-input*."
395 (funcall (editor-input-listen editor-input) editor-input))
396
397
398
399
400;;;; LAST-KEY-EVENT-CURSORPOS and WINDOW-INPUT-HANDLER.
401
402;;; LAST-KEY-EVENT-CURSORPOS -- Public
403;;;
404;;; Just look up the saved info in the last read key event.
405;;;
406(defun last-key-event-cursorpos ()
407 "Return as values, the (X, Y) character position and window where the
408 last key event happened. If this cannot be determined, Nil is returned.
409 If in the modeline, return a Y position of NIL and the correct X and window.
410 Returns nil for terminal input."
411 (let* ((ev (editor-input-head *real-editor-input*))
412 (hunk (input-event-hunk ev))
413 (window (and hunk (device-hunk-window hunk))))
414 (when window
415 (values (input-event-x ev) (input-event-y ev) window))))
416
417;;; WINDOW-INPUT-HANDLER -- Internal
418;;;
419;;; This is the input-handler function for hunks that implement windows. It
420;;; just queues the events on *real-editor-input*.
421;;;
422(defun window-input-handler (hunk char x y)
423 (q-event *real-editor-input* char x y hunk))
424
425
426
427
428;;;; Random typeout input routines.
429
430(defun wait-for-more (stream)
431 (let ((key-event (more-read-key-event)))
432 (cond ((logical-key-event-p key-event :yes))
433 ((or (logical-key-event-p key-event :do-all)
434 (logical-key-event-p key-event :exit))
435 (setf (random-typeout-stream-no-prompt stream) t)
436 (random-typeout-cleanup stream))
437 ((logical-key-event-p key-event :keep)
438 (setf (random-typeout-stream-no-prompt stream) t)
439 (maybe-keep-random-typeout-window stream)
440 (random-typeout-cleanup stream))
441 ((logical-key-event-p key-event :no)
442 (random-typeout-cleanup stream)
443 (throw 'more-punt nil))
444 (t
445 (unget-key-event key-event *editor-input*)
446 (random-typeout-cleanup stream)
447 (throw 'more-punt nil)))))
448
449(declaim (special *more-prompt-action*))
450
451(defun maybe-keep-random-typeout-window (stream)
452 (let* ((window (random-typeout-stream-window stream))
453 (buffer (window-buffer window))
454 (start (buffer-start-mark buffer)))
455 (when (typep (hi::device-hunk-device (hi::window-hunk window))
456 'hi::bitmap-device)
457 (let ((*more-prompt-action* :normal))
458 (update-modeline-field buffer window :more-prompt)
459 (random-typeout-redisplay window))
460 (buffer-start (buffer-point buffer))
461 (let* ((xwindow (make-xwindow-like-hwindow window))
462 (window (make-window start :window xwindow)))
463 (unless window
464 #+clx(xlib:destroy-window xwindow)
465 (editor-error "Could not create random typeout window."))))))
466
467(defun end-random-typeout (stream)
468 (let ((*more-prompt-action* :flush)
469 (window (random-typeout-stream-window stream)))
470 (update-modeline-field (window-buffer window) window :more-prompt)
471 (random-typeout-redisplay window))
472 (unless (random-typeout-stream-no-prompt stream)
473 (let* ((key-event (more-read-key-event))
474 (keep-p (logical-key-event-p key-event :keep)))
475 (when keep-p (maybe-keep-random-typeout-window stream))
476 (random-typeout-cleanup stream)
477 (unless (or (logical-key-event-p key-event :do-all)
478 (logical-key-event-p key-event :exit)
479 (logical-key-event-p key-event :no)
480 (logical-key-event-p key-event :yes)
481 keep-p)
482 (unget-key-event key-event *editor-input*)))))
483
484;;; MORE-READ-KEY-EVENT -- Internal.
485;;;
486;;; This gets some input from the type of stream bound to *editor-input*. Need
487;;; to loop over SERVE-EVENT since it returns on any kind of event (not
488;;; necessarily a key or button event).
489;;;
490;;; Currently this does not work for keyboard macro streams!
491;;;
492(defun more-read-key-event ()
493 (clear-editor-input *editor-input*)
494 (let ((key-event (loop
495 (let ((key-event (dq-event *editor-input*)))
496 (when key-event (return key-event))
497 (hemlock-ext:serve-event)))))
498 (when (abort-key-event-p key-event)
499 (beep)
500 (throw 'editor-top-level-catcher nil))
501 key-event))
Note: See TracBrowser for help on using the repository browser.