      (:PIXEL)
      (:CHARACTER (SETQ X (* X (PC-PPR-CHAR-WIDTH TV-STREAM-PC-PPR)))
		  (SETQ Y (* Y (PC-PPR-LINE-HEIGHT TV-STREAM-PC-PPR))))
      (:OTHERWISE (FERROR NIL "~S is not a known unit." UNIT)))
    (TV-SET-CURSORPOS-RELATIVE TV-STREAM-PC-PPR X Y))
  (:SIZE-IN-CHARACTERS ()
    (MVRETURN (// (- (PC-PPR-RIGHT-MARGIN TV-STREAM-PC-PPR)
		     (PC-PPR-LEFT-MARGIN TV-STREAM-PC-PPR))
		  (PC-PPR-CHAR-WIDTH TV-STREAM-PC-PPR))
	      (// (- (PC-PPR-BOTTOM-MARGIN TV-STREAM-PC-PPR)
		     (PC-PPR-TOP-MARGIN TV-STREAM-PC-PPR))
		  (PC-PPR-LINE-HEIGHT TV-STREAM-PC-PPR))))

  ;; Compute the new cursor position if STRING were output at the specified
  ;; point.  NIL for X-POS and Y-POS mean use the current cursor position.
  ;; X-POS and Y-POS are in pixels.
  (:COMPUTE-MOTION (X-POS Y-POS STRING &OPTIONAL (BEGIN 0) (END (STRING-LENGTH STRING)))
    (TV-COMPUTE-MOTION TV-STREAM-PC-PPR X-POS Y-POS STRING BEGIN END))

  ;; Compute the motion for printing a string and then move the cursor there.
  ;; This eliminates the problem of knowing whether to use the :PIXEL or :CHARACTER
  ;; unit when calling :SET-CURSORPOS.  Since the string is passed in here as an
  ;; argument, this stream need know nothing about the typein buffer maintained by the
  ;; editor stream.  This might be better named :MOVE-OVER-STRING.
  (:CURSOR-MOTION (X-POS Y-POS STRING &OPTIONAL (BEGIN 0) (END (STRING-LENGTH STRING)))
    (MULTIPLE-VALUE-BIND (X Y HOW-FAR)
	(TV-COMPUTE-MOTION TV-STREAM-PC-PPR X-POS Y-POS STRING BEGIN END)
      ;; Check for page wrap-around.
      (IF HOW-FAR
	  (MULTIPLE-VALUE (X Y) (TV-COMPUTE-MOTION TV-STREAM-PC-PPR 0 0 STRING HOW-FAR END)))
      (TV-SET-CURSORPOS TV-STREAM-PC-PPR X Y)))

  ;; This assumes that the cursor is positioned before the string to be underlined.
  ;; The operation should be defined to xor, so that underlining twice erases.
  (:UNDERLINE (STRING &OPTIONAL (BEGIN 0) (END (STRING-LENGTH STRING))
		      &AUX (X1 (PC-PPR-CURRENT-X TV-STREAM-PC-PPR))
			   (Y1 (+ (PC-PPR-CURRENT-Y TV-STREAM-PC-PPR)
				  (PC-PPR-LINE-HEIGHT TV-STREAM-PC-PPR))))
    (MULTIPLE-VALUE-BIND (X2 Y2)
	(TV-COMPUTE-MOTION TV-STREAM-PC-PPR X1 Y1 STRING BEGIN END)
      (TV-DRAW-LINE X1 Y1 X2 Y2 TV-ALU-XOR (PC-PPR-SCREEN TV-STREAM-PC-PPR))))

  ;; Font hackery.  FONT-LIST is a list of strings describing fonts, since
  ;; internal font representations are per-stream.  Try to avoid calling
  ;; TV-REDEFINE-PC-PPR whenever a font change is made since this reallocates
  ;; a new font map rather than reusing the old one.  If the typein buffer is
  ;; to contain font changes, then it should contain 16-bit characters.
  (:SET-FONTS (&REST FONT-LIST)
    (TV-REDEFINE-PC-PPR TV-STREAM-PC-PPR ':FONTS
			(MAPCAR #'(LAMBDA (FONT)
				    (SETQ FONT (INTERN FONT "FONTS"))
				    (IF (BOUNDP FONT) (SYMEVAL FONT) FONTS:CPTFONT))
				FONT-LIST)))
  (:SET-CURRENT-FONT (N)
    (TV-TYO TV-STREAM-PC-PPR (+ 240 N)))
  (:CHAR-WIDTH (&OPTIONAL CHAR FONT)
    (COND ((NOT CHAR) (PC-PPR-CHAR-WIDTH TV-STREAM-PC-PPR))
	  (T (SETQ FONT (IF (FIXP FONT)
			    (AREF (PC-PPR-FONT-MAP TV-STREAM-PC-PPR))
			    (PC-PPR-CURRENT-FONT TV-STREAM-PC-PPR)))
	     (TV-CHAR-WIDTH TV-STREAM-PC-PPR CHAR FONT))))
  (:LINE-HEIGHT () (PC-PPR-LINE-HEIGHT TV-STREAM-PC-PPR))
  (:STRING-WIDTH (STRING &OPTIONAL (BEGIN 0) (END (STRING-LENGTH STRING)))
    (TV-STRING-LENGTH TV-STREAM-PC-PPR STRING BEGIN END))

  (:DRAW-LINE (X0 Y0 X1 Y1 &OPTIONAL (TV-ALU TV-ALU-IOR))
    (LET ((TOP (PC-PPR-TOP TV-STREAM-PC-PPR))
	  (LEFT (PC-PPR-LEFT TV-STREAM-PC-PPR)))
      (TV-DRAW-LINE (+ LEFT X0) (+ TOP Y0) (+ LEFT X1) (+ TOP Y1) TV-ALU
		    TV-DEFAULT-SCREEN)))

  ;; The character or string to be inserted is passed along so that variable
  ;; width fonts can work correctly.  Fixed width font console
  ;; streams can ignore this argument.  By default, the characters are printed
  ;; in the newly created whitespace since this is what happens most of the time.
  ;; :INSERT-STRING and :DELETE-STRING both assume that the strings contain no newlines.
  (:INSERT-CHAR (&OPTIONAL (CHAR #\SPACE) (COUNT 1))
    (TV-INSERT-WIDTH TV-STREAM-PC-PPR
		     (* COUNT (TV-CHAR-WIDTH TV-STREAM-PC-PPR CHAR
					     (PC-PPR-CURRENT-FONT TV-STREAM-PC-PPR))))
    (DOTIMES (I COUNT) (TV-TYO TV-STREAM-PC-PPR CHAR)))
  (:INSERT-STRING (STRING &OPTIONAL (BEGIN 0) (END (STRING-LENGTH STRING)))
    (TV-INSERT-WIDTH TV-STREAM-PC-PPR (TV-STRING-LENGTH TV-STREAM-PC-PPR STRING BEGIN END))
    (TV-STRING-OUT TV-STREAM-PC-PPR STRING BEGIN END))
  (:DELETE-CHAR (&OPTIONAL (CHAR #\SPACE) (COUNT 1))
    (TV-DELETE-WIDTH TV-STREAM-PC-PPR
		     (* COUNT (TV-CHAR-WIDTH TV-STREAM-PC-PPR CHAR
					     (PC-PPR-CURRENT-FONT TV-STREAM-PC-PPR)))))
  (:DELETE-STRING (STRING &OPTIONAL (BEGIN 0) (END (STRING-LENGTH STRING)))
    (TV-DELETE-WIDTH TV-STREAM-PC-PPR (TV-STRING-LENGTH TV-STREAM-PC-PPR STRING BEGIN END)))
  
  (:CLEAR-SCREEN () (TV-CLEAR-PC-PPR TV-STREAM-PC-PPR))
  (:CLEAR-EOL	 () (TV-CLEAR-EOL TV-STREAM-PC-PPR))
  
  ;; Operations particular to this type of stream
  (:PC-PPR () TV-STREAM-PC-PPR)
  (:SET-PC-PPR (PC-PPR) (SETQ TV-STREAM-PC-PPR PC-PPR))
  )

(DEFUN TV-STREAM-DEFAULT-HANDLER (OP &OPTIONAL ARG1 &REST REST)
