source: branches/win64/cocoa-ide/hemlock/unused/archive/tty/tty-display.lisp

Last change on this file was 6, checked in by Gary Byers, 21 years ago

Initial revision

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.7 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;;; Written by Bill Chiles.
13;;;
14
15(in-package :hemlock-internals)
16
17(export '(redisplay redisplay-all define-tty-font))
18
19
20
21
22;;;; Macros.
23
24(eval-when (:compile-toplevel :execute)
25(defmacro tty-hunk-modeline-pos (hunk)
26 `(tty-hunk-text-height ,hunk))
27) ;eval-when
28
29
30(defvar *currently-selected-hunk* nil)
31(defvar *hunk-top-line*)
32
33(declaim (fixnum *hunk-top-line*))
34
35(eval-when (:compile-toplevel :execute)
36(defmacro select-hunk (hunk)
37 `(unless (eq ,hunk *currently-selected-hunk*)
38 (setf *currently-selected-hunk* ,hunk)
39 (setf *hunk-top-line*
40 (the fixnum
41 (1+ (the fixnum
42 (- (the fixnum
43 (tty-hunk-text-position ,hunk))
44 (the fixnum
45 (tty-hunk-text-height ,hunk)))))))))
46) ;eval-when
47
48
49;;; Screen image lines.
50;;;
51(defstruct (si-line (:print-function print-screen-image-line)
52 (:constructor %make-si-line (chars)))
53 (chars nil :type simple-string)
54 (length 0)
55 (fonts nil :type list))
56
57(defun make-si-line (n)
58 (%make-si-line (make-string n)))
59
60(defun print-screen-image-line (obj str n)
61 (declare (ignore n))
62 (write-string "#<Screen Image Line \"" str)
63 (write-string (si-line-chars obj) str :end (si-line-length obj))
64 (write-string "\">" str))
65
66
67(defun find-identical-prefix (dis-line dis-line-fonts si-line)
68 (declare (type dis-line dis-line)
69 (type list dis-line-fonts)
70 (type si-line si-line))
71 (let* ((dl-chars (dis-line-chars dis-line))
72 (dl-len (dis-line-length dis-line))
73 (si-chars (si-line-chars si-line))
74 (si-len (si-line-length si-line))
75 (okay-until 0))
76 (declare (type simple-string dl-chars si-chars)
77 (type (and unsigned-byte fixnum) dl-len si-len)
78 (type (and unsigned-byte fixnum) okay-until))
79 (do ((dl-fonts dis-line-fonts (cdr dis-line-fonts))
80 (si-fonts (si-line-fonts si-line) (cdr si-fonts)))
81 ((or (null dl-fonts) (null si-fonts))
82 (let ((next-font (car (or dl-fonts si-fonts))))
83 (if next-font
84 (let ((end (min dl-len si-len (cadr next-font))))
85 (or (string/= dl-chars si-chars
86 :start1 okay-until :start2 okay-until
87 :end1 end :end2 end)
88 end))
89 (let ((end (min dl-len si-len)))
90 (or (string/= dl-chars si-chars
91 :start1 okay-until :start2 okay-until
92 :end1 end :end2 end)
93 (if (= dl-len si-len) nil end))))))
94 (let ((dl-font (caar dl-fonts))
95 (dl-start (cadar dl-fonts))
96 (dl-stop (cddar dl-fonts))
97 (si-font (caar si-fonts))
98 (si-start (cadar si-fonts))
99 (si-stop (cddar si-fonts)))
100 (unless (and (= dl-font si-font)
101 (= dl-start si-start))
102 (let ((font-lossage (min dl-start si-start)))
103 (return (or (string/= dl-chars si-chars
104 :start1 okay-until :start2 okay-until
105 :end1 font-lossage :end2 font-lossage)
106 font-lossage))))
107 (unless (= dl-stop si-stop)
108 (let ((font-lossage (min dl-stop si-stop)))
109 (return (or (string/= dl-chars si-chars
110 :start1 okay-until :start2 okay-until
111 :end1 font-lossage :end2 font-lossage)
112 font-lossage))))
113 (let ((mismatch (string/= dl-chars si-chars
114 :start1 okay-until :start2 okay-until
115 :end1 dl-stop :end2 si-stop)))
116 (if mismatch
117 (return mismatch)
118 (setf okay-until dl-stop)))))))
119
120
121(defun find-identical-suffix (dis-line dis-line-fonts si-line)
122 (declare (type dis-line dis-line)
123 (type list dis-line-fonts)
124 (type si-line si-line))
125 (let* ((dl-chars (dis-line-chars dis-line))
126 (dl-len (dis-line-length dis-line))
127 (si-chars (si-line-chars si-line))
128 (si-len (si-line-length si-line))
129 (count (dotimes (i (min dl-len si-len) i)
130 (when (char/= (schar dl-chars (- dl-len i 1))
131 (schar si-chars (- si-len i 1)))
132 (return i)))))
133 (declare (type simple-string dl-chars si-chars)
134 (type (and unsigned-byte fixnum) dl-len si-len))
135 (do ((dl-fonts (reverse dis-line-fonts) (cdr dis-line-fonts))
136 (si-fonts (reverse (si-line-fonts si-line)) (cdr si-fonts)))
137 ((or (null dl-fonts) (null si-fonts))
138 (cond (dl-fonts
139 (min (- dl-len (cddar dl-fonts)) count))
140 (si-fonts
141 (min (- si-len (cddar si-fonts)) count))
142 (t
143 count)))
144 (let ((dl-font (caar dl-fonts))
145 (dl-start (- dl-len (cadar dl-fonts)))
146 (dl-stop (- dl-len (cddar dl-fonts)))
147 (si-font (caar si-fonts))
148 (si-start (- si-len (cadar si-fonts)))
149 (si-stop (- si-len (cddar si-fonts))))
150 (unless (and (= dl-font si-font)
151 (= dl-stop si-stop))
152 (return (min dl-stop si-stop count)))
153 (unless (= dl-start si-start)
154 (return (min dl-start si-start count)))
155 (when (<= count dl-start)
156 (return count))))))
157
158
159(defmacro si-line (screen-image n)
160 `(svref ,screen-image ,n))
161
162
163
164
165;;; Font support.
166
167(defvar *tty-font-strings* (make-array font-map-size :initial-element nil)
168 "Array of (start-string . end-string) for fonts, or NIL if no such font.")
169
170(defun define-tty-font (font-id &rest stuff)
171 (unless (<= 0 font-id (1- font-map-size))
172 (error "Bogus font-id: ~S" font-id))
173 (cond ((every #'keywordp stuff)
174 (error "Can't extract font strings from the termcap entry yet."))
175 ((and (= (length stuff) 2)
176 (stringp (car stuff))
177 (stringp (cadr stuff)))
178 (setf (aref *tty-font-strings* font-id)
179 (cons (car stuff) (cadr stuff))))
180 (t
181 (error "Bogus font spec: ~S~%Must be either a list of keywords or ~
182 a list of the start string and end string."))))
183
184
185(defun compute-font-usages (dis-line)
186 (do ((results nil)
187 (change (dis-line-font-changes dis-line) (font-change-next change))
188 (prev nil change))
189 ((null change)
190 (when prev
191 (let ((font (font-change-font prev)))
192 (when (and (not (zerop font))
193 (aref *tty-font-strings* font))
194 (push (list* (font-change-font prev)
195 (font-change-x prev)
196 (dis-line-length dis-line))
197 results))))
198 (nreverse results))
199 (when prev
200 (let ((font (font-change-font prev)))
201 (when (and (not (zerop font))
202 (aref *tty-font-strings* font))
203 (push (list* (font-change-font prev)
204 (font-change-x prev)
205 (font-change-x change))
206 results))))))
207
208
209
210;;;; Dumb window redisplay.
211
212(defmacro tty-dumb-line-redisplay (device hunk dis-line &optional y)
213 (let ((dl (gensym)) (dl-chars (gensym)) (dl-fonts (gensym)) (dl-len (gensym))
214 (dl-pos (gensym)) (screen-image-line (gensym)))
215 `(let* ((,dl ,dis-line)
216 (,dl-chars (dis-line-chars ,dl))
217 (,dl-fonts (compute-font-usages ,dis-line))
218 (,dl-len (dis-line-length ,dl))
219 (,dl-pos ,(or y `(dis-line-position ,dl))))
220 (funcall (tty-device-display-string ,device)
221 ,hunk 0 ,dl-pos ,dl-chars ,dl-fonts 0 ,dl-len)
222 (setf (dis-line-flags ,dl) unaltered-bits)
223 (setf (dis-line-delta ,dl) 0)
224 (select-hunk ,hunk)
225 (let ((,screen-image-line (si-line (tty-device-screen-image ,device)
226 (+ *hunk-top-line* ,dl-pos))))
227 (replace-si-line (si-line-chars ,screen-image-line) ,dl-chars
228 0 0 ,dl-len)
229 (setf (si-line-length ,screen-image-line) ,dl-len)
230 (setf (si-line-fonts ,screen-image-line) ,dl-fonts)))))
231
232(defun tty-dumb-window-redisplay (window)
233 (let* ((first (window-first-line window))
234 (hunk (window-hunk window))
235 (device (device-hunk-device hunk))
236 (screen-image (tty-device-screen-image device)))
237 (funcall (tty-device-clear-to-eow device) hunk 0 0)
238 (do ((i 0 (1+ i))
239 (dl (cdr first) (cdr dl)))
240 ((eq dl the-sentinel)
241 (setf (window-old-lines window) (1- i))
242 (select-hunk hunk)
243 (do ((last (tty-hunk-text-position hunk))
244 (i (+ *hunk-top-line* i) (1+ i)))
245 ((> i last))
246 (declare (fixnum i last))
247 (let ((si-line (si-line screen-image i)))
248 (setf (si-line-length si-line) 0)
249 (setf (si-line-fonts si-line) nil))))
250 (tty-dumb-line-redisplay device hunk (car dl) i))
251 (setf (window-first-changed window) the-sentinel
252 (window-last-changed window) first)
253 (when (window-modeline-buffer window)
254 (let ((dl (window-modeline-dis-line window))
255 (y (tty-hunk-modeline-pos hunk)))
256 (unwind-protect
257 (progn
258 (funcall (tty-device-standout-init device) hunk)
259 (funcall (tty-device-clear-to-eol device) hunk 0 y)
260 (tty-dumb-line-redisplay device hunk dl y))
261 (funcall (tty-device-standout-end device) hunk))
262 (setf (dis-line-flags dl) unaltered-bits)))))
263
264
265
266
267;;;; Dumb redisplay top n lines of a window.
268
269(defun tty-redisplay-n-lines (window n)
270 (let* ((hunk (window-hunk window))
271 (device (device-hunk-device hunk)))
272 (funcall (tty-device-clear-lines device) hunk 0 0 n)
273 (do ((n n (1- n))
274 (dl (cdr (window-first-line window)) (cdr dl)))
275 ((or (zerop n) (eq dl the-sentinel)))
276 (tty-dumb-line-redisplay device hunk (car dl)))))
277
278
279
280
281;;;; Semi dumb window redisplay
282
283;;; This is for terminals without opening and deleting lines.
284
285;;; TTY-SEMI-DUMB-WINDOW-REDISPLAY is a lot like TTY-SMART-WINDOW-REDISPLAY,
286;;; but it calls different line redisplay functions.
287;;;
288(defun tty-semi-dumb-window-redisplay (window)
289 (let* ((hunk (window-hunk window))
290 (device (device-hunk-device hunk)))
291 (let ((first-changed (window-first-changed window))
292 (last-changed (window-last-changed window)))
293 ;; Is there anything to do?
294 (unless (eq first-changed the-sentinel)
295 (if ;; One line-changed.
296 (and (eq first-changed last-changed)
297 (zerop (dis-line-delta (car first-changed))))
298 (tty-semi-dumb-line-redisplay device hunk (car first-changed))
299 ;; More lines changed.
300 (do-semi-dumb-line-writes first-changed last-changed hunk))
301 ;; Set the bounds so we know we displayed...
302 (setf (window-first-changed window) the-sentinel
303 (window-last-changed window) (window-first-line window))))
304 ;;
305 ;; Clear any extra lines at the end of the window.
306 (let ((pos (dis-line-position (car (window-last-line window)))))
307 (when (< pos (1- (window-height window)))
308 (tty-smart-clear-to-eow hunk (1+ pos)))
309 (setf (window-old-lines window) pos))
310 ;;
311 ;; Update the modeline if needed.
312 (when (window-modeline-buffer window)
313 (let ((dl (window-modeline-dis-line window)))
314 (when (/= (dis-line-flags dl) unaltered-bits)
315 (unwind-protect
316 (progn
317 (funcall (tty-device-standout-init device) hunk)
318 (tty-smart-line-redisplay device hunk dl
319 (tty-hunk-modeline-pos hunk)))
320 (funcall (tty-device-standout-end device) hunk)))))))
321
322;;; NEXT-DIS-LINE is used in DO-SEMI-DUMB-LINE-WRITES and
323;;; COMPUTE-TTY-CHANGES.
324;;;
325(eval-when (:compile-toplevel :execute)
326(defmacro next-dis-line ()
327 `(progn
328 (setf prev dl)
329 (setf dl (cdr dl))
330 (setf flags (dis-line-flags (car dl)))))
331) ;eval-when
332
333;;; DO-SEMI-DUMB-LINE-WRITES does what it says until it hits the last
334;;; changed line. The commented out code was a gratuitous optimization,
335;;; especially if the first-changed line really is the first changes line.
336;;; Anyway, this had to be removed because of this function's use in
337;;; TTY-SMART-WINDOW-REDISPLAY, which was punting line moves due to
338;;; "Scroll Redraw Ratio". However, these supposedly moved lines had their
339;;; bits set to unaltered bits in COMPUTE-TTY-CHANGES because it was
340;;; assuming TTY-SMART-WINDOW-REDISPLAY guaranteed to do line moves.
341;;;
342(defun do-semi-dumb-line-writes (first-changed last-changed hunk)
343 (let* ((dl first-changed)
344 flags ;(dis-line-flags (car dl))) flags bound for NEXT-DIS-LINE.
345 prev)
346 ;;
347 ;; Skip old, unchanged, unmoved lines.
348 ;; (loop
349 ;; (unless (zerop flags) (return))
350 ;; (next-dis-line))
351 ;;
352 ;; Write every remaining line.
353 (let* ((device (device-hunk-device hunk))
354 (force-output (device-force-output device)))
355 (loop
356 (tty-semi-dumb-line-redisplay device hunk (car dl))
357 (when force-output (funcall force-output))
358 (next-dis-line)
359 (when (eq prev last-changed) (return))))))
360
361;;; TTY-SEMI-DUMB-LINE-REDISPLAY finds the first different character
362;;; comparing the display line and the screen image line, writes out the
363;;; rest of the display line, and clears to end-of-line as necessary.
364;;;
365(defun tty-semi-dumb-line-redisplay (device hunk dl
366 &optional (dl-pos (dis-line-position dl)))
367 (declare (fixnum dl-pos))
368 (let* ((dl-chars (dis-line-chars dl))
369 (dl-len (dis-line-length dl))
370 (dl-fonts (compute-font-usages dl)))
371 (declare (fixnum dl-len) (simple-string dl-chars))
372 (when (listen-editor-input *editor-input*)
373 (throw 'redisplay-catcher :editor-input))
374 (select-hunk hunk)
375 (let* ((screen-image-line (si-line (tty-device-screen-image device)
376 (+ *hunk-top-line* dl-pos)))
377 (si-line-chars (si-line-chars screen-image-line))
378 (si-line-length (si-line-length screen-image-line))
379 (findex (find-identical-prefix dl dl-fonts screen-image-line)))
380 (declare (type (or fixnum null) findex) (simple-string si-line-chars))
381 ;;
382 ;; When the dis-line and screen chars are not string=.
383 (when findex
384 (cond
385 ;; See if the screen shows an initial substring of the dis-line.
386 ((= findex si-line-length)
387 (funcall (tty-device-display-string device)
388 hunk findex dl-pos dl-chars dl-fonts findex dl-len)
389 (replace-si-line si-line-chars dl-chars findex findex dl-len))
390 ;; When the dis-line is an initial substring of what's on the screen.
391 ((= findex dl-len)
392 (funcall (tty-device-clear-to-eol device) hunk dl-len dl-pos))
393 ;; Otherwise, blast dl-chars and clear to eol as necessary.
394 (t (funcall (tty-device-display-string device)
395 hunk findex dl-pos dl-chars dl-fonts findex dl-len)
396 (when (< dl-len si-line-length)
397 (funcall (tty-device-clear-to-eol device) hunk dl-len dl-pos))
398 (replace-si-line si-line-chars dl-chars findex findex dl-len)))
399 (setf (si-line-length screen-image-line) dl-len)
400 (setf (si-line-fonts screen-image-line) dl-fonts)))
401 (setf (dis-line-flags dl) unaltered-bits)
402 (setf (dis-line-delta dl) 0)))
403
404
405
406
407;;;; Smart window redisplay -- operation queues and internal screen image.
408
409;;; This is used for creating temporary smart redisplay structures.
410;;;
411(defconstant tty-hunk-height-limit 100)
412
413
414;;; Queues for redisplay operations and access macros.
415;;;
416(defvar *tty-line-insertions* (make-array (* 2 tty-hunk-height-limit)))
417
418(defvar *tty-line-deletions* (make-array (* 2 tty-hunk-height-limit)))
419
420(defvar *tty-line-writes* (make-array tty-hunk-height-limit))
421
422(defvar *tty-line-moves* (make-array tty-hunk-height-limit))
423
424(eval-when (:compile-toplevel :execute)
425
426(defmacro queue (value queue ptr)
427 `(progn
428 (setf (svref ,queue ,ptr) ,value)
429 (the fixnum (incf (the fixnum ,ptr)))))
430
431(defmacro dequeue (queue ptr)
432 `(prog1
433 (svref ,queue ,ptr)
434 (the fixnum (incf (the fixnum ,ptr)))))
435
436) ;eval-when
437
438;;; INSERT-LINE-COUNT is used in TTY-SMART-WINDOW-REDISPLAY. The counting is
439;;; based on calls to QUEUE in COMPUTE-TTY-CHANGES.
440;;;
441(defun insert-line-count (ins)
442 (do ((i 1 (+ i 2))
443 (count 0 (+ count (svref *tty-line-insertions* i))))
444 ((> i ins) count)))
445
446
447;;; Temporary storage for screen-image lines and accessing macros.
448;;;
449(defvar *screen-image-temp* (make-array tty-hunk-height-limit))
450
451(eval-when (:compile-toplevel :execute)
452
453;;; DELETE-SI-LINES is used in DO-LINE-DELETIONS to simulate what's
454;;; happening to the screen in a device's screen-image. At y, num
455;;; lines are deleted and saved in *screen-image-temp*; fsil is the
456;;; end of the free screen image lines saved here. Also, we must
457;;; move lines up in the screen-image structure. In the outer loop
458;;; we save lines in the temp storage and move lines up at the same
459;;; time. In the termination/inner loop we move any lines that still
460;;; need to be moved up. The screen-length is adjusted by the fsil
461;;; because any time a deletion is in progress, there are fsil bogus
462;;; lines at the bottom of the screen image from lines being moved
463;;; up previously.
464;;;
465(defmacro delete-si-lines (screen-image y num fsil screen-length)
466 (let ((do-screen-image (gensym)) (delete-index (gensym))
467 (free-lines (gensym)) (source-index (gensym)) (target-index (gensym))
468 (n (gensym)) (do-screen-length (gensym)) (do-y (gensym)))
469 `(let ((,do-screen-image ,screen-image)
470 (,do-screen-length (- ,screen-length fsil))
471 (,do-y ,y))
472 (declare (fixnum ,do-screen-length ,do-y))
473 (do ((,delete-index ,do-y (1+ ,delete-index))
474 (,free-lines ,fsil (1+ ,free-lines))
475 (,source-index (+ ,do-y ,num) (1+ ,source-index))
476 (,n ,num (1- ,n)))
477 ((zerop ,n)
478 (do ((,target-index ,delete-index (1+ ,target-index))
479 (,source-index ,source-index (1+ ,source-index)))
480 ((>= ,source-index ,do-screen-length))
481 (declare (fixnum ,target-index ,source-index))
482 (setf (si-line ,do-screen-image ,target-index)
483 (si-line ,do-screen-image ,source-index))))
484 (declare (fixnum ,delete-index ,free-lines ,source-index ,n))
485 (setf (si-line *screen-image-temp* ,free-lines)
486 (si-line ,do-screen-image ,delete-index))
487 (when (< ,source-index ,do-screen-length)
488 (setf (si-line ,do-screen-image ,delete-index)
489 (si-line ,do-screen-image ,source-index)))))))
490
491
492;;; INSERT-SI-LINES is used in DO-LINE-INSERTIONS to simulate what's
493;;; happening to the screen in a device's screen-image. At y, num free
494;;; lines are inserted from *screen-image-temp*; fsil is the end of the
495;;; free lines. When copying lines down in screen-image, we must start
496;;; with the lower lines and end with the higher ones, so we don't trash
497;;; any lines. The outer loop does all the copying, and the termination/
498;;; inner loop inserts the free screen image lines, setting their length
499;;; to zero.
500;;;
501(defmacro insert-si-lines (screen-image y num fsil screen-length)
502 (let ((do-screen-image (gensym)) (source-index (gensym))
503 (target-index (gensym)) (target-terminus (gensym))
504 (do-screen-length (gensym)) (temp (gensym)) (do-y (gensym))
505 (insert-index (gensym)) (free-lines-index (gensym))
506 (n (gensym)))
507 `(let ((,do-screen-length ,screen-length)
508 (,do-screen-image ,screen-image)
509 (,do-y ,y))
510 (do ((,target-terminus (1- (+ ,do-y ,num))) ; (1- target-start)
511 (,source-index (- ,do-screen-length ,fsil 1) ; (1- source-end)
512 (1- ,source-index))
513 (,target-index (- (+ ,do-screen-length ,num)
514 ,fsil 1) ; (1- target-end)
515 (1- ,target-index)))
516 ((= ,target-index ,target-terminus)
517 (do ((,insert-index ,do-y (1+ ,insert-index))
518 (,free-lines-index (1- ,fsil) (1- ,free-lines-index))
519 (,n ,num (1- ,n)))
520 ((zerop ,n))
521 (declare (fixnum ,insert-index ,free-lines-index ,n))
522 (let ((,temp (si-line *screen-image-temp* ,free-lines-index)))
523 (setf (si-line-length ,temp) 0)
524 (setf (si-line-fonts ,temp) nil)
525 (setf (si-line ,do-screen-image ,insert-index) ,temp)))
526 (decf ,fsil ,num))
527 (declare (fixnum ,target-terminus ,source-index ,target-index))
528 (setf (si-line ,do-screen-image ,target-index)
529 (si-line ,do-screen-image ,source-index))))))
530
531) ;eval-when
532
533
534
535
536;;;; Smart window redisplay -- the function.
537
538;;; TTY-SMART-WINDOW-REDISPLAY sees if only one line changed after
539;;; some preliminary processing. If more than one line changed,
540;;; then we compute changes to make to the screen in the form of
541;;; line insertions, deletions, and writes. Deletions must be done
542;;; first, so lines are not lost off the bottom of the screen by
543;;; inserting lines.
544;;;
545(defun tty-smart-window-redisplay (window)
546 (let* ((hunk (window-hunk window))
547 (device (device-hunk-device hunk)))
548 (let ((first-changed (window-first-changed window))
549 (last-changed (window-last-changed window)))
550 ;; Is there anything to do?
551 (unless (eq first-changed the-sentinel)
552 (if (and (eq first-changed last-changed)
553 (zerop (dis-line-delta (car first-changed))))
554 ;; One line-changed.
555 (tty-smart-line-redisplay device hunk (car first-changed))
556 ;; More lines changed.
557 (multiple-value-bind (ins outs writes moves)
558 (compute-tty-changes
559 first-changed last-changed
560 (tty-hunk-modeline-pos hunk))
561 (let ((ratio (variable-value 'hemlock::scroll-redraw-ratio)))
562 (cond ((and ratio
563 (> (/ (insert-line-count ins)
564 (tty-hunk-text-height hunk))
565 ratio))
566 (do-semi-dumb-line-writes first-changed last-changed
567 hunk))
568 (t
569 (do-line-insertions hunk ins
570 (do-line-deletions hunk outs))
571 (note-line-moves moves)
572 (do-line-writes hunk writes))))))
573 ;; Set the bounds so we know we displayed...
574 (setf (window-first-changed window) the-sentinel
575 (window-last-changed window) (window-first-line window))))
576 ;;
577 ;; Clear any extra lines at the end of the window.
578 (let ((pos (dis-line-position (car (window-last-line window)))))
579 (when (< pos (1- (window-height window)))
580 (tty-smart-clear-to-eow hunk (1+ pos)))
581 (setf (window-old-lines window) pos))
582 ;;
583 ;; Update the modeline if needed.
584 (when (window-modeline-buffer window)
585 (let ((dl (window-modeline-dis-line window)))
586 (when (/= (dis-line-flags dl) unaltered-bits)
587 (unwind-protect
588 (progn
589 (funcall (tty-device-standout-init device) hunk)
590 (tty-smart-line-redisplay device hunk dl
591 (tty-hunk-modeline-pos hunk)))
592 (funcall (tty-device-standout-end device) hunk)))))))
593
594
595
596
597;;;; Smart window redisplay -- computing changes to the display.
598
599;;; There is a lot of documentation here to help since this code is not
600;;; obviously correct. The code is not that cryptic, but the correctness
601;;; of the algorithm is somewhat. Most of the complexity is in handling
602;;; lines that moved on the screen which the introduction deals with.
603;;; Also, the block of documentation immediately before the function
604;;; COMPUTE-TTY-CHANGES has its largest portion dedicated to this part of
605;;; the function which is the largest block of code in the function.
606
607;;; The window image dis-lines are annotated with the difference between
608;;; their current intended locations and their previous locations in the
609;;; window. This delta (distance moved) is negative for an upward move and
610;;; positive for a downward move. To determine what to do with moved
611;;; groups of lines, we consider the transition (or difference in deltas)
612;;; between two adjacent groups as we look at the window's dis-lines moving
613;;; down the window image, disregarding whether they are contiguous (having
614;;; moved only by a different delta) or separated by some lines (such as
615;;; lines that are new and unmoved).
616;;;
617;;; Considering the transition between moved groups makes sense because a
618;;; given group's delta affects all the lines below it since the dis-lines
619;;; reflect the window's buffer's actual lines which are all connected in
620;;; series. Therefore, if the previous group moved up some delta number of
621;;; lines because of line deletions, then the lines below this group (down
622;;; to the last line of the window image) moved up by the same delta too,
623;;; unless one of the following is true:
624;;; 1] The lines below the group moved up by a greater delta, possibly
625;;; due to multiple disjoint buffer line deletions.
626;;; 2] The lines below the group moved up by a lesser delta, possibly
627;;; due to a number (less than the previous delta) of new line
628;;; insertions below the group that moved up.
629;;; 3] The lines below the group moved down, possibly due to a number
630;;; (greater than the previous delta) of new line insertions below
631;;; the group that moved up.
632;;; Similarly, if the previous group moved down some delta number of lines
633;;; because of new line insertions, then the lines below this group (down
634;;; to the last line of the window image not to fall off the window's lower
635;;; edge) moved down by the same delta too, unless one of the following is
636;;; true:
637;;; 1] The lines below the group moved down by a greater delta, possibly
638;;; due to multiple disjoint buffer line insertions.
639;;; 2] The lines below the group moved down by a lesser delta, possibly
640;;; due to a number (less than the previous delta) of line deletions
641;;; below the group that moved down.
642;;; 3] The lines below the group moved up, possibly due to a number
643;;; (greater than the previous delta) of line deletions below the
644;;; group that moved down.
645;;;
646;;; Now we can see how the first moved group affects the window image below
647;;; it except where there is a lower group of lines that have moved a
648;;; different delta due to separate operations on the buffer's lines viewed
649;;; through a window. We can see that this different delta is the expected
650;;; effect throughout the window image below the second group, unless
651;;; something lower down again has affected the window image. Also, in the
652;;; case of a last group of lines that moved up, the group will never
653;;; reflect all of the lines in the window image from the first line to
654;;; move down to the bottom of the window image because somewhere down below
655;;; the group that moved up are some new lines that have just been drawn up
656;;; into the window's image.
657;;;
658
659;;; COMPUTE-TTY-CHANGES is used once in TTY-SMART-WINDOW-REDISPLAY.
660;;; It goes through all the display lines for a window recording where
661;;; lines need to be inserted, deleted, or written to make the screen
662;;; consistent with the internal image of the screen. Pointers to
663;;; the insertions, deletions, and writes that have to be done are
664;;; returned.
665;;;
666;;; If a line is new, then simply queue it to be written.
667;;;
668;;; If a line is moved and/or changed, then we compute the difference
669;;; between the last block of lines that moved with the same delta and the
670;;; current block of lines that moved with the current delta. If this
671;;; difference is positive, then some lines need to be deleted. Since we
672;;; do all the line deletions first to prevent line insertions from
673;;; dropping lines off the bottom of the screen, we have to compute the
674;;; position of line deletions using the cumulative insertions
675;;; (cum-inserts). Without any insertions, deletions may be done right at
676;;; the dis-line's new position. With insertions needed above a given
677;;; deletion point combined with the fact that deletions are all done
678;;; first, the location for the deletion is higher than it would be without
679;;; the insertions being done above the deletions. The location of the
680;;; deletion is higher by the number of insertions we have currently put
681;;; off. When computing the position of line insertions (a negative delta
682;;; transition), we do not need to consider the cumulative insertions or
683;;; cumulative deletions since everything above the point of insertion
684;;; (both deletions and insertions) has been done. Because of the screen
685;;; state being correct above the point of an insertion, the screen is only
686;;; off by the delta transition number of lines. After determining the
687;;; line insertions or deletions, loop over contiguous lines with the same
688;;; delta queuing any changed ones to be written. The delta and flag
689;;; fields are initialized according to the need to be written; since
690;;; redisplay may be interrupted by more user input after moves have been
691;;; done to the screen, we save the changed bit on, so the line will be
692;;; queued to be written after redisplay is re-entered.
693;;;
694;;; If the line is changed or new, then queue it to be written. Since we can
695;;; abort out of the actual dislpay at any time (due to pending input), we
696;;; don't clear the flags or delta here. A dis-line may be groveled many times
697;;; by this function before it actually makes it to the screen, so we may have
698;;; odd combinations of bits such as both new and changed.
699;;;
700;;; Otherwise, get the next display line, loop, and see if it's
701;;; interesting.
702;;;
703(defun compute-tty-changes (first-changed last-changed modeline-pos)
704 (declare (fixnum modeline-pos))
705 (let* ((dl first-changed)
706 (flags (dis-line-flags (car dl)))
707 (ins 0) (outs 0) (writes 0) (moves 0)
708 (prev-delta 0) (cum-deletes 0) (net-delta 0) (cum-inserts 0)
709 prev)
710 (declare (fixnum flags ins outs writes moves prev-delta cum-deletes
711 net-delta cum-inserts))
712 (loop
713 (cond
714 ((logtest flags new-bit)
715 (queue (car dl) *tty-line-writes* writes)
716 (next-dis-line))
717 ((logtest flags moved-bit)
718 (let* ((start-dl (car dl))
719 (start-pos (dis-line-position start-dl))
720 (curr-delta (dis-line-delta start-dl))
721 (delta-delta (- prev-delta curr-delta))
722 (car-dl start-dl))
723 (declare (fixnum start-pos curr-delta delta-delta))
724 (cond ((plusp delta-delta)
725 (queue (the fixnum (- start-pos cum-inserts))
726 *tty-line-deletions* outs)
727 (queue delta-delta *tty-line-deletions* outs)
728 (incf cum-deletes delta-delta)
729 (decf net-delta delta-delta))
730 ((minusp delta-delta)
731 (let ((eff-pos (the fixnum (+ start-pos delta-delta)))
732 (num (the fixnum (- delta-delta))))
733 (queue eff-pos *tty-line-insertions* ins)
734 (queue num *tty-line-insertions* ins)
735 (incf net-delta num)
736 (incf cum-inserts num))))
737 (loop
738 (if (logtest flags (logior changed-bit new-bit))
739 (queue car-dl *tty-line-writes* writes)
740 (queue car-dl *tty-line-moves* moves))
741 (next-dis-line)
742 (setf car-dl (car dl))
743 (when (or (eq prev last-changed)
744 (/= (the fixnum (dis-line-delta car-dl)) curr-delta))
745 (setf prev-delta curr-delta)
746 (return)))))
747 ((logtest flags (logior changed-bit new-bit))
748 (queue (car dl) *tty-line-writes* writes)
749 (next-dis-line))
750 (t
751 (next-dis-line)))
752
753 (when (eq prev last-changed)
754 (unless (zerop net-delta)
755 (cond ((plusp net-delta)
756 (queue (the fixnum (- modeline-pos cum-deletes net-delta))
757 *tty-line-deletions* outs)
758 (queue net-delta *tty-line-deletions* outs))
759 (t (queue (the fixnum (+ modeline-pos net-delta))
760 *tty-line-insertions* ins)
761 (queue (the fixnum (- net-delta))
762 *tty-line-insertions* ins))))
763 (return (values ins outs writes moves))))))
764
765
766
767;;;; Smart window redisplay -- operation methods.
768
769;;; TTY-SMART-CLEAR-TO-EOW clears lines y through the last text line of hunk.
770;;; It takes care not to clear a line unless it really has some characters
771;;; displayed on it. It also maintains the device's screen image lines.
772;;;
773(defun tty-smart-clear-to-eow (hunk y)
774 (let* ((device (device-hunk-device hunk))
775 (screen-image (tty-device-screen-image device))
776 (clear-to-eol (tty-device-clear-to-eol device)))
777 (select-hunk hunk)
778 (do ((y y (1+ y))
779 (si-idx (+ *hunk-top-line* y) (1+ si-idx))
780 (last (tty-hunk-text-position hunk)))
781 ((> si-idx last))
782 (declare (fixnum y si-idx last))
783 (let ((si-line (si-line screen-image si-idx)))
784 (unless (zerop (si-line-length si-line))
785 (funcall clear-to-eol hunk 0 y)
786 (setf (si-line-length si-line) 0)
787 (setf (si-line-fonts si-line) nil))))))
788
789;;; NOTE-LINE-MOVES -- Internal
790;;;
791;;; Clear out the flags and delta of lines that have been moved.
792;;;
793(defun note-line-moves (moves)
794 (let ((i 0))
795 (loop
796 (when (= i moves) (return))
797 (let ((dl (dequeue *tty-line-moves* i)))
798 (setf (dis-line-flags dl) unaltered-bits)
799 (setf (dis-line-delta dl) 0)))))
800
801;;; DO-LINE-DELETIONS pops elements off the *tty-lines-deletions* queue,
802;;; deleting lines from hunk's area of the screen. The internal screen
803;;; image is updated, and the total number of lines deleted is returned.
804;;;
805(defun do-line-deletions (hunk outs)
806 (declare (fixnum outs))
807 (let* ((i 0)
808 (device (device-hunk-device hunk))
809 (fun (tty-device-delete-line device))
810 (fsil 0)) ;free-screen-image-lines
811 (declare (fixnum i fsil))
812 (loop
813 (when (= i outs) (return fsil))
814 (let ((y (dequeue *tty-line-deletions* i))
815 (num (dequeue *tty-line-deletions* i)))
816 (declare (fixnum y num))
817 (funcall fun hunk 0 y num)
818 (select-hunk hunk)
819 (delete-si-lines (tty-device-screen-image device)
820 (+ *hunk-top-line* y) num fsil
821 (tty-device-lines device))
822 (incf fsil num)))))
823
824;;; DO-LINE-INSERTIONS pops elements off the *tty-line-insertions* queue,
825;;; inserting lines into hunk's area of the screen. The internal screen
826;;; image is updated using free screen image lines pointed to by fsil.
827;;;
828(defun do-line-insertions (hunk ins fsil)
829 (declare (fixnum ins fsil))
830 (let* ((i 0)
831 (device (device-hunk-device hunk))
832 (fun (tty-device-open-line device)))
833 (declare (fixnum i))
834 (loop
835 (when (= i ins) (return))
836 (let ((y (dequeue *tty-line-insertions* i))
837 (num (dequeue *tty-line-insertions* i)))
838 (declare (fixnum y num))
839 (funcall fun hunk 0 y num)
840 (select-hunk hunk)
841 (insert-si-lines (tty-device-screen-image device)
842 (+ *hunk-top-line* y) num fsil
843 (tty-device-lines device))))))
844
845;;; DO-LINE-WRITES pops elements off the *tty-line-writes* queue, displaying
846;;; these dis-lines with TTY-SMART-LINE-REDISPLAY. We force output after
847;;; each line, so the user can see how far we've gotten in case he chooses
848;;; to give more editor commands which will abort redisplay until there's no
849;;; more input.
850;;;
851(defun do-line-writes (hunk writes)
852 (declare (fixnum writes))
853 (let* ((i 0)
854 (device (device-hunk-device hunk))
855 (force-output (device-force-output device)))
856 (declare (fixnum i))
857 (loop
858 (when (= i writes) (return))
859 (tty-smart-line-redisplay device hunk (dequeue *tty-line-writes* i))
860 (when force-output (funcall force-output)))))
861
862;;; TTY-SMART-LINE-REDISPLAY uses an auxiliary screen image structure to
863;;; try to do minimal character shipping to the terminal. Roughly, we find
864;;; the first different character when comparing what's on the screen and
865;;; what should be there; we will start altering the line after this same
866;;; initial substring. Then we find, from the end, the first character
867;;; that is different, blasting out characters to the lesser of the two
868;;; indexes. If the dis-line index is lesser, we have some characters to
869;;; delete from the screen, and if the screen index is lesser, we have some
870;;; additional dis-line characters to insert. There are a few special
871;;; cases that allow us to punt out of the above algorithm sketch. If the
872;;; terminal doesn't have insert mode or delete mode, we have blast out to
873;;; the end of the dis-line and possibly clear to the end of the screen's
874;;; line, as appropriate. Sometimes we don't use insert or delete mode
875;;; because of the overhead cost in characters; it simply is cheaper to
876;;; blast out characters and clear to eol.
877;;;
878(defun tty-smart-line-redisplay (device hunk dl
879 &optional (dl-pos (dis-line-position dl)))
880 (declare (fixnum dl-pos))
881 (let* ((dl-chars (dis-line-chars dl))
882 (dl-len (dis-line-length dl))
883 (dl-fonts (compute-font-usages dl)))
884 (declare (fixnum dl-len) (simple-string dl-chars))
885 (when (listen-editor-input *editor-input*)
886 (throw 'redisplay-catcher :editor-input))
887 (select-hunk hunk)
888 (let* ((screen-image-line (si-line (tty-device-screen-image device)
889 (+ *hunk-top-line* dl-pos)))
890 (si-line-chars (si-line-chars screen-image-line))
891 (si-line-length (si-line-length screen-image-line))
892 (findex (find-identical-prefix dl dl-fonts screen-image-line)))
893 (declare (type (or fixnum null) findex) (simple-string si-line-chars))
894 ;;
895 ;; When the dis-line and screen chars are not string=.
896 (when findex
897 (block tslr-main-body
898 ;;
899 ;; See if the screen shows an initial substring of the dis-line.
900 (when (= findex si-line-length)
901 (funcall (tty-device-display-string device)
902 hunk findex dl-pos dl-chars dl-fonts findex dl-len)
903 (replace-si-line si-line-chars dl-chars findex findex dl-len)
904 (return-from tslr-main-body t))
905 ;;
906 ;; When the dis-line is an initial substring of what's on the screen.
907 (when (= findex dl-len)
908 (funcall (tty-device-clear-to-eol device) hunk dl-len dl-pos)
909 (return-from tslr-main-body t))
910 ;;
911 ;; Find trailing substrings that are the same.
912 (multiple-value-bind
913 (sindex dindex)
914 (let ((count (find-identical-suffix dl dl-fonts
915 screen-image-line)))
916 (values (- si-line-length count)
917 (- dl-len count)))
918 (declare (fixnum sindex dindex))
919 ;;
920 ;; No trailing substrings -- blast and clear to eol.
921 (when (= dindex dl-len)
922 (funcall (tty-device-display-string device)
923 hunk findex dl-pos dl-chars dl-fonts findex dl-len)
924 (when (< dindex sindex)
925 (funcall (tty-device-clear-to-eol device)
926 hunk dl-len dl-pos))
927 (replace-si-line si-line-chars dl-chars findex findex dl-len)
928 (return-from tslr-main-body t))
929 (let ((lindex (min sindex dindex)))
930 (cond ((< lindex findex)
931 ;; This can happen in funny situations -- believe me!
932 (setf lindex findex))
933 (t
934 (funcall (tty-device-display-string device)
935 hunk findex dl-pos dl-chars dl-fonts
936 findex lindex)
937 (replace-si-line si-line-chars dl-chars
938 findex findex lindex)))
939 (cond
940 ((= dindex sindex))
941 ((< dindex sindex)
942 (let ((delete-char-num (- sindex dindex)))
943 (cond ((and (tty-device-delete-char device)
944 (worth-using-delete-mode
945 device delete-char-num (- si-line-length dl-len)))
946 (funcall (tty-device-delete-char device)
947 hunk dindex dl-pos delete-char-num))
948 (t
949 (funcall (tty-device-display-string device)
950 hunk dindex dl-pos dl-chars dl-fonts
951 dindex dl-len)
952 (funcall (tty-device-clear-to-eol device)
953 hunk dl-len dl-pos)))))
954 (t
955 (if (and (tty-device-insert-string device)
956 (worth-using-insert-mode device (- dindex sindex)
957 (- dl-len sindex)))
958 (funcall (tty-device-insert-string device)
959 hunk sindex dl-pos dl-chars sindex dindex)
960 (funcall (tty-device-display-string device)
961 hunk sindex dl-pos dl-chars dl-fonts
962 sindex dl-len))))
963 (replace-si-line si-line-chars dl-chars
964 lindex lindex dl-len))))
965 (setf (si-line-length screen-image-line) dl-len)
966 (setf (si-line-fonts screen-image-line) dl-fonts)))
967 (setf (dis-line-flags dl) unaltered-bits)
968 (setf (dis-line-delta dl) 0)))
969
970
971
972
973;;;; Device methods
974
975;;; Initializing and exiting the device (DEVICE-INIT and DEVICE-EXIT functions).
976;;; These can be found in Tty-Display-Rt.Lisp.
977
978
979;;; Clearing the device (DEVICE-CLEAR functions).
980
981(defun clear-device (device)
982 (device-write-string (tty-device-clear-string device))
983 (cursor-motion device 0 0)
984 (setf (tty-device-cursor-x device) 0)
985 (setf (tty-device-cursor-y device) 0))
986
987
988;;; Moving the cursor around (DEVICE-PUT-CURSOR)
989
990;;; TTY-PUT-CURSOR makes sure the coordinates are mapped from the hunk's
991;;; axis to the screen's and determines the minimal cost cursor motion
992;;; sequence. Currently, it does no cost analysis of relative motion
993;;; compared to absolute motion but simply makes sure the cursor isn't
994;;; already where we want it.
995;;;
996(defun tty-put-cursor (hunk x y)
997 (declare (fixnum x y))
998 (select-hunk hunk)
999 (let ((y (the fixnum (+ *hunk-top-line* y)))
1000 (device (device-hunk-device hunk)))
1001 (declare (fixnum y))
1002 (unless (and (= (the fixnum (tty-device-cursor-x device)) x)
1003 (= (the fixnum (tty-device-cursor-y device)) y))
1004 (cursor-motion device x y)
1005 (setf (tty-device-cursor-x device) x)
1006 (setf (tty-device-cursor-y device) y))))
1007
1008;;; UPDATE-CURSOR is used in device redisplay methods to make sure the
1009;;; cursor is where it should be.
1010;;;
1011(eval-when (:compile-toplevel :execute)
1012 (defmacro update-cursor (hunk x y)
1013 `(funcall (device-put-cursor (device-hunk-device ,hunk)) ,hunk ,x ,y))
1014) ;eval-when
1015
1016;;; CURSOR-MOTION takes two coordinates on the screen's axis,
1017;;; moving the cursor to that location. X is the column index,
1018;;; and y is the line index, but Unix and Termcap believe that
1019;;; the default order of indexes is first the line and then the
1020;;; column or (y,x). Because of this, when reversep is non-nil,
1021;;; we send first x and then y.
1022;;;
1023(defun cursor-motion (device x y)
1024 (let ((x-add-char (tty-device-cm-x-add-char device))
1025 (y-add-char (tty-device-cm-y-add-char device))
1026 (x-condx-add (tty-device-cm-x-condx-char device))
1027 (y-condx-add (tty-device-cm-y-condx-char device))
1028 (one-origin (tty-device-cm-one-origin device)))
1029 (when x-add-char (incf x x-add-char))
1030 (when (and x-condx-add (> x x-condx-add))
1031 (incf x (tty-device-cm-x-condx-add-char device)))
1032 (when y-add-char (incf y y-add-char))
1033 (when (and y-condx-add (> y y-condx-add))
1034 (incf y (tty-device-cm-y-condx-add-char device)))
1035 (when one-origin (incf x) (incf y)))
1036 (device-write-string (tty-device-cm-string1 device))
1037 (let ((reversep (tty-device-cm-reversep device))
1038 (x-pad (tty-device-cm-x-pad device))
1039 (y-pad (tty-device-cm-y-pad device)))
1040 (if reversep
1041 (cm-output-coordinate x x-pad)
1042 (cm-output-coordinate y y-pad))
1043 (device-write-string (tty-device-cm-string2 device))
1044 (if reversep
1045 (cm-output-coordinate y y-pad)
1046 (cm-output-coordinate x x-pad))
1047 (device-write-string (tty-device-cm-string3 device))))
1048
1049;;; CM-OUTPUT-COORDINATE outputs the coordinate with respect to the pad. If
1050;;; there is a pad, then the coordinate needs to be sent as digit-char's (for
1051;;; each digit in the coordinate), and if there is no pad, the coordinate needs
1052;;; to be converted into a character. Using CODE-CHAR here is not really
1053;;; portable. With a pad, the coordinate buffer is filled from the end as we
1054;;; truncate the coordinate by 10, generating ones digits.
1055;;;
1056(defconstant cm-coordinate-buffer-len 5)
1057(defvar *cm-coordinate-buffer* (make-string cm-coordinate-buffer-len))
1058;;;
1059(defun cm-output-coordinate (coordinate pad)
1060 (cond (pad
1061 (let ((i (1- cm-coordinate-buffer-len)))
1062 (loop
1063 (when (= i -1) (error "Terminal has too many lines!"))
1064 (multiple-value-bind (tens ones)
1065 (truncate coordinate 10)
1066 (setf (schar *cm-coordinate-buffer* i) (digit-char ones))
1067 (when (zerop tens)
1068 (dotimes (n (- pad (- cm-coordinate-buffer-len i)))
1069 (decf i)
1070 (setf (schar *cm-coordinate-buffer* i) #\0))
1071 (device-write-string *cm-coordinate-buffer* i
1072 cm-coordinate-buffer-len)
1073 (return))
1074 (decf i)
1075 (setf coordinate tens)))))
1076 (t (tty-write-char (code-char coordinate)))))
1077
1078
1079;;; Writing strings (TTY-DEVICE-DISPLAY-STRING functions)
1080
1081;;; DISPLAY-STRING is used to put a string at (x,y) on the device.
1082;;;
1083(defun display-string (hunk x y string font-info
1084 &optional (start 0) (end (strlen string)))
1085 (declare (fixnum x y start end))
1086 (update-cursor hunk x y)
1087 ;; Ignore font info for chars before the start of the string.
1088 (loop
1089 (if (or (null font-info)
1090 (< start (cddar font-info)))
1091 (return)
1092 (pop font-info)))
1093 (let ((posn start))
1094 (dolist (next-font font-info)
1095 (let ((font (car next-font))
1096 (start (cadr next-font))
1097 (stop (cddr next-font)))
1098 (when (<= end start)
1099 (return))
1100 (when (< posn start)
1101 (device-write-string string posn start)
1102 (setf posn start))
1103 (let ((new-posn (min stop end))
1104 (font-strings (aref *tty-font-strings* font)))
1105 (unwind-protect
1106 (progn
1107 (device-write-string (car font-strings))
1108 (device-write-string string posn new-posn))
1109 (device-write-string (cdr font-strings)))
1110 (setf posn new-posn))))
1111 (when (< posn end)
1112 (device-write-string string posn end)))
1113 (setf (tty-device-cursor-x (device-hunk-device hunk))
1114 (the fixnum (+ x (the fixnum (- end start))))))
1115
1116;;; DISPLAY-STRING-CHECKING-UNDERLINES is used for terminals that special
1117;;; case underlines doing an overstrike when they don't otherwise overstrike.
1118;;; Note: we do not know in this code whether the terminal can backspace (or
1119;;; what the sequence is), whether the terminal has insert-mode, or whether
1120;;; the terminal has delete-mode.
1121;;;
1122(defun display-string-checking-underlines (hunk x y string font-info
1123 &optional (start 0)
1124 (end (strlen string)))
1125 (declare (ignore font-info))
1126 (declare (fixnum x y start end) (simple-string string))
1127 (update-cursor hunk x y)
1128 (let ((upos (position #\_ string :test #'char= :start start :end end))
1129 (device (device-hunk-device hunk)))
1130 (if upos
1131 (let ((previous start)
1132 (after-pos 0))
1133 (declare (fixnum previous after-pos))
1134 (loop (device-write-string string previous upos)
1135 (setf after-pos (do ((i (1+ upos) (1+ i)))
1136 ((or (= i end)
1137 (char/= (schar string i) #\_)) i)
1138 (declare (fixnum i))))
1139 (let ((ulen (the fixnum (- after-pos upos)))
1140 (cursor-x (the fixnum (+ x (the fixnum
1141 (- after-pos start))))))
1142 (declare (fixnum ulen))
1143 (dotimes (i ulen) (tty-write-char #\space))
1144 (setf (tty-device-cursor-x device) cursor-x)
1145 (update-cursor hunk upos y)
1146 (dotimes (i ulen) (tty-write-char #\_))
1147 (setf (tty-device-cursor-x device) cursor-x))
1148 (setf previous after-pos)
1149 (setf upos (position #\_ string :test #'char=
1150 :start previous :end end))
1151 (unless upos
1152 (device-write-string string previous end)
1153 (return))))
1154 (device-write-string string start end))
1155 (setf (tty-device-cursor-x device)
1156 (the fixnum (+ x (the fixnum (- end start)))))))
1157
1158
1159;;; DEVICE-WRITE-STRING is used to shove a string at the terminal regardless
1160;;; of cursor position.
1161;;;
1162(defun device-write-string (string &optional (start 0) (end (strlen string)))
1163 (declare (fixnum start end))
1164 (unless (= start end)
1165 (tty-write-string string start (the fixnum (- end start)))))
1166
1167
1168;;; Clearing lines (TTY-DEVICE-CLEAR-TO-EOL, DEVICE-CLEAR-LINES, and
1169;;; TTY-DEVICE-CLEAR-TO-EOW functions.)
1170
1171(defun clear-to-eol (hunk x y)
1172 (update-cursor hunk x y)
1173 (device-write-string
1174 (tty-device-clear-to-eol-string (device-hunk-device hunk))))
1175
1176(defun space-to-eol (hunk x y)
1177 (declare (fixnum x))
1178 (update-cursor hunk x y)
1179 (let* ((device (device-hunk-device hunk))
1180 (num (- (the fixnum (tty-device-columns device))
1181 x)))
1182 (declare (fixnum num))
1183 (dotimes (i num) (tty-write-char #\space))
1184 (setf (tty-device-cursor-x device) (+ x num))))
1185
1186(defun clear-lines (hunk x y n)
1187 (let* ((device (device-hunk-device hunk))
1188 (clear-to-eol (tty-device-clear-to-eol device)))
1189 (funcall clear-to-eol hunk x y)
1190 (do ((y (1+ y) (1+ y))
1191 (count (1- n) (1- count)))
1192 ((zerop count)
1193 (setf (tty-device-cursor-x device) 0)
1194 (setf (tty-device-cursor-y device) (1- y)))
1195 (declare (fixnum count y))
1196 (funcall clear-to-eol hunk 0 y))))
1197
1198(defun clear-to-eow (hunk x y)
1199 (declare (fixnum x y))
1200 (funcall (tty-device-clear-lines (device-hunk-device hunk))
1201 hunk x y
1202 (the fixnum (- (the fixnum (tty-hunk-text-height hunk)) y))))
1203
1204
1205;;; Opening and Deleting lines (TTY-DEVICE-OPEN-LINE and TTY-DEVICE-DELETE-LINE)
1206
1207(defun open-tty-line (hunk x y &optional (n 1))
1208 (update-cursor hunk x y)
1209 (dotimes (i n)
1210 (device-write-string (tty-device-open-line-string (device-hunk-device hunk)))))
1211
1212(defun delete-tty-line (hunk x y &optional (n 1))
1213 (update-cursor hunk x y)
1214 (dotimes (i n)
1215 (device-write-string (tty-device-delete-line-string (device-hunk-device hunk)))))
1216
1217
1218;;; Insert and Delete modes (TTY-DEVICE-INSERT-STRING and TTY-DEVICE-DELETE-CHAR)
1219
1220(defun tty-insert-string (hunk x y string
1221 &optional (start 0) (end (strlen string)))
1222 (declare (fixnum x y start end))
1223 (update-cursor hunk x y)
1224 (let* ((device (device-hunk-device hunk))
1225 (init-string (tty-device-insert-init-string device))
1226 (char-init-string (tty-device-insert-char-init-string device))
1227 (char-end-string (tty-device-insert-char-end-string device))
1228 (end-string (tty-device-insert-end-string device)))
1229 (declare (type (or simple-string null) char-init-string char-end-string))
1230 (when init-string (device-write-string init-string))
1231 (if char-init-string
1232 (let ((cis-len (length char-init-string))
1233 (ces-len (length char-end-string)))
1234 (do ((i start (1+ i)))
1235 ((= i end))
1236 (device-write-string char-init-string 0 cis-len)
1237 (tty-write-char (schar string i))
1238 (when char-end-string
1239 (device-write-string char-end-string 0 ces-len))))
1240 (device-write-string string start end))
1241 (when end-string (device-write-string end-string))
1242 (setf (tty-device-cursor-x device)
1243 (the fixnum (+ x (the fixnum (- end start)))))))
1244
1245(defun worth-using-insert-mode (device insert-char-num chars-saved)
1246 (let* ((init-string (tty-device-insert-init-string device))
1247 (char-init-string (tty-device-insert-char-init-string device))
1248 (char-end-string (tty-device-insert-char-end-string device))
1249 (end-string (tty-device-insert-end-string device))
1250 (cost 0))
1251 (when init-string (incf cost (length (the simple-string init-string))))
1252 (when char-init-string
1253 (incf cost (* insert-char-num (+ (length (the simple-string
1254 char-init-string))
1255 (if char-end-string
1256 (length (the simple-string
1257 char-end-string))
1258 0)))))
1259 (when end-string (incf cost (length (the simple-string end-string))))
1260 (< cost chars-saved)))
1261
1262(defun delete-char (hunk x y &optional (n 1))
1263 (declare (fixnum x y n))
1264 (update-cursor hunk x y)
1265 (let* ((device (device-hunk-device hunk))
1266 (init-string (tty-device-delete-init-string device))
1267 (end-string (tty-device-delete-end-string device))
1268 (delete-char-string (tty-device-delete-char-string device)))
1269 (when init-string (device-write-string init-string))
1270 (dotimes (i n)
1271 (device-write-string delete-char-string))
1272 (when end-string (device-write-string end-string))))
1273
1274(defun worth-using-delete-mode (device delete-char-num clear-char-num)
1275 (declare (fixnum delete-char-num clear-char-num))
1276 (let ((init-string (tty-device-delete-init-string device))
1277 (end-string (tty-device-delete-end-string device))
1278 (delete-char-string (tty-device-delete-char-string device))
1279 (clear-to-eol-string (tty-device-clear-to-eol-string device))
1280 (cost 0))
1281 (declare (type (or simple-string null) init-string end-string
1282 delete-char-string)
1283 (fixnum cost))
1284 (when init-string (incf cost (the fixnum (length init-string))))
1285 (when end-string (incf cost (the fixnum (length end-string))))
1286 (incf cost (the fixnum
1287 (* (the fixnum (length delete-char-string))
1288 delete-char-num)))
1289 (< cost (+ delete-char-num
1290 (if clear-to-eol-string
1291 (length clear-to-eol-string)
1292 clear-char-num)))))
1293
1294
1295;;; Standout mode (TTY-DEVICE-STANDOUT-INIT and TTY-DEVICE-STANDOUT-END)
1296
1297(defun standout-init (hunk)
1298 (device-write-string
1299 (tty-device-standout-init-string (device-hunk-device hunk))))
1300
1301(defun standout-end (hunk)
1302 (device-write-string
1303 (tty-device-standout-end-string (device-hunk-device hunk))))
Note: See TracBrowser for help on using the repository browser.