(FILECREATED " 5-Oct-84 18:06:17" {ERIS}<LISPCORE>SOURCES>CHAT.;12 50914  

      changes to:  (FNS CHAT.TYPEIN CHAT FIND.CHAT.PROTOCOL CHAT.RESHAPEWINDOW)

      previous date: "25-Sep-84 16:32:15" {ERIS}<LISPCORE>SOURCES>CHAT.;11)


(* Copyright (c) 1982, 1983, 1984 by Xerox Corporation. All rights reserved.)

(PRETTYCOMPRINT CHATCOMS)

(RPAQQ CHATCOMS [(COMS (* CHAT typein)
		       (FNS CHAT CHAT.INIT FIND.CHAT.PROTOCOL CHAT.TYPEIN CHAT.BIN CHAT.CLOSE 
			    CHAT.CLOSEFN CHAT.CLOSE.CONNECTION CHAT.LOGIN))
	(COMS (* Chat streams)
	      (FNS ADD.CHAT.MESSAGE CHAT.LOGINFO CHAT.SENDSCREENPARAMS CHAT.SETDISPLAYTYPE 
		   CHAT.LOGINFO CHAT.FLUSH&WAIT CHAT.ENDOFSTREAMOP CHAT.OPTIONMENU))
	(COMS (* CHAT typeout and DM simulation)
	      (FNS CHAT.TYPEOUT CHAT.RESET.DISPLAY.PARMS CHAT.DID.RESHAPE DMCHAT.HANDLECHARACTER 
		   CHAT.SCREENPARAMS CHAT.ADDCHAR CHAT.ADDLINE DMCHAT.ADDRESS CHAT.CLEAR 
		   CHAT.CLEARMODES CHAT.DELCHAR CHAT.DELETELINE CHAT.DOWN CHAT.ERASE.TO.EOL 
		   CHAT.ERASEBITS CHAT.HOME CHAT.LEFT DMCHAT.NEWLINE DMCHAT.PRINTCHAR DMCHAT.RIGHT 
		   CHAT.UP))
	(COMS (* window stuff)
	      (FNS GETCHATWINDOW CHAT.BUTTONFN CHAT.HOLD CHAT.MENU CHAT.CLEAR.FROM.MENU 
		   CHAT.TAKE.INPUT CHAT.TAKE.INPUT1 DO.CHAT.OPTION CHAT.RECONNECT CHAT.RESHAPEWINDOW 
		   CHAT.TTYENTRYFN CHAT.TTYEXITFN CHAT.TYPESCRIPT CHAT.TYPESCRIPT1))
	(COMS (* for EMACS)
	      (FNS CHAT.EMACS.MOVE CHAT.SWITCH.EMACS))
	(ADDVARS (CHAT.DRIVERTYPES (DM2500 . DMCHAT.HANDLECHARACTER)))
	(INITVARS (CHAT.EMULATORTYPE (QUOTE DM2500))
		  (CHAT.DISPLAYTYPE 10)
		  (CHAT.METACHAR 195)
		  (CHAT.CONTROLCHAR 193)
		  (CHAT.INTERRUPTS)
		  (CHAT.KEYACTIONS)
		  (DEFAULTCHATHOST)
		  (CHATDEBUGFLG)
		  (CHATWINDOWLST)
		  (CHATWINDOW)
		  (CHAT.AUTOCRLF T)
		  (CLOSECHATWINDOWFLG)
		  (CHAT.ALLHOSTS)
		  (CHAT.HOSTMENU)
		  (CHAT.FONT)
		  (CHAT.IN.EMACS? NIL)
		  (CHAT.EMACSCOMMANDS (QUOTE (21 16 14 6 1)))
		  (CHAT.WAIT.TIME 2000)
		  (CHAT.PROTOCOLS))
	(VARS (CHATMENU)
	      (CHAT.REOPENMENU)
	      CHATMENUITEMS NETWORKLOGINFO)
	(DECLARE: EVAL@COMPILE DONTCOPY (LOCALVARS . T)
		  (COMS * CHATDEFS))
	(INITVARS (INVERTWINDOWFN (QUOTE INVERTW)))
	(COMS (FNS \SPAWN.CHAT)
	      (DECLARE: DONTEVAL@LOAD DOCOPY (ADDVARS (BackgroundMenuCommands (CHAT (QUOTE (
\SPAWN.CHAT))
										    
						      "Runs a new CHAT process; prompts for host")))
			(P (SETQ BackgroundMenu))
			(FILES BSP])



(* CHAT typein)

(DEFINEQ

(CHAT
  [LAMBDA (HOST LOGOPTION INITSTREAM WINDOW FROMMENU)        (* bvm: " 5-Oct-84 14:51")
    [COND
      ((NOT (THIS.PROCESS))
	(PRIN1 "Turning on Process mechanism and trying again...
" T)
	(COND
	  ((READP T)
	    (PRINTBELLS)
	    (DISMISS 1000)))
	(CLEARBUF T)
	[BKSYSBUF (MKSTRING (CONS (QUOTE CHAT)
				  (AND (OR HOST LOGOPTION)
				       (CONS (KWOTE HOST)
					     (AND LOGOPTION (CONS (KWOTE LOGOPTION]
	(RETEVAL (QUOTE CHAT)
		 (QUOTE (PROCESSWORLD T]
    (PROG (CONNECTION STREAMS OPENFN RESULT PROCESS HOSTS)
          [OR HOST (COND
		[FROMMENU (COND
			    ((OR CHAT.HOSTMENU (PROGN (SETQ HOSTS CHAT.ALLHOSTS)
						      (COND
							(DEFAULTCHATHOST (pushnew HOSTS 
										  DEFAULTCHATHOST)))
						      HOSTS))
			      [SETQ HOST (MENU (OR CHAT.HOSTMENU (SETQ CHAT.HOSTMENU
						     (create MENU
							     ITEMS ←(APPEND HOSTS (QUOTE (Other)))
							     TITLE ← "Host"]
			      (COND
				((EQ HOST (QUOTE Other))
				  (SETQ HOST NIL))
				((NULL HOST)
				  (RETURN]
		(T (SETQ HOST DEFAULTCHATHOST]
      TOP [COND
	    ((NOT HOST)
	      (COND
		([NOT (SETQ HOST (MKATOM (PROMPTFORWORD "
Host: " NIL "Enter name of host to chat to, or <cr> to abort" (AND FROMMENU PROMPTWINDOW]
		  (GO FAIL]
          (COND
	    [(NOT (SETQ OPENFN (FIND.CHAT.PROTOCOL HOST)))   (* Don't know how to talk to this host)
	      (SETQ RESULT (CONCAT "Unknown Chat host: " HOST))
	      (COND
		(FROMMENU (printout PROMPTWINDOW T RESULT]
	    ((NOT (SETQ STREAMS (APPLY* (PROGN (SETQ HOST (CAR OPENFN))
                                                             (* Value returned was (CanonicalHostName OpenFn))
					       (CADR OPENFN))
					HOST)))
	      (SETQ RESULT "Failed"))
	    (T (SETQ WINDOW (GETCHATWINDOW HOST WINDOW))
	       (CHAT.INIT STREAMS WINDOW HOST)
	       (COND
		 ((NOT (FMEMB HOST CHAT.ALLHOSTS))
		   (SETQ CHAT.ALLHOSTS (CONS HOST CHAT.ALLHOSTS))
		   (SETQ CHAT.HOSTMENU)))
	       [COND
		 (FROMMENU (PROCESSPROP (THIS.PROCESS)
					(QUOTE NAME)
					(PACK* "CHAT#" HOST))
			   (RETURN (CHAT.TYPEIN HOST WINDOW LOGOPTION INITSTREAM)))
		 (T (ADD.PROCESS (LIST (QUOTE CHAT.TYPEIN)
				       (KWOTE HOST)
				       (KWOTE WINDOW)
				       (KWOTE LOGOPTION)
				       (KWOTE INITSTREAM))
				 (QUOTE NAME)
				 (PACK* "CHAT#" HOST)
				 (QUOTE RESTARTABLE)
				 (QUOTE NO]
	       (RETURN HOST)))
      FAIL[COND
	    ((AND WINDOW (WINDOWPROP WINDOW (QUOTE CHATHOST)))
	      (WINDOWPROP WINDOW (QUOTE BUTTONEVENTFN)
			  (FUNCTION CHAT.RECONNECT]
          (RETURN RESULT])

(CHAT.INIT
  [LAMBDA (STREAMS WINDOW HOST)                              (* rda: "24-Aug-84 22:54")
    (PROG ((INSTREAM (CAR STREAMS)))
          (WINDOWPROP WINDOW (QUOTE CHATSTATE)
		      (create CHATUSERSTATE
			      RUNNING? ← T
			      CHATINEMACS ← CHAT.IN.EMACS?
			      INSTREAM ← INSTREAM
			      OUTSTREAM ←(CDR STREAMS)))
          (WINDOWPROP WINDOW (QUOTE RESHAPEFN)
		      (FUNCTION CHAT.RESHAPEWINDOW))
          (WINDOWPROP WINDOW (QUOTE BUTTONEVENTFN)
		      (FUNCTION CHAT.BUTTONFN))
          (WINDOWADDPROP WINDOW (QUOTE CLOSEFN)
			 (FUNCTION CHAT.CLOSEFN))
          (STREAMPROP INSTREAM (QUOTE OLDEOSOP)
		      (fetch ENDOFSTREAMOP of INSTREAM))
          (replace ENDOFSTREAMOP of INSTREAM with (FUNCTION CHAT.ENDOFSTREAMOP])

(FIND.CHAT.PROTOCOL
  [LAMBDA (NAME)                                             (* bvm: " 5-Oct-84 14:50")

          (* * Find a protocol for use by CHAT by calling the filter fns on CHAT.PROTOCOLS. The fns should return a 
	  CHAT.PROTOCOL that can be used to contact NAME or NIL.)


    (for FN in CHAT.PROTOCOLS bind RESULT when (SETQ RESULT (APPLY* FN NAME)) do (RETURN RESULT])

(CHAT.TYPEIN
  [LAMBDA (HOST WINDOW LOGOPTION INITSTREAM)                 (* bvm: " 5-Oct-84 14:41")
    (DECLARE (SPECVARS STREAM))                              (* so that menu can change it)
    (PROG ((THISPROC (THIS.PROCESS))
	   (DEFAULTSTREAM T)
	   (STATE (WINDOWPROP WINDOW (QUOTE CHATSTATE)))
	   CHATSTREAM WINDOWSTREAM STREAM CH DPYCODE DPYNAME X)
          (SETQ CHATSTREAM (fetch (CHATUSERSTATE OUTSTREAM) of STATE))
          (PROCESSPROP THISPROC (QUOTE TTYEXITFN)
		       (FUNCTION CHAT.TTYEXITFN))
          (PROCESSPROP THISPROC (QUOTE TTYENTRYFN)
		       (FUNCTION CHAT.TTYENTRYFN))
          (COND
	    ((TTY.PROCESSP)

          (* Already have tty (probably from menu), so explicitly turn off interrupts, since our TTYENTRYFN hadn't been set 
	  yet (so that ↑E could interrupt GETCHATWINDOW))


	      (CHAT.TTYENTRYFN THISPROC))
	    (T                                               (* want to do this early so users can start typing 
							     ahead)
	       (TTY.PROCESS THISPROC)))
          (PROCESSPROP THISPROC (QUOTE WINDOW)
		       WINDOW)
          (SETQ WINDOWSTREAM (WINDOWPROP WINDOW (QUOTE DSP)))
          (DSPFONT (OR CHAT.FONT (DEFAULTFONT (QUOTE DISPLAY)))
		   WINDOWSTREAM)
          (DSPRESET WINDOWSTREAM)
          (WINDOWPROP WINDOW (QUOTE PROCESS)
		      (THIS.PROCESS))
          (WINDOWPROP WINDOW (QUOTE CHATHOST)
		      (CONS HOST LOGOPTION))
          (RESETSAVE NIL (LIST [FUNCTION (LAMBDA (WINDOW STATE)
				   (AND RESETSTATE (fetch RUNNING? of STATE)
					(CHAT.CLOSE WINDOW T]
			       WINDOW STATE))                (* If an error occurs, process is killed, or HARDRESET 
							     happens, this will flush the connection etc)
          (COND
	    ((FIXP (SETQ DPYCODE CHAT.DISPLAYTYPE))          (* Old way, one numeric code for display)
	      )
	    ((AND (LISTP DPYCODE)
		  (OR (SETQ X (ASSOC HOST DPYCODE))
		      (ASSOC NIL DPYCODE)))
	      (SETQ DPYNAME (CADDR X))
	      (SETQ DPYCODE (CADR X)))
	    (T (SETQ DPYCODE NIL)))
          [replace TYPEOUTPROC of STATE with (ADD.PROCESS (LIST (QUOTE CHAT.TYPEOUT)
								WINDOW
								(KWOTE DPYNAME]
          (CHAT.SCREENPARAMS (fetch (CHATUSERSTATE INSTREAM) of STATE)
			     WINDOW)
          (COND
	    (DPYCODE (CHAT.SETDISPLAYTYPE (fetch (CHATUSERSTATE INSTREAM) of STATE)
					  DPYCODE)))
          (AND (NEQ LOGOPTION (QUOTE NONE))
	       (CHAT.LOGIN HOST LOGOPTION WINDOW STATE))
          (COND
	    (INITSTREAM (XNLSETQ (SETQ STREAM (\GETSTREAM (OR (STRINGP INITSTREAM)
							      (OPENFILE INITSTREAM (QUOTE INPUT)))
							  (QUOTE INPUT)))
				 NOBREAK)))
          (TTYDISPLAYSTREAM WINDOWSTREAM)                    (* So that \TTYBACKGROUND flashes the caret where we 
							     expect)
          (while (EQ (fetch RUNNING? of STATE)
		     T)
	     do (COND
		  ((NULL STREAM)
		    (SETQ STREAM DEFAULTSTREAM)))
		[COND
		  [(EQ STREAM T)                             (* Handle terminal differently.
							     Mainly because we may be inside a blocked process's 
							     \fillbuffer, making READP think there is input.
							     Ugh!!!)
		    (OR (TTY.PROCESSP)
			(\WAIT.FOR.TTY))
		    (COND
		      ((\SYSBUFP)
			(do (SETQ CH (\GETKEY))
			    (BOUT CHATSTREAM (COND
				    ((EQ CH CHAT.CONTROLCHAR)
                                                             (* Controlify it)
				      (LOGAND (CHAT.BIN CHATSTREAM STATE)
					      31))
				    ((EQ CH CHAT.METACHAR)   (* Prefix meta, turn on 200q bit)
				      (LOGOR (CHAT.BIN CHATSTREAM STATE)
					     128))
				    (T CH)))
			   repeatwhile (\SYSBUFP))
			(FORCEOUTPUT CHATSTREAM]
		  (T (until (EOFP STREAM) do (BOUT CHATSTREAM (\BIN STREAM)))
		     (FORCEOUTPUT CHATSTREAM)
		     (CLOSEF STREAM)
		     (SETQ STREAM)
		     (COND
		       ((SETQ X (GETPROMPTWINDOW WINDOW NIL NIL T))
                                                             (* Indicate completion of Input if came from menu 
							     command)
			 (CLEARW X]
		(\TTYBACKGROUND))

          (* * Get here if we close connection.)


          [SELECTQ (fetch RUNNING? of STATE)
		   (CLOSE (CHAT.CLOSE WINDOW))
		   (ABORT (CHAT.CLOSE WINDOW T))
		   (NIL                                      (* Already dead.))
		   (SHOULDNT (CONCAT "Unknown state in CHAT: " (fetch RUNNING? of STATE]
          (BLOCK])

(CHAT.BIN
  [LAMBDA (OUTSTREAM STATE)                                  (* rda: "20-Aug-84 23:09")
    (until (\SYSBUFP) bind (FIRSTTIME ← T)
       do (COND
	    (FIRSTTIME (FORCEOUTPUT OUTSTREAM)
		       (SETQ FIRSTTIME NIL)))
	  (\TTYBACKGROUND))
    (\GETKEY])

(CHAT.CLOSE
  [LAMBDA (WINDOW ABORTED CLOSING)                           (* rda: "27-Aug-84 01:12")
                                                             (* Close chat connection that is using WINDOW.
							     Also serves as the CLOSEFN of this window, when CLOSING 
							     is NIL)
    (PROG ((CHATSTATE (WINDOWPROP WINDOW (QUOTE CHATSTATE)))
	   PROC FILE KEEP)
          (DETACHALLWINDOWS WINDOW)
          (COND
	    [CHATSTATE (DEL.PROCESS (fetch TYPEOUTPROC of CHATSTATE))
		       (COND
			 ((SETQ FILE (fetch TYPESCRIPTOFD of CHATSTATE))
			   (TERPRI WINDOW)
			   (PRIN1 "Closing " WINDOW)
			   (PRINT (CLOSEF FILE)
				  WINDOW)))
		       (\CHECKCARET WINDOW)
		       (replace RUNNING? of (WINDOWPROP WINDOW (QUOTE CHATSTATE)
							NIL)
			  with NIL)
		       (OR ABORTED (CHAT.CLOSE.CONNECTION (fetch (CHATUSERSTATE INSTREAM)
							     of CHATSTATE)
							  (fetch (CHATUSERSTATE OUTSTREAM)
							     of CHATSTATE]
	    (T (RETURN)))
          (SETQ CHATWINDOWLST (DREMOVE WINDOW CHATWINDOWLST))
          (SETQ PROC (WINDOWPROP WINDOW (QUOTE PROCESS)
				 NIL))

          (* Save the process running, if any; don't do anything with it until after we close the window, if we're going to,
	  so that windows don't flip around excessively)


          (WINDOWDELPROP WINDOW (QUOTE CLOSEFN)
			 (FUNCTION CHAT.CLOSEFN))
          (PROG [(TITLE (WINDOWPROP WINDOW (QUOTE TITLE]
	        (WINDOWPROP WINDOW (QUOTE TITLE)
			    (CONCAT (SUBSTRING TITLE 1 (IPLUS (OR (STRPOS ", height" TITLE)
								  0)
							      -1))
				    ", closed")))            (* Change title to indicate closure)
          (WINDOWPROP WINDOW (QUOTE BUTTONEVENTFN)
		      (FUNCTION CHAT.RECONNECT))
          (if (AND (NOT (SETQ KEEP (WINDOWPROP WINDOW (QUOTE KEEPCHAT)
					       NIL)))
		   (NOT CLOSING)
		   (OR CLOSECHATWINDOWFLG (NEQ WINDOW CHATWINDOW)))
	      then (CLOSEW WINDOW))
          [COND
	    ((EQ KEEP (QUOTE NEW))                           (* Invoked via the New command -- start up a new 
							     connection in this window)
	      (ADD.PROCESS (LIST (QUOTE CHAT)
				 NIL NIL NIL WINDOW T]
          (COND
	    (PROC                                            (* Do this last, because if we are PROC, DEL.PROCESS 
							     won't return)
		  (DEL.PROCESS PROC])

(CHAT.CLOSEFN
  [LAMBDA (WINDOW)                                           (* rda: "21-Aug-84 13:23")

          (* * Close this chat connection making sure that the window gets closed. Used as CLOSEFN of the chat window.)


    (CHAT.CLOSE WINDOW NIL T])

(CHAT.CLOSE.CONNECTION
  [LAMBDA (INSTREAM OUTSTREAM)                               (* rda: "23-Aug-84 15:25")

          (* * Close the streams for a connection if they are open.)


    (if (OPENP INSTREAM)
	then (CLOSEF INSTREAM))
    (if (OPENP OUTSTREAM)
	then (CLOSEF OUTSTREAM])

(CHAT.LOGIN
  [LAMBDA (HOST OPTION WINDOW CHATSTATE)                     (* rda: "27-Aug-84 01:12")

          (* * Login to HOST. If a job already exists on HOST, Attach to it unless OPTION overrides.)


    (PROG ((LOGINFO (CDR (ASSOC (OR (GETOSTYPE HOST)
				    (QUOTE IFS))
				NETWORKLOGINFO)))
	   (STATE (WINDOWPROP WINDOW (QUOTE CHATSTATE)))
	   NAME/PASS COM INSTREAM OUTSTREAM)
          (SETQ INSTREAM (fetch (CHATUSERSTATE INSTREAM) of STATE))
          (OR LOGINFO (RETURN))
          (SETQ NAME/PASS (\INTERNAL/GETPASSWORD HOST))
          [SETQ COM (COND
	      (OPTION)
	      ((ASSOC (QUOTE ATTACH)
		      LOGINFO)
		(OR (CHAT.LOGINFO INSTREAM HOST (CAR NAME/PASS))
		    (QUOTE LOGIN)))
	      (T                                             (* Don't know how to do anything but login, so silly to 
							     try anything else)
		 (QUOTE LOGIN]
          (COND
	    ((NULL (SETQ LOGINFO (ASSOC COM LOGINFO)))
	      (printout PROMPTWINDOW T "Login option " COM " not implemented for this type of host"))
	    (T (SETQ OUTSTREAM (fetch (CHATUSERSTATE OUTSTREAM) of STATE))
	       (for X in (CDR LOGINFO) do (SELECTQ X
						   (CR (BOUT OUTSTREAM (CHARCODE CR))
						       (FORCEOUTPUT OUTSTREAM))
						   (USERNAME (PRIN3 (CAR NAME/PASS)
								    OUTSTREAM))
						   (PASSWORD (PRIN3 (\DECRYPT.PWD (CDR NAME/PASS))
								    OUTSTREAM))
						   (WAIT     (* Some systems do not permit typeahead)
							 (if (NOT (CHAT.FLUSH&WAIT INSTREAM))
							     then 
                                                             (* Couldn't sync, so wait longer.)
								  (DISMISS CHAT.WAIT.TIME))
							 (DISMISS CHAT.WAIT.TIME))
						   (PRIN3 X OUTSTREAM)))
	       (FORCEOUTPUT OUTSTREAM])
)



(* Chat streams)

(DEFINEQ

(ADD.CHAT.MESSAGE
  [LAMBDA (STREAM MSG)                                       (* rda: "22-Aug-84 18:07")
    (STREAMPROP STREAM (QUOTE MESSAGE)
		(CONCAT (OR (STREAMPROP STREAM (QUOTE MESSAGE))
			    "")
			MSG])

(CHAT.LOGINFO
  [LAMBDA (INSTREAM HOST NAME)                               (* rda: "22-Aug-84 17:04")

          (* * Invoke the LOGINFO method for INSTREAM, if any.)


    (PROG [(FN (STREAMPROP INSTREAM (QUOTE LOGINFO]
          (RETURN (if (FNTYP FN)
		      then (APPLY* FN HOST NAME])

(CHAT.SENDSCREENPARAMS
  [LAMBDA (INSTREAM HEIGHT WIDTH)                            (* rda: "22-Aug-84 16:56")

          (* * Invoke the SENDSCREENPARAMS method for INSTREAM, if any.)


    (PROG [(FN (STREAMPROP INSTREAM (QUOTE SENDSCREENPARAMS]
          (RETURN (if (FNTYP FN)
		      then (APPLY* FN INSTREAM HEIGHT WIDTH])

(CHAT.SETDISPLAYTYPE
  [LAMBDA (INSTREAM CODE)                                    (* rda: "24-Aug-84 11:09")

          (* * Invoke the SETDISPLAYTYPE method for INSTREAM.)


    (OR (NUMBERP CODE)
	(ERROR "Non-numeric arg: " CODE))
    (PROG [(FN (STREAMPROP INSTREAM (QUOTE SETDISPLAYTYPE]
          (RETURN (if (FNTYP FN)
		      then (APPLY* FN INSTREAM CODE])

(CHAT.LOGINFO
  [LAMBDA (INSTREAM HOST NAME)                               (* rda: "22-Aug-84 17:04")

          (* * Invoke the LOGINFO method for INSTREAM, if any.)


    (PROG [(FN (STREAMPROP INSTREAM (QUOTE LOGINFO]
          (RETURN (if (FNTYP FN)
		      then (APPLY* FN HOST NAME])

(CHAT.FLUSH&WAIT
  [LAMBDA (INSTREAM)                                         (* rda: "21-Aug-84 13:48")

          (* * Invoke the FLUSH&WAIT method for INSTREAM)


    (PROG [(FN (STREAMPROP INSTREAM (QUOTE FLUSH&WAIT]
          (RETURN (if (FNTYP FN)
		      then (APPLY* FN INSTREAM])

(CHAT.ENDOFSTREAMOP
  [LAMBDA (STREAM)                                           (* rda: "24-Aug-84 22:52")

          (* * Return -1 to indicate EOS to CHAT, and restore the streams EOS op incase it's needed for other things.)


    (replace ENDOFSTREAMOP of STREAM with (OR (STREAMPROP STREAM (QUOTE EOSOP))
					      (FUNCTION \EOSERROR)))
    -1])

(CHAT.OPTIONMENU
  [LAMBDA (INSTREAM)                                         (* rda: "31-Aug-84 16:33")

          (* * Apply the menu-building method for INSTREAM, if any.)


    (PROG [(FN (STREAMPROP INSTREAM (QUOTE OPTIONMENU]
          (RETURN (if (FNTYP FN)
		      then (APPLY* FN INSTREAM])
)



(* CHAT typeout and DM simulation)

(DEFINEQ

(CHAT.TYPEOUT
  [LAMBDA (WINDOW DPYTYPE)                                   (* rda: "27-Aug-84 01:07")
    (DECLARE (SPECVARS WINDOW DSP OUTSTREAM INSTREAM DINGED EATLF EATCRLF EATTOCRLF AUTOLF TTYWIDTH 
		       TTYHEIGHT XPOS YPOS ADDRESSING IDMODE ROLLMODE BLINKMODE FONTWIDTH FONTHEIGHT 
		       FONTDESCENT FONT PLAINFONT CHATBOLDFONT HOMEPOS TYPESCRIPTSTREAM))
    (bind (XPOS ← 0)
	  (YPOS ← 0)
	  (CNT ← 1)
	  (HANDLECHARFN ←(OR (CDR (ASSOC (OR DPYTYPE CHAT.EMULATORTYPE)
					 CHAT.DRIVERTYPES))
			     (FUNCTION DMCHAT.HANDLECHARACTER)))
	  (STATE ←(WINDOWPROP WINDOW (QUOTE CHATSTATE)))
	  MSG HOMEPOS INSTREAM OUTSTREAM DSP TTYWIDTH TTYHEIGHT DINGED CH ADDRESSING IDMODE 
	  TYPESCRIPTSTREAM BLINKMODE EATLF EATCRLF EATTOCRLF AUTOLF FONT CHATBOLDFONT PLAINFONT 
	  FONTWIDTH FONTHEIGHT FONTDESCENT CRPENDING (ROLLMODE ← T)
       first (SETQ DSP (WINDOWPROP WINDOW (QUOTE DSP)))
	     (SETQ INSTREAM (fetch (CHATUSERSTATE INSTREAM) of STATE))
	     (SETQ OUTSTREAM (\GETSTREAM WINDOW (QUOTE OUTPUT)))
	     (CHAT.RESET.DISPLAY.PARMS)
	     (CHAT.HOME)
       while (IGEQ (SETQ CH (\BIN INSTREAM))
		   0)
       do (while (fetch HELD of STATE) do (BLOCK))
	  (\CHECKCARET OUTSTREAM)
	  (if (SETQ MSG (STREAMPROP INSTREAM (QUOTE MESSAGE)))
	      then (PRIN1 MSG OUTSTREAM)
		   (STREAMPROP INSTREAM (QUOTE MESSAGE)
			       NIL))                         (* Print any protocol related msgs that might have come 
							     along while we where asleep)
	  (SPREADAPPLY* HANDLECHARFN CH)
	  (if TYPESCRIPTSTREAM
	      then (if (SELCHARQ CH
				 (CR (PROG1 CRPENDING (SETQ CRPENDING T)))
				 (LF (if CRPENDING
					 then (\OUTCHAR TYPESCRIPTSTREAM (CHARCODE EOL)) 
                                                             (* Have the typescript put turn crlf into whatever it 
							     likes for eol)
					      (SETQ CRPENDING NIL)
				       else T))
				 (PROGN (if CRPENDING
					    then (\BOUT TYPESCRIPTSTREAM (CHARCODE CR))
						 (SETQ CRPENDING NIL))
					T))
		       then (\BOUT TYPESCRIPTSTREAM CH)))
	  [COND
	    (CHATDEBUGFLG (COND
			    ((OR (EQ CHATDEBUGFLG T)
				 (IGREATERP (add CNT 1)
					    CHATDEBUGFLG))
			      (BLOCK)
			      (SETQ CNT 1]
       finally (SELECTQ CH
			(-1 (printout OUTSTREAM T "[Connection closed by remote host]" T)
			    (replace RUNNING? of STATE with (QUOTE CLOSE)))
			(-2 (printout OUTSTREAM T "[Connection aborted by remote host]" T)
			    (replace RUNNING? of STATE with (QUOTE ABORT)))
			(PROGN (printout OUTSTREAM T 
					 "[Connection closed by remote host in unknown way]"
					 T)
			       (replace RUNNING? of STATE with (QUOTE CLOSE])

(CHAT.RESET.DISPLAY.PARMS
  [LAMBDA NIL
    (DECLARE (USEDFREE (DSP WINDOW COM FONTHEIGHT FONTWIDTH PLAINFONT FONT FONTDESCENT TTYWIDTH 
			    TTYHEIGHT HOMEPOS)))             (* bvm: " 2-Jun-84 15:31")
                                                             (* Reset state)
    (SETQ FONTHEIGHT (IMINUS (DSPLINEFEED NIL DSP)))
    [SETQ FONTWIDTH (CHARWIDTH (CHARCODE A)
			       (SETQ PLAINFONT (SETQ FONT (DSPFONT NIL DSP]
    (SETQ FONTDESCENT (FONTPROP FONT (QUOTE DESCENT)))
    (SETQ TTYWIDTH (WINDOWPROP WINDOW (QUOTE WIDTH)))
    (SETQ TTYWIDTH (ITIMES (IQUOTIENT TTYWIDTH FONTWIDTH)
			   FONTWIDTH))                       (* Make TTYWIDTH multiple of FONTWIDTH)
    (SETQ TTYHEIGHT (WINDOWPROP WINDOW (QUOTE HEIGHT)))
    (SETQ HOMEPOS (IPLUS (ITIMES (SUB1 (IQUOTIENT TTYHEIGHT FONTHEIGHT))
				 FONTHEIGHT)
			 FONTDESCENT])

(CHAT.DID.RESHAPE
  [LAMBDA NIL
    (DECLARE (USEDFREE INSTREAM DSP))                        (* rda: "22-Aug-84 16:40")
                                                             (* Invoked in the type-out process when window is 
							     reshaped)
    (CHAT.SCREENPARAMS INSTREAM DSP)
    (CHAT.RESET.DISPLAY.PARMS])

(DMCHAT.HANDLECHARACTER
  [LAMBDA (CHAR)                                             (* bvm: " 2-Jun-84 15:07")
    (DECLARE (USEDFREE WINDOW DSP OUTSTREAM DINGED EATLF EATCRLF EATTOCRLF AUTOLF TTYWIDTH TTYHEIGHT 
		       XPOS YPOS ADDRESSING IDMODE ROLLMODE BLINKMODE FONT PLAINFONT CHATBOLDFONT 
		       FONTWIDTH FONTHEIGHT FONTDESCENT HOMEPOS))
                                                             (* Here and/or below)
    (PROG NIL
          [COND
	    ((EQ CHAR (CHARCODE BELL))
	      (RETURN (COND
			((NOT DINGED)
			  (APPLY* INVERTWINDOWFN WINDOW)     (* Complement window)
			  (SETQ DINGED T]
          (COND
	    (DINGED (APPLY* INVERTWINDOWFN WINDOW)
		    (SETQ DINGED NIL)))
          (COND
	    ((AND AUTOLF (OR (NEQ CHAR (CHARCODE CR))
			     (NOT EATTOCRLF)))

          (* We last received a CR, so DM wants auto LF after it. However, we postpone doing so until the next char is 
	  received, so that we get scroll holding right)


	      (CHAT.DOWN)
	      (SETQ AUTOLF NIL)))
          [COND
	    (ADDRESSING (COND
			  ((DMCHAT.ADDRESS CHAR)
			    (RETURN]
          [COND
	    ((AND (IGEQ CHAR (CHARCODE SPACE))
		  (ILESSP CHAR (CHARCODE DEL)))              (* Normal char)
	      (SETQ EATLF (SETQ EATCRLF NIL))
	      (RETURN (COND
			((NOT EATTOCRLF)                     (* Print the char)
			  (COND
			    (IDMODE                          (* this is discouraged by the DM manual, but apparently 
							     EMACS does it, so might as well support it)
				    (CHAT.ADDCHAR)))
			  (DMCHAT.PRINTCHAR CHAR]
          [COND
	    (EATLF (SETQ EATLF NIL)                          (* LF is ignored after CR)
		   (COND
		     ((EQ CHAR (CHARCODE LF))
		       (RETURN]
          [COND
	    (EATCRLF                                         (* We just wrapped around, so ignore CR and/or LF if 
							     next)
		     (COND
		       ((EQ CHAR (CHARCODE CR))
			 (SETQ EATLF T)
			 (RETURN (SETQ EATCRLF NIL)))
		       (T                                    (* Intervening control characters do not stop the 
							     eating, except for a few inconsistent exceptions...)
			  (SELCHARQ CHAR
				    ((↑B ↑\ ↑↑ ↑←)
				      (SETQ EATCRLF NIL))
				    NIL]
          (SELCHARQ CHAR
		    [LF (COND
			  (IDMODE (CHAT.ADDLINE))
			  (T (CHAT.DOWN]
		    (CR (SETQ EATTOCRLF NIL)
			(DMCHAT.NEWLINE T))
		    [BS (COND
			  (IDMODE (CHAT.DELCHAR))
			  (T (CHAT.LEFT]
		    (↑W                                      (* Erase to end of line)
			(CHAT.ERASE.TO.EOL))
		    (↑L                                      (* Start of cursor address)
			(SETQ ADDRESSING -1))
		    (↑B                                      (* Homes cursor, cancels some modes)
			(CHAT.HOME)
			(CHAT.CLEARMODES))
		    (↑X                                      (* Cancel --resets modes)
			(CHAT.CLEARMODES)
			(SETQ ROLLMODE))
		    ((↑↑ ↑←)                                 (* Master Reset -- Clears screen, modes)
		      (CHAT.CLEAR))
		    [↑\                                      (* Forward space)
			(COND
			  ((NOT EATTOCRLF)
			    (COND
			      (IDMODE (CHAT.ADDCHAR))
			      (T (DMCHAT.RIGHT]
		    [↑Z                                      (* Up)
			(COND
			  (IDMODE (CHAT.DELETELINE))
			  (T (CHAT.UP]
		    [(↑N ↑O)                                 (* Enter blink mode, enter protected mode.
							     Do both as embolden)
		      (COND
			((NOT BLINKMODE)
			  (SETQ BLINKMODE T)
			  (DSPFONT [SETQ FONT (OR CHATBOLDFONT (SETQ CHATBOLDFONT
						    (FONTCOPY PLAINFONT (QUOTE WEIGHT)
							      (QUOTE BOLD]
				   DSP]
		    (↑P                                      (* Enter i/d mode)
			(SETQ IDMODE T))
		    (↑%]                                     (* Set roll mode)
			 (SETQ ROLLMODE T))
		    NIL])

(CHAT.SCREENPARAMS
  [LAMBDA (INSTREAM WINDOW)                                  (* rda: "22-Aug-84 16:42")

          (* * Sends screen width, height to partner and updates title. If INSTREAM is NIL then only update title.)


    (PROG ((HEIGHT (IMIN [IQUOTIENT (WINDOWPROP WINDOW (QUOTE HEIGHT))
				    (IABS (DSPLINEFEED NIL (WINDOWPROP WINDOW (QUOTE DSP]
			 127))
	   (WIDTH (IMIN (LINELENGTH NIL WINDOW)
			127))
	   (TITLE (WINDOWPROP WINDOW (QUOTE TITLE)))
	   (STATE (WINDOWPROP WINDOW (QUOTE CHATSTATE)))
	   EMACSMODE TITLEMIDDLE)
          (COND
	    (INSTREAM (CHAT.SENDSCREENPARAMS INSTREAM HEIGHT WIDTH)))
          (WINDOWPROP WINDOW (QUOTE TITLE)
		      (CONCAT (SUBSTRING TITLE 1 (SUB1 (OR (SETQ TITLEMIDDLE (STRPOS ", height" TITLE)
							     )
							   0)))
			      ", height = " HEIGHT ", width = " WIDTH
			      (COND
				[[OR (SETQ EMACSMODE (fetch (CHATUSERSTATE CHATINEMACS) of STATE))
				     (AND TITLEMIDDLE (NOT (FIXP (NTHCHAR TITLE -1]
				  (CONCAT ", Emacs " (COND
					    (EMACSMODE "ON")
					    (T "OFF"]
				(T ""])

(CHAT.ADDCHAR
  [LAMBDA NIL                                                (* bvm: "28-APR-82 21:46")
                                                             (* Insert a space at cursor position, pushing rest of 
							     line to right)
    (PROG ((Y (IDIFFERENCE YPOS FONTDESCENT)))
          (BITBLT DSP XPOS Y DSP (IPLUS XPOS FONTWIDTH)
		  Y
		  (IPLUS (IDIFFERENCE TTYWIDTH XPOS)
			 FONTWIDTH)
		  FONTHEIGHT
		  (QUOTE INPUT)
		  (QUOTE REPLACE))                           (* Blt remainder of line to the right by FONTWIDTH, then
							     erase the character under the cursor)
          (CHAT.ERASEBITS XPOS Y FONTWIDTH FONTHEIGHT])

(CHAT.ADDLINE
  [LAMBDA (ATYPOS)                                           (* bvm: "28-APR-82 18:19")
    (OR ATYPOS (SETQ ATYPOS YPOS))                           (* To insert line at ATYPOS, we blt everything below it 
							     down one, then clear the line at ATYPOS)
    (BITBLT DSP 0 FONTHEIGHT DSP 0 0 TTYWIDTH (IDIFFERENCE ATYPOS FONTDESCENT)
	    (QUOTE INPUT)
	    (QUOTE REPLACE))
    (CHAT.ERASEBITS 0 (IDIFFERENCE ATYPOS FONTDESCENT)
		    TTYWIDTH FONTHEIGHT])

(DMCHAT.ADDRESS
  [LAMBDA (CHAR)                                             (* bvm: "28-APR-82 21:22")
                                                             (* In the middle of doing absolute address.
							     Return T unless a cancel is received)
    (COND
      ((SELCHARQ CHAR
		 ((↑X ↑↑ ↑←)
		   T)
		 NIL)                                        (* Cancel it)
	(SETQ ADDRESSING NIL))
      ((EQ CHAR (CHARCODE ↑L))                               (* Restarting the address in the middle of the address 
							     is legal)
	(SETQ ADDRESSING -1))
      ((ILESSP ADDRESSING 0)                                 (* Accept first position)
	(SETQ ADDRESSING (LOGXOR CHAR 96)))
      (T                                                     (* Accept second position and go there)
	 (MOVETO (SETQ XPOS (IMIN (ITIMES ADDRESSING FONTWIDTH)
				  (IDIFFERENCE TTYWIDTH FONTWIDTH)))
		 [SETQ YPOS (IMAX FONTDESCENT (IDIFFERENCE HOMEPOS (ITIMES (LOGXOR CHAR 96)
									   FONTHEIGHT]
		 DSP)
	 (SETQ ADDRESSING)
	 T])

(CHAT.CLEAR
  [LAMBDA (SETROLL)                                          (* bvm: "17-SEP-82 12:53")
    (CLEARW WINDOW)
    (CHAT.CLEARMODES)
    (AND SETROLL (SETQ ROLLMODE T))
    (CHAT.HOME])

(CHAT.CLEARMODES
  [LAMBDA NIL                                                (* bvm: "17-SEP-82 13:03")
    (COND
      (BLINKMODE                                             (* Restore normal font)
		 (DSPFONT PLAINFONT DSP)
		 (SETQ FONT PLAINFONT)
		 (SETQ BLINKMODE)))
    (SETQ IDMODE (SETQ ADDRESSING])

(CHAT.DELCHAR
  [LAMBDA NIL                                                (* bvm: "28-APR-82 21:48")
                                                             (* Delete character under cursor, moving rest of line to
							     left)
    (PROG ((Y (IDIFFERENCE YPOS FONTDESCENT)))
          (BITBLT DSP (IPLUS XPOS FONTWIDTH)
		  Y DSP XPOS Y (IPLUS (IDIFFERENCE TTYWIDTH XPOS)
				      FONTWIDTH)
		  FONTHEIGHT
		  (QUOTE INPUT)
		  (QUOTE REPLACE))                           (* Blt remainder of line to the left by FONTWIDTH, then 
							     erase the rightmost character position)
          (CHAT.ERASEBITS (IDIFFERENCE TTYWIDTH FONTWIDTH)
			  Y FONTWIDTH FONTHEIGHT])

(CHAT.DELETELINE
  [LAMBDA (ATYPOS)                                           (* bvm: "28-APR-82 18:16")
    (OR ATYPOS (SETQ ATYPOS YPOS))                           (* To delete line at ATYPOS, we blt everything below it 
							     up one, then clear the bottom line)
    (BITBLT DSP 0 0 DSP 0 FONTHEIGHT TTYWIDTH (IDIFFERENCE ATYPOS FONTDESCENT)
	    (QUOTE INPUT)
	    (QUOTE REPLACE))
    (CHAT.ERASEBITS 0 0 TTYWIDTH FONTHEIGHT])

(CHAT.DOWN
  [LAMBDA NIL                                                (* bvm: "28-APR-82 17:05")
                                                             (* Move down a line)
    (COND
      ((IGREATERP YPOS FONTDESCENT)
	(MOVETO XPOS (SETQ YPOS (IDIFFERENCE YPOS FONTHEIGHT))
		DSP))
      ((NULL ROLLMODE)                                       (* Wraparound to top)
	(MOVETO XPOS (SETQ YPOS HOMEPOS)
		DSP))
      (T                                                     (* On bottom line in rollmode, scroll screen up one)
	 (CHAT.DELETELINE HOMEPOS])

(CHAT.ERASE.TO.EOL
  [LAMBDA NIL                                                (* bvm: "28-APR-82 21:16")
    (CHAT.ERASEBITS XPOS (IDIFFERENCE YPOS FONTDESCENT)
		    (IDIFFERENCE TTYWIDTH XPOS)
		    FONTHEIGHT])

(CHAT.ERASEBITS
  [LAMBDA (LEFT BOTTOM WIDTH HEIGHT)                         (* bvm: "28-APR-82 18:13")
    (BITBLT NIL NIL NIL DSP LEFT BOTTOM WIDTH HEIGHT (QUOTE TEXTURE)
	    (QUOTE REPLACE)
	    WHITESHADE])

(CHAT.HOME
  [LAMBDA NIL                                                (* bvm: "28-APR-82 16:42")
    (MOVETO (SETQ XPOS 0)
	    (SETQ YPOS HOMEPOS)
	    DSP])

(CHAT.LEFT
  [LAMBDA NIL                                                (* bvm: "28-APR-82 21:42")
    (COND
      ((IGREATERP XPOS 0)
	(MOVETO (SETQ XPOS (IDIFFERENCE XPOS FONTWIDTH))
		YPOS DSP])

(DMCHAT.NEWLINE
  [LAMBDA (EXPLICIT)                                         (* bvm: "28-APR-82 17:05")
                                                             (* Do a CRLF. EXPLICIT = T means a CR was received, NIL 
							     means we did autowraparound)
    (MOVETO (SETQ XPOS 0)
	    YPOS DSP)                                        (* Do only the CR part now, saving the LF for when next 
							     char arrives)
    (SETQ AUTOLF T)
    (COND
      (EXPLICIT (SETQ EATLF T))
      (T (SETQ EATCRLF T])

(DMCHAT.PRINTCHAR
  [LAMBDA (CHAR)                                             (* bvm: " 2-Jun-84 15:07")
    (\OUTCHAR OUTSTREAM CHAR)
    (COND
      ((IGEQ (add XPOS FONTWIDTH)
	     TTYWIDTH)                                       (* Have reached right margin, so wrap around)
	(COND
	  (CHAT.AUTOCRLF (DMCHAT.NEWLINE))
	  (T (SETQ EATTOCRLF T])

(DMCHAT.RIGHT
  [LAMBDA NIL                                                (* bvm: " 2-Jun-84 15:07")
    (COND
      ((ILESSP (IPLUS XPOS FONTWIDTH)
	       TTYWIDTH)
	(add XPOS FONTWIDTH)
	(MOVETO XPOS YPOS DSP))
      (T                                                     (* Auto crlf)
	 (DMCHAT.NEWLINE])

(CHAT.UP
  [LAMBDA NIL                                                (* bvm: "28-APR-82 16:59")
    (COND
      ((ILESSP YPOS HOMEPOS)
	(MOVETO XPOS (SETQ YPOS (IPLUS YPOS FONTHEIGHT))
		DSP])
)



(* window stuff)

(DEFINEQ

(GETCHATWINDOW
  [LAMBDA (HOST WINDOW)                                      (* rda: "21-Aug-84 13:22")
                                                             (* Return a window, possibly new, to run a chat 
							     connection to HOST. Uses WINDOW if possible)
    (PROG ((TITLE (CONCAT "Chat connection to " HOST))
	   DSP STATE)
          [COND
	    [[AND (WINDOWP (OR WINDOW (SETQ WINDOW CHATWINDOW)))
		  (OR [NOT (SETQ STATE (WINDOWPROP WINDOW (QUOTE CHATSTATE]
		      (COND
			((NOT (fetch RUNNING? of STATE))     (* Connection in CHATWINDOW is dead)
			  (CHAT.CLOSE WINDOW NIL T)
			  T]                                 (* Old window not in use. This shouldn't happen, but...)
	      (WINDOWPROP WINDOW (QUOTE TITLE)
			  TITLE)
	      (SETQ DSP (WINDOWPROP WINDOW (QUOTE DSP]
	    (T (SETQ DSP (WINDOWPROP (SETQ WINDOW (CREATEW NIL TITLE))
				     (QUOTE DSP)))
	       (DSPSCROLL T DSP)
	       (OR CHATWINDOW (SETQ CHATWINDOW WINDOW]
          (push CHATWINDOWLST WINDOW)
          (RETURN WINDOW])

(CHAT.BUTTONFN
  [LAMBDA (WINDOW)                                           (* bvm: "11-SEP-83 17:09")
    (COND
      [(LASTMOUSESTATE LEFT)
	(PROG (TMP)
	      (COND
		((AND (SETQ TMP (WINDOWPROP WINDOW (QUOTE CHATSTATE)))
		      (fetch CHATINEMACS of TMP)
		      (SETQ TMP (fetch TYPEOUTPROC of TMP)))
		  (PROCESS.APPLY TMP (FUNCTION CHAT.EMACS.MOVE)))
		(T (CHAT.HOLD WINDOW]
      ((LASTMOUSESTATE MIDDLE)
	(CHAT.MENU WINDOW])

(CHAT.HOLD
  [LAMBDA (WINDOW)                                           (* bvm: "23-SEP-81 12:14")

          (* * Toggle HOLD while button is down)


    (PROG [(STATE (WINDOWPROP WINDOW (QUOTE CHATSTATE]
          (TOTOPW WINDOW)
          (OR STATE (RETURN))
          [COND
	    ((NOT (fetch HELD of STATE))
	      (replace HELD of STATE with T)
	      (UNINTERRUPTABLY
                  (UNTILMOUSESTATE UP))]
          (replace HELD of STATE with NIL])

(CHAT.MENU
  [LAMBDA (WINDOW)                                           (* rda: "31-Aug-84 16:27")
    (DECLARE (GLOBALVARS CHATMENU CHAT.REOPENMENU)
	     (SPECVARS WINDOW STATE))                        (* Called by YELLOW)
    (PROG ((STATE (WINDOWPROP WINDOW (QUOTE CHATSTATE)))
	   COMMAND)
          [COND
	    ((NOT STATE)                                     (* No Connection here; try to reestablish)
	      (RETURN (COND
			((LASTMOUSESTATE MIDDLE)
			  (CHAT.RECONNECT WINDOW))
			(T (TOTOPW WINDOW]
          (replace HELD of STATE with T)
          (\CHECKCARET WINDOW)
          (SELECTQ [SETQ COMMAND (MENU (OR CHATMENU (SETQ CHATMENU (create MENU
									   ITEMS ← CHATMENUITEMS]
		   (Close (replace RUNNING? of STATE with (QUOTE CLOSE))
                                                             (* Ask CHAT.TYPEIN to shut things down.)
			  )
		   (New (replace RUNNING? of STATE with (QUOTE CLOSE))
			(WINDOWPROP WINDOW (QUOTE KEEPCHAT)
				    (QUOTE NEW)))
		   (Suspend (replace RUNNING? of STATE with (QUOTE CLOSE))
			    (WINDOWPROP WINDOW (QUOTE KEEPCHAT)
					T))
		   (Freeze                                   (* Leave in HELD state)
			   (RETURN))
		   (NIL)
		   (APPLY* COMMAND STATE WINDOW))
          (replace HELD of STATE with NIL])

(CHAT.CLEAR.FROM.MENU
  [LAMBDA (STATE)                                            (* rda: "10-Aug-84 17:18")
    (PROCESS.EVAL (fetch TYPEOUTPROC of STATE)
		  (QUOTE (CHAT.CLEAR T])

(CHAT.TAKE.INPUT
  [LAMBDA (STATE WINDOW)                                     (* bvm: " 1-Jun-84 17:43")
    (PROCESS.APPLY (WINDOWPROP WINDOW (QUOTE PROCESS))
		   (FUNCTION CHAT.TAKE.INPUT1)
		   (LIST WINDOW])

(CHAT.TAKE.INPUT1
  [LAMBDA (WINDOW)                                           (* bvm: " 3-Jun-84 15:24")
    (DECLARE (USEDFREE STREAM))                              (* In CHAT.TYPEIN)
    (PROG ((PWINDOW (GETPROMPTWINDOW WINDOW))
	   FILE)
          (CLEARW PWINDOW)
          (COND
	    ((AND STREAM (NEQ STREAM T))
	      (printout PWINDOW "Can't, still reading " (FULLNAME STREAM)))
	    (T (SETQ FILE (PROMPTFORWORD "Take input from file: " NIL NIL PWINDOW))
	       (COND
		 ((NULL FILE)
		   (CLEARW PWINDOW))
		 [[SETQ FILE (CAR (PROG1 (NLSETQ (OPENSTREAM (MKATOM FILE)
							     (QUOTE INPUT)))
					 (CLEARW PWINDOW]
		   (printout PWINDOW "Reading " (FULLNAME (SETQ STREAM FILE]
		 (T (printout PWINDOW (ERRORSTRING (CAR (ERRORN)))
			      " - "
			      (CADR (ERRORN])

(DO.CHAT.OPTION
  [LAMBDA (STATE WINDOW)                                     (* rda: "31-Aug-84 16:27")

          (* * Pop up a menu of protocol specific options.)


    (PROG [(MENU (CHAT.OPTIONMENU (fetch (CHATUSERSTATE INSTREAM) of STATE]
          (if MENU
	      then (MENU MENU)
	    else (printout PROMPTWINDOW "This protocol has no options."])

(CHAT.RECONNECT
  [LAMBDA (WINDOW)                                           (* bvm: "22-Apr-84 22:30")
    (PROG [(STATE (WINDOWPROP WINDOW (QUOTE CHATHOST]
          (COND
	    ((NULL STATE)
	      (WINDOWPROP WINDOW (QUOTE BUTTONEVENTFN)
			  (QUOTE TOTOPW))
	      (TOTOPW WINDOW))
	    ((NOT (LASTMOUSESTATE MIDDLE))
	      (TOTOPW WINDOW))
	    ([MENU (OR CHAT.REOPENMENU (SETQ CHAT.REOPENMENU (create MENU
								     ITEMS ←(QUOTE ((ReConnect T 
							  "Will reestablish this Chat connection"]
	      (WINDOWPROP WINDOW (QUOTE BUTTONEVENTFN)
			  (QUOTE TOTOPW))                    (* Don't let this command get issued twice)
	      (TTY.PROCESS (ADD.PROCESS (LIST (QUOTE CHAT)
					      (KWOTE (CAR STATE))
					      (KWOTE (CDR STATE))
					      NIL WINDOW T])

(CHAT.RESHAPEWINDOW
  [LAMBDA (WINDOW OLDIMAGE IMAGEREGION OLDSCREENREGION)      (* bvm: " 5-Oct-84 18:05")
                                                             (* RESHAPEFN for the chat window)
    (RESHAPEBYREPAINTFN WINDOW OLDIMAGE IMAGEREGION)

          (* Note: Don't pass OLDSCREENREGION to RESHAPEBYREPAINTFN or it may try to leave the image fixed and move the 
	  coordinate system. Our code assumes that the bottom of the window is zero. If someone gets ambitious, can figure out
	  how to change the rest of Chat code so it does not make that assumption)


    (PROG [(X (WINDOWPROP WINDOW (QUOTE CHATSTATE]
          (COND
	    ((AND X (SETQ X (fetch TYPEOUTPROC of X)))
	      (PROCESS.APPLY X (FUNCTION CHAT.DID.RESHAPE])

(CHAT.TTYENTRYFN
  [LAMBDA (PROCESS)                                          (* bvm: "12-Jul-84 17:36")
                                                             (* Switch to a chat window)
    (DECLARE (GLOBALVARS \CURRENTINTERRUPTS CHAT.INTERRUPTS))
    (PROG ((WINDOW (PROCESSPROP PROCESS (QUOTE WINDOW)))
	   STATE INTERRUPTS)
          (COND
	    ([AND WINDOW (SETQ STATE (WINDOWPROP WINDOW (QUOTE CHATSTATE]
	      (replace HELD of STATE with NIL)))
          [SETQ INTERRUPTS (for PAIR in (APPEND \CURRENTINTERRUPTS) collect (INTERRUPTCHAR
									      (CAR PAIR]
                                                             (* Turn everything off, then turn selected interrupts 
							     back on)
          (PROCESSPROP PROCESS (QUOTE CHAT.INTERRUPTS)
		       (NCONC (MAPCAR CHAT.INTERRUPTS (FUNCTION INTERRUPTCHAR))
			      INTERRUPTS))
          (PROCESSPROP PROCESS (QUOTE CHAT.KEYACTIONS)
		       (for PAIR in CHAT.KEYACTIONS collect (CONS (CAR PAIR)
								  (KEYACTION (CAR PAIR)
									     (CDR PAIR])

(CHAT.TTYEXITFN
  [LAMBDA (PROCESS NEWPROCESS)                               (* bvm: "12-Jul-84 17:36")
    (MAPC (PROCESSPROP PROCESS (QUOTE CHAT.INTERRUPTS)
		       NIL)
	  (FUNCTION INTERRUPTCHAR))
    (for PAIR in (PROCESSPROP PROCESS (QUOTE CHAT.KEYACTIONS)
			      NIL)
       do (KEYACTION (CAR PAIR)
		     (CDR PAIR])

(CHAT.TYPESCRIPT
  [LAMBDA (STATE)                                            (* bvm: " 2-Jun-84 15:43")
    (PROG ((PROC (fetch TYPEOUTPROC of STATE)))
          (COND
	    (PROC (PROCESS.APPLY PROC (FUNCTION CHAT.TYPESCRIPT1)
				 (LIST STATE])

(CHAT.TYPESCRIPT1
  [LAMBDA (CHATSTATE)
    (DECLARE (USEDFREE TYPESCRIPTSTREAM WINDOW))             (* bvm: " 2-Jun-84 16:51")
                                                             (* Called in context of type-out proc to change the 
							     dribble file)
    (PROG ((PWINDOW (GETPROMPTWINDOW WINDOW))
	   FILE OLDFILE)
          (CLEARW PWINDOW)
          (COND
	    ((NEQ (SETQ FILE (MKATOM (PROMPTFORWORD "Typescript to file (cr to close): " NIL NIL 
						    PWINDOW)))
		  T)
	      (CLEARW PWINDOW)
	      (COND
		[[OR (NULL FILE)
		     (NLSETQ (SETQ FILE (OPENSTREAM FILE (QUOTE OUTPUT)
						    (QUOTE NEW]
		  (COND
		    (TYPESCRIPTSTREAM (printout PWINDOW (CLOSEF TYPESCRIPTSTREAM)
						" closed.  ")))
		  (replace TYPESCRIPTOFD of CHATSTATE with (SETQ TYPESCRIPTSTREAM FILE))
		  (AND FILE (printout PWINDOW "Opened " (FULLNAME FILE]
		(T (printout PWINDOW "Could not open " FILE])
)



(* for EMACS)

(DEFINEQ

(CHAT.EMACS.MOVE
  [LAMBDA NIL                                                (* rda: "27-Aug-84 01:13")
    (DECLARE (USEDFREE FONTHEIGHT FONTWIDTH WINDOW XPOS YPOS))

          (* * This function is invoked in the context of the typeout process, so that we can easily see where we are on the
	  display, and so that we don't hang up the mouse if connection gets in trouble)


    (PROG ([OUTSTREAM (fetch (CHATUSERSTATE OUTSTREAM) of (WINDOWPROP WINDOW (QUOTE CHATSTATE]
	   (CLOC (CURSORPOSITION NIL WINDOW))
	   DROW CCOLUMN)

          (* * The characters are FONTHEIGHT high by FONTWIDTH wide)


          [COND
	    ((IGEQ XPOS FONTWIDTH)                           (* Go back to column 0)
	      (BOUT OUTSTREAM (fetch EMCOL0 of CHAT.EMACSCOMMANDS]
          (SETQ DROW (IDIFFERENCE (IQUOTIENT YPOS FONTHEIGHT)
				  (IQUOTIENT (fetch YCOORD of CLOC)
					     FONTHEIGHT)))

          (* * Positive DROW means go DOWN)


          [COND
	    ((ILESSP DROW 0)                                 (* Go up DROW rows)
	      (COND
		((NEQ DROW -1)
		  (BOUT OUTSTREAM (fetch EMARG of CHAT.EMACSCOMMANDS))
		  (PRIN3 (MKSTRING (IMINUS DROW))
			 OUTSTREAM)))
	      (BOUT OUTSTREAM (fetch EMUP of CHAT.EMACSCOMMANDS)))
	    ((IGREATERP DROW 0)                              (* Go down DROW rows)
	      (COND
		((NEQ DROW 1)
		  (BOUT OUTSTREAM (fetch EMARG of CHAT.EMACSCOMMANDS))
		  (PRIN3 (MKSTRING DROW)
			 OUTSTREAM)))
	      (BOUT OUTSTREAM (fetch EMDOWN of CHAT.EMACSCOMMANDS]
          (SETQ CCOLUMN (IQUOTIENT (fetch XCOORD of CLOC)
				   FONTWIDTH))
          [COND
	    ((IGREATERP CCOLUMN 0)                           (* Now go to the correct column)
	      (COND
		((NEQ CCOLUMN 1)
		  (BOUT OUTSTREAM (fetch EMARG of CHAT.EMACSCOMMANDS))
		  (PRIN3 (MKSTRING CCOLUMN)
			 OUTSTREAM)))
	      (BOUT OUTSTREAM (fetch EMFORWARD of CHAT.EMACSCOMMANDS]
          (FORCEOUTPUT OUTSTREAM])

(CHAT.SWITCH.EMACS
  [LAMBDA (CHATSTATE WINDOW)                                 (* rda: "22-Aug-84 16:40")

          (* * Toggles the value of CHAT.IN.EMACS?)


    (replace CHATINEMACS of CHATSTATE with (NOT (fetch CHATINEMACS of CHATSTATE)))
                                                             (* Now update title to show Emacs state)
    (CHAT.SCREENPARAMS NIL WINDOW])
)

(ADDTOVAR CHAT.DRIVERTYPES (DM2500 . DMCHAT.HANDLECHARACTER))

(RPAQ? CHAT.EMULATORTYPE (QUOTE DM2500))

(RPAQ? CHAT.DISPLAYTYPE 10)

(RPAQ? CHAT.METACHAR 195)

(RPAQ? CHAT.CONTROLCHAR 193)

(RPAQ? CHAT.INTERRUPTS )

(RPAQ? CHAT.KEYACTIONS )

(RPAQ? DEFAULTCHATHOST )

(RPAQ? CHATDEBUGFLG )

(RPAQ? CHATWINDOWLST )

(RPAQ? CHATWINDOW )

(RPAQ? CHAT.AUTOCRLF T)

(RPAQ? CLOSECHATWINDOWFLG )

(RPAQ? CHAT.ALLHOSTS )

(RPAQ? CHAT.HOSTMENU )

(RPAQ? CHAT.FONT )

(RPAQ? CHAT.IN.EMACS? NIL)

(RPAQ? CHAT.EMACSCOMMANDS (QUOTE (21 16 14 6 1)))

(RPAQ? CHAT.WAIT.TIME 2000)

(RPAQ? CHAT.PROTOCOLS )

(RPAQQ CHATMENU NIL)

(RPAQQ CHAT.REOPENMENU NIL)

(RPAQQ CHATMENUITEMS ((Close (QUOTE Close)
			     "Closes the connection and returns")
		      (Suspend (QUOTE Suspend)
			       "Closes the connection but leaves window up")
		      (New (QUOTE New)
			   "Closes this connection and prompts for a new host")
		      (Freeze (QUOTE Freeze)
			      "Holds typeout in this window until you bug it again")
		      (Clear (FUNCTION CHAT.CLEAR.FROM.MENU)
			     "Clears window, sets roll mode")
		      ("Dribble" (FUNCTION CHAT.TYPESCRIPT)
				 "Starts a typescript of window typeout")
		      ("Input" (FUNCTION CHAT.TAKE.INPUT)
			       "Allows input from a file")
		      ("Emacs" (FUNCTION CHAT.SWITCH.EMACS)
			       "Toggle EMACS positioning")
		      ("Option" (FUNCTION DO.CHAT.OPTION)
				"Do protocol specific option")))

(RPAQQ NETWORKLOGINFO ((TENEX (LOGIN "LOGIN " USERNAME " " PASSWORD " 
")
			      (ATTACH "ATTACH " USERNAME " " PASSWORD " 
")
			      (WHERE "WHERE " USERNAME CR "ATTACH " USERNAME " " PASSWORD CR))
		       (TOPS20 (LOGIN "LOGIN " USERNAME CR PASSWORD CR)
			       (ATTACH "ATTACH " USERNAME "" CR PASSWORD CR)
			       (WHERE "LOGIN " USERNAME CR PASSWORD CR))
		       (UNIX (LOGIN WAIT USERNAME CR WAIT PASSWORD CR))
		       (IFS (LOGIN "Login " USERNAME " " PASSWORD CR)
			    (ATTACH))))
(DECLARE: EVAL@COMPILE DONTCOPY 
(DECLARE: DOEVAL@COMPILE DONTCOPY

(LOCALVARS)
)


(RPAQQ CHATDEFS ((RECORDS CHATUSERSTATE EMACSCOMMANDS)
	(GLOBALVARS CHAT.ALLHOSTS CHAT.AUTOCRLF CHAT.CONTROLCHAR CHAT.DISPLAYTYPE CHAT.EMACSCOMMANDS 
		    CHAT.FONT CHAT.HOSTMENU CHAT.INTERRUPTS CHAT.KEYACTIONS CHAT.METACHAR 
		    CHAT.REOPENMENU CHAT.WAIT.TIME CHATDEBUGFLG CHATMARKTYPES CHATMENU CHATWINDOW 
		    CHATWINDOWLST CLOSECHATWINDOWFLG DEFAULTCHATHOST INVERTWINDOWFN NETWORKLOGINFO 
		    PUPTYPES \CURRENTINTERRUPTS CHATMENUITEMS CHAT.EMULATORTYPE CHAT.DRIVERTYPES)))
[DECLARE: EVAL@COMPILE 

(RECORD CHATUSERSTATE (HELD RUNNING? INSTREAM OUTSTREAM CARETSTATE TYPESCRIPTOFD TYPEOUTPROC 
			    CHATINEMACS))

(RECORD EMACSCOMMANDS (EMARG EMUP EMDOWN EMFORWARD EMCOL0))
]
(DECLARE: DOEVAL@COMPILE DONTCOPY

(GLOBALVARS CHAT.ALLHOSTS CHAT.AUTOCRLF CHAT.CONTROLCHAR CHAT.DISPLAYTYPE CHAT.EMACSCOMMANDS 
	    CHAT.FONT CHAT.HOSTMENU CHAT.INTERRUPTS CHAT.KEYACTIONS CHAT.METACHAR CHAT.REOPENMENU 
	    CHAT.WAIT.TIME CHATDEBUGFLG CHATMARKTYPES CHATMENU CHATWINDOW CHATWINDOWLST 
	    CLOSECHATWINDOWFLG DEFAULTCHATHOST INVERTWINDOWFN NETWORKLOGINFO PUPTYPES 
	    \CURRENTINTERRUPTS CHATMENUITEMS CHAT.EMULATORTYPE CHAT.DRIVERTYPES)
)
)

(RPAQ? INVERTWINDOWFN (QUOTE INVERTW))
(DEFINEQ

(\SPAWN.CHAT
  [LAMBDA NIL                                                (* bvm: "22-Apr-84 22:41")
                                                             (* From the Background Menu, runs CHAT as a process)
    (AND (THIS.PROCESS)
	 (TTY.PROCESS (ADD.PROCESS (QUOTE (CHAT NIL NIL NIL NIL T])
)
(DECLARE: DONTEVAL@LOAD DOCOPY 

(ADDTOVAR BackgroundMenuCommands (CHAT (QUOTE (\SPAWN.CHAT))
				       "Runs a new CHAT process; prompts for host"))

(SETQ BackgroundMenu)

(FILESLOAD BSP)
)
(PUTPROPS CHAT COPYRIGHT ("Xerox Corporation" 1982 1983 1984))
(DECLARE: DONTCOPY
  (FILEMAP (NIL (2427 16662 (CHAT 2437 . 5415) (CHAT.INIT 5417 . 6206) (FIND.CHAT.PROTOCOL 6208 . 6640) 
(CHAT.TYPEIN 6642 . 11582) (CHAT.BIN 11584 . 11868) (CHAT.CLOSE 11870 . 14275) (CHAT.CLOSEFN 14277 . 
14548) (CHAT.CLOSE.CONNECTION 14550 . 14862) (CHAT.LOGIN 14864 . 16660)) (16688 19289 (
ADD.CHAT.MESSAGE 16698 . 16920) (CHAT.LOGINFO 16922 . 17231) (CHAT.SENDSCREENPARAMS 17233 . 17581) (
CHAT.SETDISPLAYTYPE 17583 . 17967) (CHAT.LOGINFO 17969 . 18278) (CHAT.FLUSH&WAIT 18280 . 18588) (
CHAT.ENDOFSTREAMOP 18590 . 18966) (CHAT.OPTIONMENU 18968 . 19287)) (19333 35161 (CHAT.TYPEOUT 19343 . 
22142) (CHAT.RESET.DISPLAY.PARMS 22144 . 23007) (CHAT.DID.RESHAPE 23009 . 23359) (
DMCHAT.HANDLECHARACTER 23361 . 27281) (CHAT.SCREENPARAMS 27283 . 28367) (CHAT.ADDCHAR 28369 . 29047) (
CHAT.ADDLINE 29049 . 29544) (DMCHAT.ADDRESS 29546 . 30601) (CHAT.CLEAR 30603 . 30813) (CHAT.CLEARMODES
 30815 . 31136) (CHAT.DELCHAR 31138 . 31846) (CHAT.DELETELINE 31848 . 32304) (CHAT.DOWN 32306 . 32888)
 (CHAT.ERASE.TO.EOL 32890 . 33117) (CHAT.ERASEBITS 33119 . 33338) (CHAT.HOME 33340 . 33508) (CHAT.LEFT
 33510 . 33715) (DMCHAT.NEWLINE 33717 . 34255) (DMCHAT.PRINTCHAR 34257 . 34625) (DMCHAT.RIGHT 34627 . 
34956) (CHAT.UP 34958 . 35159)) (35187 44508 (GETCHATWINDOW 35197 . 36257) (CHAT.BUTTONFN 36259 . 
36725) (CHAT.HOLD 36727 . 37229) (CHAT.MENU 37231 . 38599) (CHAT.CLEAR.FROM.MENU 38601 . 38804) (
CHAT.TAKE.INPUT 38806 . 39026) (CHAT.TAKE.INPUT1 39028 . 39841) (DO.CHAT.OPTION 39843 . 40235) (
CHAT.RECONNECT 40237 . 41029) (CHAT.RESHAPEWINDOW 41031 . 41842) (CHAT.TTYENTRYFN 41844 . 42936) (
CHAT.TTYEXITFN 42938 . 43286) (CHAT.TYPESCRIPT 43288 . 43550) (CHAT.TYPESCRIPT1 43552 . 44506)) (44531
 46989 (CHAT.EMACS.MOVE 44541 . 46563) (CHAT.SWITCH.EMACS 46565 . 46987)) (50305 50632 (\SPAWN.CHAT 
50315 . 50630)))))
STOP