;;;; ;;;; s t e r m . s t k -- A simple terminal emulator written in Scheme ;;;; ;;;; Copyright © 1993-1999 Erick Gallesio - I3S-CNRS/ESSI ;;;; ;;;; Permission to use, copy, modify, distribute,and license this ;;;; software and its documentation for any purpose is hereby granted, ;;;; provided that existing copyright notices are retained in all ;;;; copies and that this notice is included verbatim in any ;;;; distributions. No written agreement, license, or royalty fee is ;;;; required for any of the authorized uses. ;;;; This software is provided ``AS IS'' without express or implied ;;;; warranty. ;;;; ;;;; Author: Erick Gallesio [eg@unice.fr] ;;;; Creation date: 2-Oct-1995 22:57 ;;;; Last file update: 3-Sep-1999 19:55 (eg) (require "process") (define (sterm) (define prompt-color "red") (define output-color "blue") (define update-counter 0) (define (close-sterm w) (w 'insert "end" "*** EOF ***" "list-prompt") ;; Set state to "disabled" (or delete the binding). Not doing so, ;; stops the interpreter. The bugs seems to be in Tk event loop, since ;; similar code works without event. (tk-set! w :state "disabled")) (define (insert-prompt w file) (let Loop () (when (char-ready? file) (let ((c (read-char file))) (cond ((eof-object? c) (when-port-readable file #f) (close-sterm w)) (ELSE ;; Insert this char and try to read another one (w 'insert "end" (string c) "list-prompt") (Loop)))))) ;; Add a mark to the current position (w 'mark 'set "start_expr" "end-1c") (w 'mark 'gravity "start_expr" "left") (w 'see "end")) (define (insert-line w line) (w 'insert "insert" line "list-output" "\n" "") (w 'see "end") ;; Force a redisplay when we have a bunch of lines to animate screen (if (= update-counter 5) (begin (update 'idle) (set! update-counter 0)) (set! update-counter (+ update-counter 1)))) (define (read-a-line w file) (let Loop () (when (char-ready? file) (let ((l (read-line file))) (if (eof-object? l) (when-port-readable file #f) (begin (insert-line w l) (Loop))))))) (define (run-shell shell) (let ((TERM (getenv "TERM"))) (setenv! "TERM" "dumb") ; to avoid esc characters given by modern shell (let ((res (run-process shell "-i" :input :pipe :output :pipe :error :pipe))) (setenv! "TERM" TERM) ; reset original TERM res))) (define (make-term name closure . tk-args) (let ((w (apply Tk:text (format #f "~A.t" name) tk-args)) (s (scrollbar (format #f "~A.s" name) :orient "vert"))) (pack w :expand #t :fill "both" :side "left") (pack s :expand #f :fill "y" :side "right") ;; Associate bindings to the scrollbar (tk-set! w :yscroll (lambda l (apply s 'set l))) (tk-set! s :command (lambda l (apply w 'yview l))) (bind w "" closure) (w 'tag 'configure "list-prompt" :foreground prompt-color) (w 'tag 'configure "list-output" :foreground output-color) w)) (let* ((p (run-shell (or (getenv "SHELL") "/bin/sh"))) (in (process-input p)) (out (process-output p)) (err (process-error p)) (top (toplevel (gensym ".term"))) (t #f) ;; Will be set later since it needs C defined below (C (lambda () (let ((txt (t 'get "start_expr" "insert"))) (display txt in) (newline in) (flush in) 'continue)))) (set! t (make-term (widget-name top) C :font "fixed" :setgrid #t)) (pack t :expand #t :fill "both") ;; Create handlers (when-port-readable err (lambda () (insert-prompt t err))) (when-port-readable out (lambda () (read-a-line t out))))) (provide "sterm")