comparison lisp/packages/terminal.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 0293115a14e9
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; terminal.el --- terminal emulator for GNU Emacs.
2 ;; Keywords: comm, terminals
3
4 ;; Copyright (C) 1986, 1987, 1988, 1989, 1993 Free Software Foundation, Inc.
5 ;; Written by Richard Mlynarik, November 1986.
6 ;; Face and attribute support added by Richard Mlynarik, April 1996.
7
8 ;; This file is part of XEmacs.
9
10 ;; XEmacs is free software; you can redistribute it and/or modify it
11 ;; under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; XEmacs is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs; see the file COPYING. If not, write to the Free
22 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 ;;; Synched up with: Not synched with FSF.
25
26 ;;#### TODO
27 ;;#### terminfo?
28
29 ;;#### One probably wants to do setenv MORE -c when running with
30 ;;#### more-processing enabled.
31
32 (provide 'terminal)
33 (require 'ehelp)
34
35 (defvar terminal-escape-char ?\C-^
36 "*All characters except for this are passed verbatim through the
37 terminal-emulator. This character acts as a prefix for commands
38 to the emulator program itself. Type this character twice to send
39 it through the emulator. Type ? after typing it for a list of
40 possible commands.
41 This variable is local to each terminal-emulator buffer.")
42
43 (defvar terminal-scrolling t
44 "*If non-nil, the terminal-emulator will `scroll' when output occurs
45 past the bottom of the screen. If nil, output will `wrap' to the top
46 of the screen.
47 This variable is local to each terminal-emulator buffer.")
48
49 (defvar terminal-more-processing t
50 "*If non-nil, do more-processing.
51 This variable is local to each terminal-emulator buffer.")
52
53 ;; If you are the sort of loser who uses scrolling without more breaks
54 ;; and expects to actually see anything, you should probably set this to
55 ;; around 400
56 (defvar terminal-redisplay-interval 5000
57 "*Maximum number of characters which will be processed by the
58 terminal-emulator before a screen redisplay is forced.
59 Set this to a large value for greater throughput,
60 set it smaller for more frequent updates but overall slower
61 performance.")
62
63 (defvar terminal-more-break-insertion
64 "*** More break -- Press space to continue ***")
65
66 (defvar terminal-escape-map nil)
67 (defvar terminal-map nil)
68 (defvar terminal-more-break-map nil)
69 (if terminal-map
70 nil
71 (let ((map (make-keymap)))
72 (set-keymap-name map 'terminal-map)
73
74 (let ((meta-prefix-char -1)
75 (s (make-string 1 0))
76 (i 0))
77 (while (< i 256)
78 (aset s 0 i)
79 (define-key map s 'te-pass-through)
80 (setq i (1+ i))))
81
82 ;(define-key map "\C-l"
83 ; '(lambda () (interactive) (te-pass-through) (redraw-display)))
84 (setq terminal-map map)))
85
86 (if terminal-escape-map
87 nil
88 (let ((map (make-keymap)))
89 (set-keymap-name map 'terminal-escape-map)
90 (let ((s (make-string 1 ?0)))
91 (while (<= (aref s 0) ?9)
92 (define-key map s 'digit-argument)
93 (aset s 0 (1+ (aref s 0)))))
94 (define-key map "b" 'switch-to-buffer)
95 (define-key map "o" 'other-window)
96 (define-key map "e" 'te-set-escape-char)
97 (define-key map "\C-l" 'redraw-display)
98 (define-key map "\C-o" 'te-flush-pending-output)
99 (define-key map "m" 'te-toggle-more-processing)
100 (define-key map "x" 'te-escape-extended-command)
101 (define-key map "?" 'te-escape-help)
102 (define-key map (char-to-string help-char) 'te-escape-help)
103 (setq terminal-escape-map map)))
104
105 (defvar te-escape-command-alist '())
106 (if te-escape-command-alist
107 nil
108 (setq te-escape-command-alist
109 '(("Set Escape Character" . te-set-escape-char)
110 ("Refresh" . redraw-display)
111 ("Record Output" . te-set-output-log)
112 ("Photo" . te-set-output-log)
113 ("Tofu" . te-tofu) ;; confuse the uninitiated
114 ("Stuff Input" . te-stuff-string)
115 ("Flush Pending Output" . te-flush-pending-output)
116 ("Enable More Processing" . te-enable-more-processing)
117 ("Disable More Processing" . te-disable-more-processing)
118 ("Scroll at end of page" . te-do-scrolling)
119 ("Wrap at end of page" . te-do-wrapping)
120 ("Switch To Buffer" . switch-to-buffer)
121 ("Other Window" . other-window)
122 ("Kill Buffer" . kill-buffer)
123 ("Help" . te-escape-help)
124 ("Set Redisplay Interval" . te-set-redisplay-interval)
125 )))
126
127 ;(setq terminal-more-break-map nil)
128 (if terminal-more-break-map
129 nil
130 (let ((map (make-keymap)))
131 (set-keymap-name map 'terminal-more-break-map)
132
133 (let ((meta-prefix-char -1)
134 (s (make-string 1 0))
135 (i 0))
136 (while (< i 256)
137 (aset s 0 i)
138 (define-key map s 'te-more-break-unwind)
139 (setq i (1+ i))))
140
141 (define-key map (char-to-string help-char) 'te-more-break-help)
142 (define-key map " " 'te-more-break-resume)
143 (define-key map "\C-l" 'redraw-display)
144 (define-key map "\C-o" 'te-more-break-flush-pending-output)
145 ;;#### this isn't right
146 ;(define-key map "\^?" 'te-more-break-flush-pending-output) ;DEL
147 (define-key map "\r" 'te-more-break-advance-one-line)
148
149 (setq terminal-more-break-map map)))
150
151 (defvar te-width)
152 (defvar te-height)
153 (defvar te-process)
154 (defvar te-pending-output)
155 (defvar te-saved-point)
156 (defvar te-pending-output-info)
157 (defvar te-log-buffer)
158 (defvar te-more-count)
159 (defvar te-redisplay-count)
160 (defvar te-current-face)
161 (defvar te-current-attributes)
162
163 (make-face 'terminal-default)
164
165 (make-face 'terminal-standout)
166 (if (not (face-differs-from-default-p 'terminal-standout))
167 (copy-face 'bold 'terminal-standout))
168
169 (make-face 'terminal-underline)
170 (cond ((face-differs-from-default-p 'terminal-underline))
171 ((find-face 'underline)
172 (copy-face 'underline 'terminal-underline))
173 (t
174 (set-face-underline-p 'terminal-underline t)))
175
176 (make-face 'terminal-standout-underline)
177 (cond ((face-differs-from-default-p 'terminal-standout-underline))
178 (t
179 (copy-face 'terminal-standout 'terminal-standout-underline)
180 (set-face-underline-p 'terminal-standout-underline t)))
181
182 (defun te-insert-blank (count)
183 (let ((p (point)))
184 (insert-char ?\ count)
185 (put-text-property p (point) 'face 'terminal-default)))
186
187
188 ;;;; escape map
189
190 (defun te-escape-p (event)
191 (cond ((eventp terminal-escape-char)
192 (cond ((key-press-event-p event)
193 (and (key-press-event-p terminal-escape-char)
194 (= (event-modifier-bits event)
195 (event-modifier-bits terminal-escape-char))
196 (eq (event-key event)
197 (event-key terminal-escape-char))))
198 ((button-press-event-p event)
199 (and (button-press-event-p terminal-escape-char)
200 (= (event-modifier-bits event)
201 (event-modifier-bits terminal-escape-char))
202 (eq (event-button event)
203 (event-button terminal-escape-char))))
204 (t nil)))
205 ((numberp terminal-escape-char)
206 (let ((c (event-to-character event nil t nil)))
207 (and c (= c terminal-escape-char))))
208 (t
209 nil)))
210
211
212 (defun te-escape ()
213 (interactive)
214 (let ((c (let ((cursor-in-echo-area t)
215 (prompt (if prefix-arg
216 (format "Emacs Terminal escape> %d "
217 (prefix-numeric-value prefix-arg))
218 "Emacs Terminal escape> ")))
219 (message "%s" prompt)
220 (let ((e (next-command-event)))
221 (while (button-release-event-p e)
222 (setq e (next-command-event e)))
223 (if (te-escape-p e)
224 e
225 (progn
226 (setq unread-command-event e)
227 (lookup-key terminal-escape-map
228 (read-key-sequence prompt))))))))
229 (cond ((eventp c)
230 (message nil)
231 (copy-event c last-command-event)
232 (let ((terminal-escape-char -259))
233 (te-pass-through)))
234 (c
235 (call-interactively c)))))
236
237 (defun te-escape-help ()
238 "Provide help on commands available after terminal-escape-char is typed."
239 (interactive)
240 (message "Terminal emulator escape help...")
241 (let ((char (single-key-description terminal-escape-char)))
242 (with-electric-help
243 (function (lambda ()
244 (princ (format "Terminal-emulator escape, invoked by \"%s\"
245 Type \"%s\" twice to send a single \"%s\" through.
246
247 Other chars following \"%s\" are interpreted as follows:\n"
248 char char char char))
249
250 (princ (substitute-command-keys "\\{terminal-escape-map}\n"))
251 (princ (format "\nSubcommands of \"%s\" (%s)\n"
252 (where-is-internal 'te-escape-extended-command
253 terminal-escape-map t)
254 'te-escape-extended-command))
255 (let ((l (if (fboundp 'sortcar)
256 (sortcar (copy-sequence te-escape-command-alist)
257 'string<)
258 (sort (copy-sequence te-escape-command-alist)
259 (function (lambda (a b)
260 (string< (car a) (car b))))))))
261 (while l
262 (let ((doc (or (documentation (cdr (car l)))
263 "Not documented")))
264 (if (string-match "\n" doc)
265 ;; just use first line of documentation
266 (setq doc (substring doc 0 (match-beginning 0))))
267 (princ " \"")
268 (princ (car (car l)))
269 (princ "\":\n ")
270 (princ doc)
271 (write-char ?\n))
272 (setq l (cdr l))))
273 nil)))))
274
275
276
277 (defun te-escape-extended-command ()
278 (interactive)
279 (let ((c (let ((completion-ignore-case t))
280 (completing-read "terminal command: "
281 te-escape-command-alist
282 nil t))))
283 (if c
284 (catch 'foo
285 (setq c (downcase c))
286 (let ((l te-escape-command-alist))
287 (while l
288 (if (string= c (downcase (car (car l))))
289 (throw 'foo (call-interactively (cdr (car l))))
290 (setq l (cdr l)))))))))
291
292 ;; not used.
293 (defun te-escape-extended-command-unread ()
294 (interactive)
295 (setq unread-command-event last-command-event)
296 (te-escape-extended-command))
297
298 (defun te-set-escape-char (c)
299 "Change the terminal-emulator escape character."
300 (interactive (list (let ((cursor-in-echo-area t))
301 (message "Set escape character to: ")
302 (let ((e (next-command-event)))
303 (while (button-release-event-p e)
304 (setq e (next-command-event e)))
305 e))))
306 (cond ((te-escape-p c)
307 (message "\"%s\" is escape char"))
308 ((and (eventp terminal-escape-char)
309 (event-to-character terminal-escape-char nil t nil))
310 (message "\"%s\" is now escape; \"%s\" passes though"
311 (single-key-description c)
312 (single-key-description terminal-escape-char)))
313 (t
314 (message "\"%s\" is now escape"
315 (single-key-description c))
316 ;; Let mouse-events, for example, go back to looking at global map
317 (local-unset-key (vector terminal-escape-char))))
318 (local-set-key (vector c) 'te-escape) ;ensure it's defined
319 (setq terminal-escape-char c))
320
321
322 (defun te-stuff-string (string)
323 "Read a string to send to through the terminal emulator
324 as though that string had been typed on the keyboard.
325
326 Very poor man's file transfer protocol."
327 (interactive "sStuff string: ")
328 (process-send-string te-process string))
329
330 (defun te-set-output-log (name)
331 "Record output from the terminal emulator in a buffer."
332 (interactive (list (if te-log-buffer
333 nil
334 (read-buffer "Record output in buffer: "
335 (format "%s output-log"
336 (buffer-name (current-buffer)))
337 nil))))
338 (if (or (null name) (equal name ""))
339 (progn (setq te-log-buffer nil)
340 (message "Output logging off."))
341 (if (get-buffer name)
342 nil
343 (save-excursion
344 (set-buffer (get-buffer-create name))
345 (fundamental-mode)
346 (buffer-disable-undo (current-buffer))
347 (erase-buffer)))
348 (setq te-log-buffer (get-buffer name))
349 (message "Recording terminal emulator output into buffer \"%s\""
350 (buffer-name te-log-buffer))))
351
352 (defun te-tofu ()
353 "Discontinue output log."
354 (interactive)
355 (te-set-output-log nil))
356
357
358 (defun te-toggle (sym arg)
359 (set sym (cond ((not (numberp arg)) arg)
360 ((= arg 1) (not (symbol-value sym)))
361 ((< arg 0) nil)
362 (t t))))
363
364 (defun te-toggle-more-processing (arg)
365 (interactive "p")
366 (message (if (te-toggle 'terminal-more-processing arg)
367 "More processing on" "More processing off"))
368 (if terminal-more-processing (setq te-more-count -1)))
369
370 (defun te-toggle-scrolling (arg)
371 (interactive "p")
372 (message (if (te-toggle 'terminal-scrolling arg)
373 "Scroll at end of page" "Wrap at end of page")))
374
375 (defun te-enable-more-processing ()
376 "Enable ** MORE ** processing"
377 (interactive)
378 (te-toggle-more-processing t))
379
380 (defun te-disable-more-processing ()
381 "Disable ** MORE ** processing"
382 (interactive)
383 (te-toggle-more-processing nil))
384
385 (defun te-do-scrolling ()
386 "Scroll at end of page (yuck)"
387 (interactive)
388 (te-toggle-scrolling t))
389
390 (defun te-do-wrapping ()
391 "Wrap to top of window at end of page"
392 (interactive)
393 (te-toggle-scrolling nil))
394
395
396 (defun te-set-redisplay-interval (arg)
397 "Set the maximum interval (in output characters) between screen updates.
398 Set this number to large value for greater throughput,
399 set it smaller for more frequent updates (but overall slower performance."
400 (interactive "NMax number of output chars between redisplay updates: ")
401 (setq arg (max arg 1))
402 (setq terminal-redisplay-interval arg
403 te-redisplay-count 0))
404
405 ;;;; more map
406
407 ;; every command -must- call te-more-break-unwind
408 ;; or grave lossage will result
409
410 (put 'te-more-break-unread 'suppress-keymap t)
411 (defun te-more-break-unread ()
412 (interactive)
413 (if (te-escape-p last-command-event)
414 (call-interactively 'te-escape)
415 (message "Continuing from more break (\"%s\" typed, %d chars output pending...)"
416 (single-key-description last-command-event)
417 (te-pending-output-length))
418 (setq te-more-count 259259)
419 (te-more-break-unwind)
420 (let ((terminal-more-processing nil))
421 (te-pass-through))))
422
423 (defun te-more-break-resume ()
424 "Proceed past the **MORE** break,
425 allowing the next page of output to appear"
426 (interactive)
427 (message "Continuing from more break")
428 (te-more-break-unwind))
429
430 (defun te-more-break-help ()
431 "Provide help on commands available in a terminal-emulator **MORE** break"
432 (interactive)
433 (message "Terminal-emulator more break help...")
434 (sit-for 0)
435 (with-electric-help
436 (function (lambda ()
437 (princ "Terminal-emulator more break.\n\n")
438 (princ (format "Type \"%s\" (te-more-break-resume)\n%s\n"
439 (where-is-internal 'te-more-break-resume
440 terminal-more-break-map t)
441 (documentation 'te-more-break-resume)))
442 (princ (substitute-command-keys "\\{terminal-more-break-map}\n"))
443 (princ "Any other key is passed through to the program
444 running under the terminal emulator and disables more processing until
445 all pending output has been dealt with.")
446 nil))))
447
448
449 (defun te-more-break-advance-one-line ()
450 "Allow one more line of text to be output before doing another more break."
451 (interactive)
452 (setq te-more-count 1)
453 (te-more-break-unwind))
454
455 (defun te-more-break-flush-pending-output ()
456 "Discard any output which has been received by the terminal emulator but
457 not yet proceesed and then proceed from the more break."
458 (interactive)
459 (te-more-break-unwind)
460 (te-flush-pending-output))
461
462 (defun te-flush-pending-output ()
463 "Discard any as-yet-unprocessed output which has been received by
464 the terminal emulator."
465 (interactive)
466 ;; this could conceivably be confusing in the presence of
467 ;; escape-sequences spanning process-output chunks
468 (if (null (cdr te-pending-output))
469 (message "(There is no output pending)")
470 (let ((length (te-pending-output-length)))
471 (message "Flushing %d chars of pending output" length)
472 (setq te-pending-output
473 (list 0 (format "\n*** %d chars of pending output flushed ***\n"
474 length)))
475 (te-update-pending-output-display)
476 (te-process-output nil)
477 (sit-for 0))))
478
479
480 (defun te-pass-through ()
481 "Send the last character typed through the terminal-emulator
482 without any interpretation"
483 (interactive)
484 (if (te-escape-p last-command-event)
485 (call-interactively 'te-escape)
486 (and terminal-more-processing
487 (null (cdr te-pending-output))
488 (te-set-more-count nil))
489 (let ((c (event-to-character last-command-event nil t nil)))
490 (if c (process-send-string te-process (make-string 1 c))))
491 (te-process-output t)))
492
493 (defun te-set-window-start ()
494 (let* ((w (get-buffer-window (current-buffer)))
495 (h (if w (window-height w))))
496 (cond ((not w)) ; buffer not displayed
497 ((>= h (/ (- (point) (point-min)) (1+ te-width)))
498 ;; this is the normal case
499 (set-window-start w (point-min)))
500 ;; this happens if some vandal shrinks our window.
501 ((>= h (/ (- (point-max) (point)) (1+ te-width)))
502 (set-window-start w (- (point-max) (* h (1+ te-width)) -1)))
503 ;; I give up.
504 (t nil))))
505
506 (defun te-pending-output-length ()
507 (let ((length (car te-pending-output))
508 (tem (cdr te-pending-output)))
509 (while tem
510 (setq length (+ length (length (car tem))) tem (cdr tem)))
511 length))
512
513 ;;;; more break hair
514
515 (defun te-more-break ()
516 (te-set-more-count t)
517 (make-local-variable 'te-more-old-point)
518 (setq te-more-old-point (point))
519 (make-local-variable 'te-more-old-local-map)
520 (setq te-more-old-local-map (current-local-map))
521 (use-local-map terminal-more-break-map)
522 (make-local-variable 'te-more-old-filter)
523 (setq te-more-old-filter (process-filter te-process))
524 (make-local-variable 'te-more-old-mode-line-format)
525 (setq te-more-old-mode-line-format mode-line-format
526 mode-line-format (list "-- **MORE** "
527 mode-line-buffer-identification
528 "%-"))
529 (set-process-filter te-process
530 (function (lambda (process string)
531 (save-excursion
532 (set-buffer (process-buffer process))
533 (setq te-pending-output (nconc te-pending-output
534 (list string))))
535 (te-update-pending-output-display))))
536 (te-update-pending-output-display)
537 (if (eq (window-buffer (selected-window)) (current-buffer))
538 (message "More break "))
539 (or (eobp)
540 (null terminal-more-break-insertion)
541 (save-excursion
542 (forward-char 1)
543 (delete-region (point) (+ (point) te-width))
544 (insert terminal-more-break-insertion)))
545 (run-hooks 'terminal-more-break-hook)
546 (sit-for 0) ;get display to update
547 (throw 'te-process-output t))
548
549 (defun te-more-break-unwind ()
550 (interactive)
551 (use-local-map te-more-old-local-map)
552 (set-process-filter te-process te-more-old-filter)
553 (goto-char te-more-old-point)
554 (setq mode-line-format te-more-old-mode-line-format)
555 (set-buffer-modified-p (buffer-modified-p))
556 (let ((buffer-read-only nil))
557 (cond ((eobp))
558 (terminal-more-break-insertion
559 (forward-char 1)
560 (delete-region (point)
561 (+ (point) (length terminal-more-break-insertion)))
562 (te-insert-blank te-width)
563 (goto-char te-more-old-point)))
564 (setq te-more-old-point nil)
565 (let ((te-more-count 259259))
566 (te-newline)))
567 ;(sit-for 0)
568 (te-process-output t))
569
570 (defun te-set-more-count (newline)
571 (let ((line (/ (- (point) (point-min)) (1+ te-width))))
572 (if newline (setq line (1+ line)))
573 (cond ((= line te-height)
574 (setq te-more-count te-height))
575 ;#### something is strange. Investigate this!
576 ((= line (1- te-height))
577 (setq te-more-count te-height))
578 ((or (< line (/ te-height 2))
579 (> (- te-height line) 10))
580 ;; break at end of this page
581 (setq te-more-count (- te-height line)))
582 (t
583 ;; migrate back towards top (ie bottom) of screen.
584 (setq te-more-count (- te-height
585 (if (> te-height 10) 2 1)))))))
586
587
588 ;;;; More or less straight-forward terminal escapes
589
590 ;; ^j, meaning `newline' to non-display programs.
591 ;; (Who would think of ever writing a system which doesn't understand
592 ;; display terminals natively? Un*x: The Operating System of the Future.)
593 (defun te-newline ()
594 "Move down a line, optionally do more processing, perhaps wrap/scroll,
595 move to start of new line, clear to end of line."
596 (end-of-line)
597 (cond ((not terminal-more-processing))
598 ((< (setq te-more-count (1- te-more-count)) 0)
599 (te-set-more-count t))
600 ((eq te-more-count 0)
601 ;; this doesn't return
602 (te-more-break)))
603 (if (eobp)
604 (progn
605 (delete-region (point-min) (+ (point-min) te-width))
606 (goto-char (point-min))
607 (if terminal-scrolling
608 (progn (delete-char 1)
609 (goto-char (point-max))
610 (insert ?\n))))
611 (forward-char 1)
612 (delete-region (point) (+ (point) te-width)))
613 (te-insert-blank te-width)
614 (beginning-of-line)
615 (te-set-window-start))
616
617 ;; ^p ^j
618 ;; Handle the `do' or `nl' termcap capability.
619 ;;#### I am not sure why this broken, obsolete, capability is here.
620 ;;#### Perhaps it is for VIle. No comment was made about why it
621 ;;#### was added (in "Sun Dec 6 01:22:27 1987 Richard Stallman")
622 (defun te-down-vertically-or-scroll ()
623 "Move down a line vertically, or scroll at bottom."
624 (let ((column (current-column)))
625 (end-of-line)
626 (if (eobp)
627 (progn
628 (delete-region (point-min) (+ (point-min) te-width))
629 (goto-char (point-min))
630 (delete-char 1)
631 (goto-char (point-max))
632 (insert ?\n)
633 (te-insert-blank te-width)
634 (beginning-of-line))
635 (forward-line 1))
636 (move-to-column column))
637 (te-set-window-start))
638
639 ; ^p = x+32 y+32
640 (defun te-move-to-position ()
641 ;; must offset by #o40 since cretinous unix won't send a 004 char through
642 (let ((y (- (te-get-char) 32))
643 (x (- (te-get-char) 32)))
644 (if (or (> x te-width)
645 (> y te-height))
646 () ;(error "fucked %d %d" x y)
647 (goto-char (+ (point-min) x (* y (1+ te-width))))
648 ;(te-set-window-start?)
649 ))
650 (setq te-more-count -1))
651
652
653
654 ;; ^p c
655 (defun te-clear-rest-of-line ()
656 (save-excursion
657 (let ((n (- (point) (progn (end-of-line) (point)))))
658 (delete-region (point) (+ (point) n))
659 (te-insert-blank (- n)))))
660
661
662 ;; ^p C
663 (defun te-clear-rest-of-screen ()
664 (save-excursion
665 (te-clear-rest-of-line)
666 (while (progn (end-of-line) (not (eobp)))
667 (forward-char 1) (end-of-line)
668 (delete-region (- (point) te-width) (point))
669 (te-insert-blank te-width))))
670
671
672 ;; ^p ^l
673 (defun te-clear-screen ()
674 ;; regenerate buffer to compensate for (nonexistent!!) bugs.
675 (erase-buffer)
676 (let ((i 0))
677 (while (< i te-height)
678 (setq i (1+ i))
679 (te-insert-blank te-width)
680 (insert ?\n)))
681 (delete-region (1- (point-max)) (point-max))
682 (goto-char (point-min))
683 (setq te-more-count -1))
684
685
686 ;; ^p ^o count+32
687 (defun te-insert-lines ()
688 (if (not (bolp))
689 ();(error "fooI")
690 (save-excursion
691 (let* ((line (- te-height (/ (- (point) (point-min)) (1+ te-width)) -1))
692 (n (min (- (te-get-char) ?\ ) line))
693 (i 0))
694 (delete-region (- (point-max) (* n (1+ te-width))) (point-max))
695 (if (eq (point) (point-max)) (insert ?\n))
696 (while (< i n)
697 (setq i (1+ i))
698 (te-insert-blank te-width)
699 (or (eq i line) (insert ?\n))))))
700 (setq te-more-count -1))
701
702
703 ;; ^p ^k count+32
704 (defun te-delete-lines ()
705 (if (not (bolp))
706 ();(error "fooD")
707 (let* ((line (- te-height (/ (- (point) (point-min)) (1+ te-width)) -1))
708 (n (min (- (te-get-char) ?\ ) line))
709 (i 0))
710 (delete-region (point)
711 (min (+ (point) (* n (1+ te-width))) (point-max)))
712 (save-excursion
713 (goto-char (point-max))
714 (while (< i n)
715 (setq i (1+ i))
716 (te-insert-blank te-width)
717 (or (eq i line) (insert ?\n))))))
718 (setq te-more-count -1))
719
720 ;; ^p ^a
721 (defun te-beginning-of-line ()
722 (beginning-of-line))
723
724 ;; ^p ^b
725 (defun te-backward-char ()
726 (if (not (bolp))
727 (backward-char 1)))
728
729 ;; ^p ^f
730 (defun te-forward-char ()
731 (if (not (eolp))
732 (forward-char 1)))
733
734
735 ;; ^p *
736 (defun te-change-attribute ()
737 (let* ((attribute (te-get-char))
738 (on (= (te-get-char) ?1))
739 (standout (assq 'standout te-current-attributes))
740 (underline (assq 'underline te-current-attributes))
741 (frob (function (lambda ()
742 ;; This would be even more of a combinatorial mess if I
743 ;; decided I wanted to support anything more than the two
744 ;; standout and underline attributes.
745 (setq te-current-face
746 (or (cdr (assoc te-current-attributes
747 '((((standout . t) (underline . nil))
748 . terminal-standout)
749 (((standout . nil) (underline . t))
750 . terminal-standout)
751 (((standout . t) (underline . nil))
752 . terminal-standout-underline))))
753 'terminal-default))))))
754 (cond ((= attribute ?+) ;standout on/off
755 (setcdr standout on)
756 (funcall frob))
757 ((= attribute ?_) ;underline on/off
758 (setcdr underline on)
759 (funcall frob))
760 ;; reverse, blink, half-bright, double-bright, blank, protect
761 ;; ??Colours??
762 (t ;; #\space
763 (setcdr underline nil)
764 (setcdr standout nil)
765 (setq te-current-face 'terminal-default)))))
766
767
768 ;; 0177
769 (defun te-delete ()
770 (if (bolp)
771 ()
772 (delete-region (1- (point)) (point))
773 (te-insert-blank 1)
774 (forward-char -1)))
775
776 ;; ^p ^g
777 (defun te-beep ()
778 (beep))
779
780
781 ;; ^p _ count+32
782 (defun te-insert-spaces ()
783 (let* ((p (point))
784 (n (min (- (te-get-char) 32)
785 (- (progn (end-of-line) (point)) p))))
786 (if (<= n 0)
787 nil
788 (delete-char (- n))
789 (goto-char p)
790 (insert-char ?\ n))
791 (goto-char p)))
792
793 ;; ^p d count+32 (should be ^p ^d but cretinous un*x won't send ^d chars!!!)
794 (defun te-delete-char ()
795 (let* ((p (point))
796 (n (min (- (te-get-char) 32)
797 (- (progn (end-of-line) (point)) p))))
798 (if (<= n 0)
799 nil
800 (te-insert-blank n)
801 (goto-char p)
802 (delete-char n))
803 (goto-char p)))
804
805
806
807 ;; disgusting unix-required shit
808 ;; Are we living twenty years in the past yet?
809
810 (defun te-losing-unix ()
811 ;(what lossage)
812 ;(message "fucking-unix: %d" char)
813 )
814
815 ;; ^i
816 (defun te-output-tab ()
817 (let* ((p (point))
818 (x (- p (progn (beginning-of-line) (point))))
819 (l (min (- 8 (logand x 7))
820 (progn (end-of-line) (- (point) p)))))
821 (goto-char (+ p l))))
822
823 ;; Also:
824 ;; ^m => beginning-of-line (for which it -should- be using ^p ^a, right?!!)
825 ;; ^g => te-beep (for which it should use ^p ^g)
826 ;; ^h => te-backward-char (for which it should use ^p ^b)
827
828
829
830 (defun te-filter (process string)
831 (let* ((obuf (current-buffer)))
832 ;; can't use save-excursion, as that preserves point, which we don't want
833 (unwind-protect
834 (progn
835 (set-buffer (process-buffer process))
836 (goto-char te-saved-point)
837 (and (bufferp te-log-buffer)
838 (if (null (buffer-name te-log-buffer))
839 ;; killed
840 (setq te-log-buffer nil)
841 (set-buffer te-log-buffer)
842 (goto-char (point-max))
843 (insert-before-markers string)
844 (set-buffer (process-buffer process))))
845 (setq te-pending-output (nconc te-pending-output (list string)))
846 (te-update-pending-output-display)
847 (te-process-output (eq (current-buffer)
848 (window-buffer (selected-window))))
849 (set-buffer (process-buffer process))
850 (setq te-saved-point (point)))
851 (set-buffer obuf))))
852
853 ;; fucking unix has -such- braindamaged lack of tty control...
854 (defun te-process-output (preemptable)
855 ;;#### There seems no good reason to ever disallow preemption
856 (setq preemptable t)
857 (catch 'te-process-output
858 (let ((buffer-read-only nil)
859 (string nil) ostring start char (matchpos nil))
860 (while (cdr te-pending-output)
861 (setq ostring string
862 start (car te-pending-output)
863 string (car (cdr te-pending-output))
864 char (aref string start))
865 (if (eq (setq start (1+ start)) (length string))
866 (progn (setq te-pending-output
867 (cons 0 (cdr (cdr te-pending-output)))
868 start 0
869 string (car (cdr te-pending-output)))
870 (te-update-pending-output-display))
871 (setcar te-pending-output start))
872 (if (and (> char ?\037) (< char ?\377))
873 (cond ((eolp)
874 ;; unread char
875 (if (eq start 0)
876 (setq te-pending-output
877 (cons 0 (cons (make-string 1 char)
878 (cdr te-pending-output))))
879 (setcar te-pending-output (1- start)))
880 (te-newline))
881 ((null string)
882 (delete-char 1) (insert char)
883 (put-text-property (1- (point)) (point)
884 'face te-current-face)
885 (te-redisplay-if-necessary 1))
886 (t
887 (let ((end (or (and (eq ostring string) matchpos)
888 (setq matchpos (string-match
889 "[\000-\037\177-\377]"
890 string start))
891 (length string))))
892 (delete-char 1) (insert char)
893 (setq char (point))
894 (put-text-property (1- char) char 'face te-current-face)
895 (end-of-line)
896 (setq end (min end (+ start (- (point) char))))
897 (goto-char char)
898 (if (eq end matchpos) (setq matchpos nil))
899 (delete-region (point) (+ (point) (- end start)))
900 (setq char (point))
901 (insert (if (and (eq start 0)
902 (eq end (length string)))
903 string
904 (substring string start end)))
905 (put-text-property char (point) 'face te-current-face)
906 (if (eq end (length string))
907 (setq te-pending-output
908 (cons 0 (cdr (cdr te-pending-output))))
909 (setcar te-pending-output end))
910 (te-redisplay-if-necessary (1+ (- end start))))))
911 ;; I suppose if I split the guts of this out into a separate
912 ;; function we could trivially emulate different terminals
913 ;; Who cares in any case? (Apart from stupid losers using rlogin)
914 (funcall
915 (if (eq char ?\^p)
916 (or (cdr (assq (te-get-char)
917 '((?= . te-move-to-position)
918 (?c . te-clear-rest-of-line)
919 (?C . te-clear-rest-of-screen)
920 (?\C-o . te-insert-lines)
921 (?\C-k . te-delete-lines)
922 (?* . te-change-attribute)
923 ;; not necessary, but help sometimes.
924 (?\C-a . te-beginning-of-line)
925 (?\C-b . te-backward-char)
926 ;; should be C-d, but un*x
927 ;; pty's won't send \004 through!
928 ;; Can you believe this?
929 (?d . te-delete-char)
930 (?_ . te-insert-spaces)
931 ;; random
932 (?\C-f . te-forward-char)
933 (?\C-g . te-beep)
934 (?\C-j . te-down-vertically-or-scroll)
935 (?\C-l . te-clear-screen)
936 )))
937 'te-losing-unix)
938 (or (cdr (assq char
939 '((?\C-j . te-newline)
940 (?\177 . te-delete)
941 ;; Did I ask to be sent these characters?
942 ;; I don't remember doing so, either.
943 ;; (Perhaps some operating system or
944 ;; other is completely incompetent...)
945 (?\C-m . te-beginning-of-line) ;fuck me harder
946 (?\C-g . te-beep) ;again and again!
947 (?\C-h . te-backward-char) ;wa12id!!
948 (?\C-i . te-output-tab)))) ;(spiked)
949 'te-losing-unix))) ;That feels better
950 (te-redisplay-if-necessary 1))
951 (and preemptable
952 (input-pending-p)
953 ;; preemptable output! Oh my!!
954 (throw 'te-process-output t)))))
955 ;; We must update window-point in every window displaying our buffer
956 (let* ((s (selected-window))
957 (w s))
958 (while (not (eq s (setq w (next-window w))))
959 (if (eq (window-buffer w) (current-buffer))
960 (set-window-point w (point))))))
961
962 (defun te-get-char ()
963 (if (cdr te-pending-output)
964 (let ((start (car te-pending-output))
965 (string (car (cdr te-pending-output))))
966 (prog1 (aref string start)
967 (if (eq (setq start (1+ start)) (length string))
968 (setq te-pending-output (cons 0 (cdr (cdr te-pending-output))))
969 (setcar te-pending-output start))))
970 (catch 'char
971 (let ((filter (process-filter te-process)))
972 (unwind-protect
973 (progn
974 (set-process-filter te-process
975 (function (lambda (p s)
976 (or (eq (length s) 1)
977 (setq te-pending-output (list 1 s)))
978 (throw 'char (aref s 0)))))
979 (accept-process-output te-process))
980 (set-process-filter te-process filter))))))
981
982
983 (defun te-redisplay-if-necessary (length)
984 (and (<= (setq te-redisplay-count (- te-redisplay-count length)) 0)
985 (eq (current-buffer) (window-buffer (selected-window)))
986 (waiting-for-user-input-p)
987 (progn (te-update-pending-output-display)
988 (sit-for 0)
989 (setq te-redisplay-count terminal-redisplay-interval))))
990
991 (defun te-update-pending-output-display ()
992 (if (null (cdr te-pending-output))
993 (setq te-pending-output-info "")
994 (let ((length (te-pending-output-length)))
995 (if (< length 1500)
996 (setq te-pending-output-info "")
997 (setq te-pending-output-info (format "(%dK chars output pending) "
998 (/ (+ length 512) 1024))))))
999 ;; update mode line
1000 (set-buffer-modified-p (buffer-modified-p)))
1001
1002
1003 (defun te-sentinel (process message)
1004 (cond ((eq (process-status process) 'run))
1005 ((null (buffer-name (process-buffer process)))) ;deleted
1006 (t (let ((b (current-buffer)))
1007 (save-excursion
1008 (set-buffer (process-buffer process))
1009 (setq buffer-read-only nil)
1010 (fundamental-mode)
1011 (goto-char (point-max))
1012 (delete-blank-lines)
1013 (delete-horizontal-space)
1014 (insert "\n*******\n" message "*******\n"))
1015 (if (and (eq b (process-buffer process))
1016 (waiting-for-user-input-p))
1017 (progn (goto-char (point-max))
1018 (recenter -1)))))))
1019
1020 (defvar te-stty-string "stty -nl new dec echo"
1021 "Command string (to be interpreted by \"sh\") which sets the modes
1022 of the virtual terminal to be appropriate for interactive use.")
1023
1024 (defvar explicit-shell-file-name nil
1025 "*If non-nil, is file name to use for explicitly requested inferior shell.")
1026
1027 ;;;###autoload
1028 (defun terminal-emulator (buffer program args &optional width height)
1029 "Under a display-terminal emulator in BUFFER, run PROGRAM on arguments ARGS.
1030 ARGS is a list of argument-strings. Remaining arguments are WIDTH and HEIGHT.
1031 BUFFER's contents are made an image of the display generated by that program,
1032 and any input typed when BUFFER is the current Emacs buffer is sent to that
1033 program an keyboard input.
1034
1035 Interactively, BUFFER defaults to \"*terminal*\" and PROGRAM and ARGS
1036 are parsed from an input-string using your usual shell.
1037 WIDTH and HEIGHT are determined from the size of the current window
1038 -- WIDTH will be one less than the window's width, HEIGHT will be its height.
1039
1040 To switch buffers and leave the emulator, or to give commands
1041 to the emulator itself (as opposed to the program running under it),
1042 type Control-^. The following character is an emulator command.
1043 Type Control-^ twice to send it to the subprogram.
1044 This escape character may be changed using the variable `terminal-escape-char'.
1045
1046 `Meta' characters may not currently be sent through the terminal emulator.
1047
1048 Here is a list of some of the variables which control the behaviour
1049 of the emulator -- see their documentation for more information:
1050 terminal-escape-char, terminal-scrolling, terminal-more-processing,
1051 terminal-redisplay-interval.
1052
1053 This function calls the value of terminal-mode-hook if that exists
1054 and is non-nil after the terminal buffer has been set up and the
1055 subprocess started.
1056
1057 Presently with `termcap' only; if somebody sends us code to make this
1058 work with `terminfo' we will try to use it."
1059 (interactive
1060 (cons (save-excursion
1061 (set-buffer (get-buffer-create "*terminal*"))
1062 (buffer-name (if (or (not (boundp 'te-process))
1063 (null te-process)
1064 (not (eq (process-status te-process)
1065 'run)))
1066 (current-buffer)
1067 (generate-new-buffer "*terminal*"))))
1068 (append
1069 (let* ((default-s
1070 ;; Default shell is same thing M-x shell uses.
1071 (or explicit-shell-file-name
1072 (getenv "ESHELL")
1073 (getenv "SHELL")
1074 "/bin/sh"))
1075 (s (read-shell-command
1076 (format "Run program in emulator: (default %s) "
1077 default-s))))
1078 (if (equal s "")
1079 (list default-s '())
1080 (te-parse-program-and-args s))))))
1081 (switch-to-buffer buffer)
1082 (if (null width) (setq width (- (window-width (selected-window)) 1)))
1083 (if (null height) (setq height (- (window-height (selected-window)) 1)))
1084 (terminal-mode)
1085 (setq te-width width te-height height)
1086 (setq mode-line-buffer-identification
1087 (list (format "Emacs terminal %dx%d: %%b " te-width te-height)
1088 'te-pending-output-info))
1089 (let ((buffer-read-only nil))
1090 (te-clear-screen))
1091 (let (process)
1092 (while (setq process (get-buffer-process (current-buffer)))
1093 (if (y-or-n-p (format "Kill process %s? " (process-name process)))
1094 (delete-process process)
1095 (error "Process %s not killed" (process-name process)))))
1096 (condition-case err
1097 (let ((termcap
1098 ;; Because of Unix Brain Death(tm), we can't change
1099 ;; the terminal type of a running process, and so
1100 ;; terminal size and scrollability are wired-down
1101 ;; at this point. ("Detach? What's that?")
1102 (concat (format "emacs-virtual:co#%d:li#%d:%s:km:"
1103 ;; Sigh. These can't be dynamically changed.
1104 te-width te-height (if terminal-scrolling
1105 "" "ns:"))
1106 ;;-- Basic things
1107 ;; cursor-motion, bol, forward/backward char
1108 "cm=^p=%+ %+ :cr=^p^a:le=^p^b:nd=^p^f:"
1109 ;; newline, clear eof/eof, audible bell
1110 "nw=^j:ce=^pc:cd=^pC:cl=^p^l:bl=^p^g:"
1111 ;; insert/delete char/line
1112 "IC=^p_%+ :DC=^pd%+ :AL=^p^o%+ :DL=^p^k%+ :"
1113 ;;-- Not-widely-known (ie nonstandard) flags, which mean
1114 ;; o writing in the last column of the last line
1115 ;; doesn't cause idiotic scrolling, and
1116 ;; o don't use idiotische c-s/c-q sogenannte
1117 ;; ``flow control'' auf keinen Fall.
1118 "LP:NF:"
1119 ;;-- For stupid or obsolete programs
1120 "ic=^p_!:dc=^pd!:al=^p^o!:dl=^p^k!:ho=^p= :"
1121 ;;-- For disgusting programs.
1122 ;; (VI? What losers need these, I wonder?)
1123 "im=:ei=:dm=:ed=:mi:do=^p^j:nl=^p^j:bs:"
1124 "ms:me=^p* :"
1125 (if (face-equal 'terminal-default 'terminal-standout)
1126 "" "so=^p*+1:se=^p*+0")
1127 (if (face-equal 'terminal-default 'terminal-underline)
1128 "" "us=^p*_1:ue=^p*_0")
1129 )))
1130 (if (fboundp 'start-subprocess)
1131 ;; this winning function would do everything, except that
1132 ;; rms doesn't want it.
1133 (setq te-process (start-subprocess "terminal-emulator"
1134 program args
1135 'channel-type 'terminal
1136 'filter 'te-filter
1137 'buffer (current-buffer)
1138 'sentinel 'te-sentinel
1139 'modify-environment
1140 (list (cons "TERM" "emacs-virtual")
1141 (cons "TERMCAP" termcap))))
1142 ;; so instead we resort to this...
1143 (setq te-process
1144 (let ((process-environment
1145 (cons "TERM=emacs-virtual"
1146 (cons (concat "TERMCAP=" termcap)
1147 process-environment))))
1148 (start-process "terminal-emulator"
1149 (current-buffer)
1150 "/bin/sh" "-c"
1151 ;; Yuck!!! Start a shell to set some
1152 ;; terminal control characteristics.
1153 ;; Then exec the program we wanted.
1154 (format "%s; exec %s"
1155 te-stty-string
1156 (mapconcat 'te-quote-arg-for-sh
1157 (cons program args)
1158 " ")))))
1159 (set-process-filter te-process 'te-filter)
1160 (set-process-sentinel te-process 'te-sentinel)))
1161 (error (fundamental-mode)
1162 (signal (car err) (cdr err))))
1163 ;; sigh
1164 (setq inhibit-quit t) ;sport death
1165 (use-local-map terminal-map)
1166 (run-hooks 'terminal-mode-hook)
1167 (message "Entering emacs terminal-emulator... Type %s %s for help"
1168 (single-key-description terminal-escape-char)
1169 (mapconcat 'single-key-description
1170 (where-is-internal 'te-escape-help
1171 terminal-escape-map
1172 t)
1173 " ")))
1174
1175
1176 (defun te-parse-program-and-args (s)
1177 (cond ((string-match "\\`\\([a-zA-Z0-9-+=_.@/:]+[ \t]*\\)+\\'" s)
1178 (let ((l ()) (p 0))
1179 (while p
1180 (setq l (cons (if (string-match
1181 "\\([a-zA-Z0-9-+=_.@/:]+\\)\\([ \t]+\\)*"
1182 s p)
1183 (prog1 (substring s p (match-end 1))
1184 (setq p (match-end 0))
1185 (if (eq p (length s)) (setq p nil)))
1186 (prog1 (substring s p)
1187 (setq p nil)))
1188 l)))
1189 (setq l (nreverse l))
1190 (list (car l) (cdr l))))
1191 ((and (string-match "[ \t]" s) (not (file-exists-p s)))
1192 (list shell-file-name (list "-c" (concat "exec " s))))
1193 (t (list s ()))))
1194
1195 (put 'terminal-mode 'mode-class 'special)
1196 ;; This is only separated out from function terminal-emulator
1197 ;; to keep the latter a little more managable.
1198 (defun terminal-mode ()
1199 "Set up variables for use f the terminal-emualtor.
1200 One should not call this -- it is an internal function
1201 of the terminal-emulator"
1202 (kill-all-local-variables)
1203 (buffer-disable-undo (current-buffer))
1204 (setq major-mode 'terminal-mode)
1205 (setq mode-name "terminal")
1206 ; (make-local-variable 'Helper-return-blurb)
1207 ; (setq Helper-return-blurb "return to terminal simulator")
1208 (setq mode-line-process '(": %s"))
1209 (setq buffer-read-only t)
1210 (setq truncate-lines t)
1211 (make-local-variable 'terminal-escape-char)
1212 (setq terminal-escape-char (default-value 'terminal-escape-char))
1213 (make-local-variable 'terminal-scrolling)
1214 (setq terminal-scrolling (default-value 'terminal-scrolling))
1215 (make-local-variable 'terminal-more-processing)
1216 (setq terminal-more-processing (default-value 'terminal-more-processing))
1217 (make-local-variable 'terminal-redisplay-interval)
1218 (setq terminal-redisplay-interval (default-value 'terminal-redisplay-interval))
1219 (make-local-variable 'te-width)
1220 (make-local-variable 'te-height)
1221 (make-local-variable 'te-process)
1222 (make-local-variable 'te-pending-output)
1223 (setq te-pending-output (list 0))
1224 (make-local-variable 'te-saved-point)
1225 (setq te-saved-point (point-min))
1226 (make-local-variable 'te-pending-output-info) ;for the mode line
1227 (setq te-pending-output-info "")
1228 (make-local-variable 'inhibit-quit)
1229 ;(setq inhibit-quit t)
1230 (make-local-variable 'te-log-buffer)
1231 (setq te-log-buffer nil)
1232 (make-local-variable 'te-more-count)
1233 (setq te-more-count -1)
1234 (make-local-variable 'te-redisplay-count)
1235 (setq te-redisplay-count terminal-redisplay-interval)
1236 (make-local-variable 'te-current-face)
1237 (setq te-current-face 'terminal-default)
1238 (make-local-variable 'te-current-attributes)
1239 (setq te-current-attributes (list (cons 'standout nil)
1240 (cons 'underline nil)))
1241 ;(use-local-map terminal-mode-map)
1242 ;; terminal-mode-hook is called above in function terminal-emulator
1243 (make-local-variable 'meta-prefix-char)
1244 (setq meta-prefix-char -1) ;death to ASCII lossage
1245 )
1246
1247 ;;;; what a complete loss
1248
1249 (defun te-quote-arg-for-sh (fuckme)
1250 (cond ((string-match "\\`[a-zA-Z0-9-+=_.@/:]+\\'"
1251 fuckme)
1252 fuckme)
1253 ((not (string-match "[$]" fuckme))
1254 ;; "[\"\\]" are special to sh and the lisp reader in the same way
1255 (prin1-to-string fuckme))
1256 (t
1257 (let ((harder "")
1258 (cretin 0)
1259 (stupid 0))
1260 (while (cond ((>= cretin (length fuckme))
1261 nil)
1262 ;; this is the set of chars magic with "..." in `sh'
1263 ((setq stupid (string-match "[\"\\$]"
1264 fuckme cretin))
1265 t)
1266 (t (setq harder (concat harder
1267 (substring fuckme cretin)))
1268 nil))
1269 (setq harder (concat harder (substring fuckme cretin stupid)
1270 ;; Can't use ?\\ since `concat'
1271 ;; unfortunately does prin1-to-string
1272 ;; on fixna. Amazing.
1273 "\\"
1274 (substring fuckme
1275 stupid
1276 (1+ stupid)))
1277 cretin (1+ stupid)))
1278 (concat "\"" harder "\"")))))