heiser@ethz.UUCP (Gernot Heiser) (08/13/88)
Posting-number: Volume 4, Issue 31 Submitted-by: "Gernot Heiser" <heiser@ethz.UUCP> Archive-name: gnumacs-blit/Part3 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 3)." # Contents: emacs/mega-lilith.el emacs/mega-lilith.tex # Wrapped by heiser@eiger on Sun Aug 7 14:16:02 1988 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'emacs/mega-lilith.el' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'emacs/mega-lilith.el'\" else echo shar: Extracting \"'emacs/mega-lilith.el'\" \(22798 characters\) sed "s/^X//" >'emacs/mega-lilith.el' <<'END_OF_FILE' X;; GNU Emacs code for Teletype 5620 mouse. X;; Copyright (C) Free Software Foundation Sept 1987. X X X;; This file is part of GNU Emacs. X X;; Gnu Emacs is distributed in the hope that it will be useful, X;; but WITHOUT ANY WARRANTY. No author or distributor X;; accepts responsibility to anyone for the consequences of using it X;; or for whether it(se serves any particular purpose or works at all, X;; unless he says so in writing. Refer to the GNU Emacs General Public X;; License for full details. X X;; Everyone is granted permission to copy, modify and redistribute X;; GNU Emacs, but only under the conditions described in the X;; GNU Emacs General Public License. A copy of this license is X;; supposed to have been given to you along with GNU Emacs so you X;; can know your rights and responsibilities. It should be in a X;; file named COPYING. Among other things, the copyright notice X;; and this notice must be preserved on all copies. X X X;;; Author: paolo conti, September 1987 (conti@iis.uucp) X;;; ideas stolen from the version for the BBN Bitgraph mouse by John Robinson X X(provide 'mega-lilith) X X(defconst selection ?S X "letter terminating selection events") X X(defconst click ?R X "letter terminating normal mouse events") X X(defconst compressed-click ?Q X "letter terminating compressed mouse events") X X(defconst layer-resized ?C X "letter terminating layer size events") X X(defconst resizing-event ?c X "letter terminating resizing events communicated autonomously by X the terminal") X X(defconst select 1 X "reported by terminal for a selection event") X X(defconst copy 2 X "reported by terminal for a copy event") X X(defconst delete 3 X "reported by terminal for a delete event") X X(defconst move 4 X "reported by terminal for a move event") X X(defun 5620-mouse-report () X "called every time a mouse event has happened; it decodes the event and executes the required actions" X (interactive) X (let* ((event (5620-get-mouse-event)) X (type (car event)) X (parameters (cdr event))) X (cond X ((eq type selection) X (5620-handle-selection parameters)) X ((eq type click) X (5620-handle-click parameters)) X ((eq type compressed-click) X (5620-handle-compressed-click parameters)) X ((eq type layer-resized) X (5620-resize-layer parameters)) X ((eq type resizing-event) X (read-char) ;; to flush the lf following the "c" in resizing events X (5620-redraw-display))))) X X(defun 5620-handle-selection (number-list) X "checks wether the selected region is legal (i.e. in one window or Xin two abutting windows on the same buffer); if so, executes the requested Xaction on the region (i.e. select, move, copy or delete)" X (let* ((y0 (car number-list)) X (x0 (nth 1 number-list)) X (y1 (nth 2 number-list)) X (x1 (nth 3 number-list)) X (action (nth 4 number-list)) X (window0 (5620-pos-to-window x0 y0)) X (window1 (5620-pos-to-window x1 y1)) X (edges0 (window-edges window0)) X (edges1 (window-edges window1)) X (top1-of-0 (eq (nth 3 edges0) X (nth 1 edges1))) X (top0-of-1 (eq (nth 3 edges1) X (nth 1 edges0)))) X ;;; check for all possible invalid selections X (cond ((not (or (eq window0 window1) top0-of-1 top1-of-0)) X (error "invalid selection; windows don't abut")) X ((not (eq (window-buffer window0) (window-buffer window1))) X (error "invalid selection; windows not on same buffer")) X ((or (eq y0 (screen-height)) (eq y1 (screen-height))) X (error "invalid selection; selection ends in mini-buffer")) X ((or (eq y0 (nth 3 edges0)) X (eq y1 (nth 3 edges1))) X (error "invalid selection; selection ends in mode line")) X ((or (= x0 0) X (= x1 0)) X (error "invalid selection; selection ends in scroll bar")) X ;;; treat a valid selection X (t X (let ((old-window (selected-window)) X (old-point (point-marker)) X (window-x0 (1- (- x0 (nth 0 edges0)))) X (window-y0 (1- (- y0 (nth 1 edges0)))) X (window-x1 (1- (- x1 (nth 0 edges1)))) X (window-y1 (1- (- y1 (nth 1 edges1)))) X end-of-selection X point-in-new-window) X ;find and save end of selection X (select-window window1) X (5620-move-point-to-x-y window-x1 window-y1) X (setq end-of-selection (point)) X ;go to start of selection X (select-window window0) X (setq point-in-new-window (point)) X (5620-move-point-to-x-y window-x0 window-y0) X ;treat according to action X (cond X ((/= end-of-selection (point)) X (cond X ((or (eq action select) (eq action copy)) X ; avoid appending in previous kill entry X (setq last-command nil) X (copy-region-as-kill (point) end-of-selection)) X ; case action move or delete X (t X (setq last-command nil) X (kill-region (point) end-of-selection))) X (cond X ((or (eq action copy) (eq action move)) X (select-window old-window) X (goto-char (marker-position old-point)) X (setq this-command 'yank) X (yank) X (pop-mark) X (princ "")) X ((eq action select) X (goto-char point-in-new-window) X (select-window old-window) X (goto-char (marker-position old-point))))) X ;;; selection is empty; X (t X (push-mark end-of-selection) X (goto-char point-in-new-window) X (select-window old-window) X (goto-char (marker-position old-point))))))))) X X X;;; the following information may be accessed by the functions X;;; bound to mouse events; these functions all don't take parameters X;;; but access global variables X X(defvar buttons0 0 X "buttons pressed for the current click event") X X(defvar x0 X "screen column where a mouse button was last pressed") X X(defvar y0 X "screen line where a mouse button was last pressed") X X(defvar window0 X "window in which a mouse button was last pressed") X X(defvar site0 X "site in which a mouse button was last pressed; sites include the scroll-bar, Xthe mode-line etc") X X(defvar x1 X "screen column where the mouse button(s) was (were) released") X X(defvar y1 X "screen line where the mouse button(s) was (were) released") X X X(defun 5620-handle-click (parameters) X "handles the mouse events which are not selection events; calls the function Xbound to the given region of the screen and to the givenn buttons" X (let ((new-buttons (nth 2 parameters))) X (cond X ((= buttons0 0) X (setq buttons0 new-buttons) X (setq y0 (car parameters)) X (setq x0 (nth 1 parameters)) X (let ((site-info (5620-get-site-info x0 y0))) X (setq site0 (car site-info)) X (setq window0 (nth 1 site-info)))) X ((> new-buttons buttons0) X (setq buttons0 new-buttons)) X ((= new-buttons 0) X (setq y1 (car parameters)) X (setq x1 (nth 1 parameters)) X (let ((action (aref (aref 5620-button-actions site0) X (1- buttons0)))) X (setq buttons0 0) X (cond X ((not (null action)) X (funcall action)) X (t X (error "Gaat's no? No action defined for those buttons"))))) X ;;; some, but not all buttons released X (t nil)))) X X X(defun 5620-handle-compressed-click (parameters) X "handles the mouse events which are not selection events; calls the function Xbound to the given region of the screen and to the given buttons" X (setq y0 (car parameters)) X (setq x0 (nth 1 parameters)) X (setq y1 (nth 2 parameters)) X (setq x1 (nth 3 parameters)) X (setq buttons0 (nth 4 parameters)) X (let ((site-info (5620-get-site-info x0 y0))) X (setq site0 (car site-info)) X (setq window0 (nth 1 site-info))) X (let ((action (aref (aref 5620-button-actions site0) X (1- buttons0)))) X (setq buttons0 0) X (cond X ((not (null action)) X (funcall action)) X (t X (error "Gaat's no? No action defined for those buttons"))))) X X(defun 5620-get-site-info (x y) X "returns a list with the site (scroll-bar, buffer-area etc) and with the Xwindow the coordinates lie in" X (let* ((window (5620-pos-to-window x y)) X (edges (window-edges window)) X (site X (cond X ((= y (screen-height)) X (cond X ((= x 0) X echo-close-box) X (t X echo-area))) X ((= x 0) X (cond X ((= y (nth 3 edges)) X close-box) X (t scroll-bar))) X ((= y (nth 3 edges)) X mode-line) X (t buffer-area)))) X (list site window))) X X;;; X;;; functions which may be bound to clicks in different sites X;;; X X(defun 5620-scroll-up () X "scrolls the line the mouse was clicked in to the top of its window" X (let ((site-info (5620-get-site-info x1 y1)) X (current-window (selected-window))) X (cond X ((and (eq (car site-info) scroll-bar) X (eq (nth 1 site-info) window0)) X (select-window window0) X (scroll-up (- y1 1 (nth 1 (window-edges window0)))) X (select-window current-window))))) X X(defconst 5620-symmetric-scrolling nil X "*if nil, the action 5620-scroll-down is the perfect inverse of X5620-scroll-up; otherwise the pointed line is scrolled to the bottom") X X(defun 5620-scroll-down () X "scrolls down in the window the mouse was clicked in; the semantic depends X on the value of the variable 5620-scroll-line-to-bottom" X (let ((site-info (5620-get-site-info x1 y1)) X (current-window (selected-window))) X (cond X ((and (eq (car site-info) scroll-bar) X (eq (nth 1 site-info) window0)) X (select-window window0) X (cond X ((null 5620-symmetric-scrolling) X (scroll-up (- (1+ y1) (nth 3 (window-edges window0))))) X (t (scroll-up (- (nth 1 (window-edges window0)) (1- y1))))) X (select-window current-window))))) X X(defun 5620-set-point () X "selects the window where the mouse was clicked and sets it's point at Xthe proper location" X (let ((site-info (5620-get-site-info x1 y1))) X (cond ((and (eq (car site-info) buffer-area) X (eq (nth 1 site-info) window0)) X (select-window window0) X (let ((edges0 (window-edges window0))) X (5620-move-point-to-x-y X (1- (- x1 (nth 0 edges0))) X (1- (- y1 (nth 1 edges0))))))))) X X(defun 5620-yank () X "inserts the top of the kill ring at point (and not at the location of Xthe mouse click!); if the mouse was not clicked in the selected-window, Xthe function does nothing" X (let ((site-info (5620-get-site-info x1 y1))) X (cond X ((and (eq (car site-info) buffer-area) X (eq (nth 1 site-info) window0) X (eq window0 (selected-window))) X (setq this-command 'yank) X (yank))))) X X(defun 5620-split-window () X "splits the window in which the mouse was clicked at the line the click Xoccured" X (let ((site-info (5620-get-site-info x1 y1)) X (current-window (selected-window))) X (cond X ((and (eq (car site-info) scroll-bar) X (eq (nth 1 site-info) window0)) X (select-window window0) X (let ((edges0 (window-edges window0))) X (split-window-vertically (- y1 (nth 1 edges0))) X (select-window current-window)))))) X X(defun 5620-move-border () X "moves the border on which the mouse was pressed to the line the mouse Xwas released. Allows to enlarge a window at the cost of its neighbour. XThe neighbour window can't be killed with 5620-move-border. The echo area Xcan't be enlarged with 5620-move-border." X (cond X ((and (eq site0 mode-line) X (/= y0 (1- (screen-height)))) X (let* ((current-window (selected-window)) X (edges0 (window-edges window0)) X (offset (- y1 (nth 3 edges0))) X (enable X (cond X ((< offset 0) X (>= offset (- window-min-height (window-height window0)))) X ((> offset 0) X (<= offset X (- (window-height (5620-pos-to-window x0 (1+ y0))) X window-min-height)))))) X (cond X (enable X (select-window window0) X (enlarge-window offset) X (cond X ((window-point current-window) X (select-window current-window))))))))) X X(defun 5620-flip-from-column () X "displays in the window the part of the buffer corresponding to the ratio Xbetween the column where the mouse was clicked and the number of columns of Xthe screen. Flipping in the first column displays the top of the buffer, flipping Xin the central column the central part of the buffer etc" X (let ((site-info (5620-get-site-info x1 y1)) X (current-window (selected-window))) X (cond X ((and (eq (car site-info) site0) X (eq (nth 1 site-info) window0)) X (select-window window0) X X ;; we'd like to have X ;; (goto-char (+ (point-min) X ;; (/ (* (1- x1) X ;; (- (point-max) (point-min))) X ;; (1- (window-width))))) X ;; but we must avoid the first multiplication in order to avoid X ;; an overflow for very long files; X (let* ((a (1- x1)) X (b (- (point-max) (point-min))) X (c (1- (window-width))) X (a_div_c (/ a c)) X (a_mod_c (% a c)) X (b_div_c (/ b c)) X (b_mod_c (% b c))) X (goto-char ( + (point-min) X (+ (* c a_div_c b_div_c) X (* a_div_c b_mod_c) X (* a_mod_c b_div_c) X (/ (* a_mod_c b_mod_c) c))))) X (select-window current-window))))) X X(defun 5620-flip-from-line () X "displays in the window the part of the buffer corresponding to the ratio Xbetween the window line where the mouse was clicked and the number of lines of Xthe screen. Flipping in the top line displays the top of the buffer, flipping Xin the middle of the window the central part of the buffer etc XNote: 5620-flip-from-column will usually permit a more precise flipping, since most windows have much more columns than lines" X (let ((site-info (5620-get-site-info x1 y1)) X (current-window (selected-window))) X (cond X ((and (eq (car site-info) site0) X (eq (nth 1 site-info) window0)) X (select-window window0) X X ;; we'd like to have X ;; (goto-char ( + (point-min) X ;; (/ (* (1- (- y1 (nth 1 (window-edges)))) X ;; (- (point-max) (point-min))) X ;; (- (window-height) 2)))) X ;; but we must avoid the first multiplication in order to avoid X ;; an overflow for very long files; X (let* ((a (1- (- y1 (nth 1 (window-edges))))) X (b (- (point-max) (point-min))) X (c (- (window-height) 2)) X (a_div_c (/ a c)) X (a_mod_c (% a c)) X (b_div_c (/ b c)) X (b_mod_c (% b c))) X (goto-char ( + (point-min) X (+ (* c a_div_c b_div_c) X (* a_div_c b_mod_c) X (* a_mod_c b_div_c) X (/ (* a_mod_c b_mod_c) c))))) X (select-window current-window))))) X X(defun 5620-select-window () X "makes the window the mouse was clicked in the active window" X (let ((site-info (5620-get-site-info x1 y1)) X (current-window (selected-window))) X (cond X ((and (eq (car site-info) site0) X (eq (nth 1 site-info) window0)) X (select-window window0))))) X X(defun 5620-delete-window () X "kills the window the mouse was clicked in" X (let ((site-info (5620-get-site-info x1 y1)) X (current-window (selected-window))) X (cond X ((and (eq (car site-info) site0) X (eq (nth 1 site-info) window0)) X (delete-window window0) X (cond X ((window-point current-window) X (select-window current-window))))))) X X(defun 5620-select-minibuffer () X "Selects the minibuffer if it is active" X (let ((edges (window-edges)) X (window nil)) X (while (and (not (eq window (selected-window))) X (> (screen-height) (nth 3 edges))) X (setq window (next-window window)) X (setq edges (window-edges window))) X (select-window window))) X X(defun 5620-find-forward () X "Searches forward for the string in the kill ring" X (let ((search-string (car kill-ring))) X (cond X ((looking-at search-string) X (search-forward search-string))) X (search-forward search-string))) X X(defun 5620-find-backward () X "Searches backward for the string in the kill ring." X (let ((search-string (car kill-ring)) X (old-point (point))) X (search-backward search-string) X (cond X ((eq (length search-string) X (- old-point (point))) X (search-backward search-string))))) X X(defun 5620-set-sam-scrolling () X "Binds the buttons in the scroll-bar the same way the editor sam does" X (interactive) X (5620-bind-click 'right 'scroll-bar '5620-scroll-up) X (5620-bind-click 'left 'scroll-bar '5620-scroll-down) X (5620-bind-click 'middle 'scroll-bar '5620-flip-from-line) X (5620-bind-click 'left-middle 'scroll-bar '5620-split-window)) X X(defun 5620-set-lilith-scrolling () X "Binds the buttons in the scroll-bar the default way" X (interactive) X (5620-bind-click 'left 'scroll-bar '5620-scroll-up) X (5620-bind-click 'right 'scroll-bar '5620-scroll-down) X (5620-bind-click 'middle 'scroll-bar '5620-split-window)) X X X(defconst buffer-area 0 X "*the whole Emacs window except its mode line") X X(defconst scroll-bar 1 X "*the leftmost column of the screen") X X(defconst mode-line 2 X "*the lowest line of an Emacs window (inverted)") X X(defconst close-box 3 X "*the part of the scroll bar at the left of the mode line") X X(defconst echo-area 4 X "*the bottom line of the screen or layer") X X(defconst echo-close-box 5 X "*the part of the scroll bar at the left of the echo area") X X(defun make-vector-7 () X (vector nil nil nil nil nil nil nil)) X X(defvar 5620-button-actions X (vector (make-vector-7) (make-vector-7) (make-vector-7) X (make-vector-7) (make-vector-7) (make-vector-7))) X X X(defconst right 1 "*") X(defconst middle 2 "*") X(defconst middle-right 3 "*") X(defconst left 4 "*") X(defconst left-right 5 "*") X(defconst left-middle 6 "*") X(defconst left-middle-right 7 "*") X X(defun 5620-bind-click (buttons-symbol site-symbol action) X "Binds BUTTONS clicked in SITE to the function ACTION" X (interactive "vButtons: \nvSite: \naAction: ") X (let((site (eval site-symbol)) X (buttons (eval buttons-symbol))) X (if (and (integerp buttons) (> buttons 0) (< buttons 8)) X (cond X ((< site (length 5620-button-actions)) X (aset (aref 5620-button-actions site) (1- buttons) action)) X (t X (error (concat site-symbol " is not a predefined site")))) X (error "buttons should be between 1 and 7")))) X X(5620-bind-click 'left 'scroll-bar '5620-scroll-up) X(5620-bind-click 'right 'scroll-bar '5620-scroll-down) X(5620-bind-click 'middle 'scroll-bar '5620-split-window) X(5620-bind-click 'left 'buffer-area '5620-set-point) X(5620-bind-click 'middle 'buffer-area '5620-yank) X(5620-bind-click 'left 'close-box '5620-select-window) X(5620-bind-click 'left-right 'close-box '5620-delete-window) X(5620-bind-click 'middle 'mode-line '5620-move-border) X(5620-bind-click 'left 'mode-line '5620-flip-from-column) X(5620-bind-click 'left 'echo-area '5620-find-forward) X(5620-bind-click 'middle 'echo-area '5620-find-backward) X(5620-bind-click 'left 'echo-close-box '5620-select-minibuffer) X X(defun 5620-bind-selection-buttons (select-button copy-button) X "sets the SELECT-BUTTON and COPY-BUTTON; these must be different and Xboth must be in {left, middle, right}; the delete button is set automatically Xto the third button" X (interactive "vSelection Button: \nvCopy Button: ") X (setq select-button (eval select-button)) X (setq copy-button (eval copy-button)) X (cond X ((/= select-button copy-button) X (send-string-to-terminal X (concat "\e[?" 0 ";" select-button ";" copy-button "b"))) X (t X (error "selection and copy button must be different")))) X X X(defun 5620-get-tty-num () X "Read an integer from terminal, and return a list with the char following X the int and the int" X (let X ((num 0) X (char (- (read-char) 48))) X (while (and (>= char 0) X (<= char 9)) X (setq num (+ (* num 10) char)) X (setq char (- (read-char) 48))) X (list (+ char 48) num))) X X(defun 5620-get-mouse-event () X "Read a list of numbers from the terminal until one of them is not X terminated by a semi-colon and reports a list containing the numbers read" X (interactive) X (let ((result nil) X (next-item (5620-get-tty-num))) X (while X (= (car next-item) ?;) X (setq result (append result (cdr next-item))) X (setq next-item (5620-get-tty-num))) X (setq result (append result (cdr next-item))) X (cons (car next-item) result))) X X(defun 5620-move-point-to-x-y (x y) X "Position cursor in window coordinates. XX and Y are 0-based character positions in the window." X (move-to-window-line y) X (move-to-column (+ (current-column) X (min (- (window-width) 1) x)))) X X(defun 5620-pos-to-window (x y) X "Find window corresponding to screen coordinates. XX and Y are 1-based character positions on the screen." X (let ((edges (window-edges)) X (window nil)) X (setq x (max x 1)) X (while (and (not (eq window (selected-window))) X (or (<= y (nth 1 edges)) X (> y (nth 3 edges)) X (<= x (nth 0 edges)) X (> x (nth 2 edges)))) X (setq window (next-window window)) X (setq edges (window-edges window))) X (or window (selected-window)))) X X(defvar redraw-called-interacively nil X "tells wether 5620-redraw-display was called from the user, in which case Xthe screen is redrawn at least once, or wether it was called after the Xscreen size has been required, in which case the screen is redrawn only if Xit's size has changed; the solution with the global variable is very ugly") X X(defun 5620-redraw-display () X "refreshes the screen after having asked how big the layer currently is" X (interactive) X (setq redraw-called-interacively t) X (send-string-to-terminal "\e[?C")) X X(defun 5620-resize-layer (parameters) X (let ((new-height (nth 0 parameters)) X (new-width (1- (nth 1 parameters))) X layer-resized) X (cond X ((/= new-height (screen-height)) X (set-screen-height new-height) X (setq layer-resized t))) X (cond X ((/= new-width (screen-width)) X (set-screen-width new-width) X (setq layer-resized t))) X (cond X ((and X redraw-called-interacively X (null layer-resized)) X (redraw-display))) X (setq redraw-called-interacively nil))) X X(setq suspend-hook X (cond X ((and (boundp 'suspend-hook) suspend-hook) X (append suspend-hook '((send-string-to-terminal "\e[?0s")))) X (t X '(lambda () X (send-string-to-terminal "\e[?0s"))))) X X(setq suspend-resume-hook X (cond X ((and (boundp 'suspend-resume-hook) suspend-resume-hook) X (append suspend-resume-hook '((send-string-to-terminal "\e[?1s") X (send-string-to-terminal "\e[?C")))) X (t X '(lambda () X (send-string-to-terminal "\e[?1s") X (send-string-to-terminal "\e[?C"))))) X X;;; X;;; send initialization strings to the terminal X;;; X X;;; enable mouse X(send-string-to-terminal "\e[?1m") X X;;; enable scroll-bar X(send-string-to-terminal "\e[?1s") X X;;; take scroll bar in account X(set-screen-width (1- (screen-width))) X X;;;define default selection and copy buttons X(5620-bind-selection-buttons 'right 'middle) X X;;; unbind "\e[" if it is bound to a function, since otherwise X;;; "\e[" can't be bound X X(let ((binding (global-key-binding "\e["))) X (cond X ((and (not (null binding)) X (not (keymapp binding))) X (global-unset-key "\e[")))) X;; X;; X;; X(global-set-key "\e[?" '5620-mouse-report) X;; X;;; ask for current layer size X;; X(send-string-to-terminal "\e[?C") X;; X;; X;; X(run-hooks 'mega-lilith-hook)END_OF_FILE if test 22798 -ne `wc -c <'emacs/mega-lilith.el'`; then echo shar: \"'emacs/mega-lilith.el'\" unpacked with wrong size! fi # end of 'emacs/mega-lilith.el' fi if test -f 'emacs/mega-lilith.tex' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'emacs/mega-lilith.tex'\" else echo shar: Extracting \"'emacs/mega-lilith.tex'\" \(26703 characters\) sed "s/^X//" >'emacs/mega-lilith.tex' <<'END_OF_FILE' X\hyphenation{} X X\documentstyle[A4,E,ETHtechreport,11pt,IIS]{article} X\reportnumber{87/6} X X\begin{document} X X\title{Mega-Lilith: An Efficient Mouse Support for GNU Emacs Xon the Teletype DMD 5620} X X\author{Paolo Conti\\Gernot Heiser} X X\maketitle X X\begin{abstract} X XA software package which provides advanced mouse support for the GNU XEmacs editor on the Teletype DMD 5620 terminal is presented. It allows Xthe user to perform the basic editing operations delete, copy, move, Xsnarf and paste quickly and conveniently using the mouse. All these Xcommands can be executed in one step, even across windows and buffers. XMoreover, searching and window managing operations can be performed Xwith the mouse. The binding of different operations to particular Xmouse events can be redefined by the user, thus shortening the Xlearning period. The package consists of two parts: A terminal Xemulator for the layers program sends to the host information about Xmouse events and handles the appropriate inverting of selected Xregions. A package of Emacs Lisp functions performs the appropriate Xoperations. X X\end{abstract} X X\section{Introduction} X XThe present report describes a powerful interface between the GNU XEmacs editor and the mouse of a Teletype DMD 5620 terminal. The Xinterface consists of two parts: A terminal emulator running under Xlayers provides the user with appropriate feedback about the attempted Xediting operations and reports the user's intent to the host. A Xpackage of Lisp functions performs the actual operations in Emacs. X XFrom previous programming experience we learned that when developing Xor debugging a program, lot of time is spent in moving and copying Xaround portions of source code. Hence, the most important feature of Xthe mouse support for an editor is fast and convenient access to the Xbasic operations delete, copy and move. In particular, it should be Xpossible to move or copy portions of text (single identifiers or whole Xprocedures) from any place in the file and insert them at the current Xinsertion point with as little repositioning of the mouse as possible. XFor our implementation of these textual commands we have borrowed most Xideas from the editors running on the Lilith computer [Gut], since Xthey permit to rearrange portions of text in what we have found to be Xan efficient way. X XFurther operations supported by the Mega-Lilith mouse interface Xinclude creation, resizing, activation, deletion, Xscrolling and flipping of Emacs windows, and searching for strings. XAll that can be performed using nothing but the mouse. X XIn section 2, we explain the main properties of our mouse support for XEmacs. Section 3 describes the implemented functionality in detail. It can Xbe used as a user manual. Section 4 describes the terminal emulator Xneeded to support mouse editing on the 5620 terminal. Appendix A Xcontains a quick reference for the default settings of mouse commands, Xappendix B a list of available functions that can be bound to mouse Xevents and appendix C the Unix manual page of our terminal emulator. X X\section{Design concepts of the Mega-Lilith interface} X XThe Mega-Lilith interface meets the following requirements Xfor textual operations: Deleting, copying and moving of text Xcan be performed with a single mouse command, i.e. without Xrepositioning of point in between. The destination of a copy or move Xoperation needs not be in the same buffer or window as the source. XFurthermore, the size of the text portion to be Xselected must not necessarily fit on the screen or in one window. XText can be selected across two windows, provided the windows where the Xselection starts and ends display the same buffer. Hence, Xcopying a procedure or a full paragraph from one buffer Xto another is as fast and as easy as copying a single statement. X XThe implementation of these requirements forced a slight deviation Xfrom the original Emacs philosophy: copying or moving portions of text Xwithout repositioning point is not a standard Emacs feature. Since we Xwant to insert a selected portion of text at the current location of Xpoint, there is a difference between our notion of selected text and Xthe usual Emacs notion of region. The Emacs region is delimited by Xpoint and mark, which makes it impossible to select text without Xmodifying the location of the insertion point. Conversely, our Xselected text is delimited only by the mouse locations where the Xselecting operation started and ended. Moreover, the selection may Xextend over parts of two windows, something normally unknown in Emacs. X XA portion of text is selected by sweeping a part of the screen while Xkeeping the selection button depressed. The selected text portion is Xdisplayed in inverse video. The selected text can be deleted with the Xdelete button, copied to the location of point with the copy button or Xmoved (i.e. copied to the destination and deleted from the source) by Xpressing both the delete and copy button. After each insertion, point Xis left at the end of the inserted text. Hence portions of text can be Xcollected from different places and inserted at the current insertion Xposition without ever repositioning point. The selection, copy and Xdelete buttons are predefined, but the user can change the bindings Xaccording to taste. X XMega-Lilith allows to search forward and backward for the last Xselected string. For example, when wondering about the meaning of the Xparameters in a certain procedure call, the procedure's declaration Xcan be found with just a few mouse clicks. X XSeveral other commands are bound to mouse events: the portion of text Xdisplayed in a window can be changed with different scrolling and Xflipping commands, and windows can be created, resized and deleted by Xclicking the mouse in the appropriate screen areas. X XUser taylorability enhances the acceptance of a highly interactive Xtool considerably. A programmer will be less reluctant to use a new Xeditor if he or she can execute basic operations like positioning the Xinsertion mark or scrolling up a window with the same mouse buttons Xhe/she is used to. Hence, in Mega-Lilith, the binding of mouse buttons Xto commands can be changed (even interactively) by the user. Moreover, Xthe semantics of some operations depend on the settings of certain Xflags, allowing the user to set up an environment similar to what Xhe/she is used to. X XIn the design of our mouse interface we tried to minimize the amount Xof information exchanged between the terminal and the host. This Ximproves response times when working with low speed lines. Once the Xselection button has been pressed, the terminal autonomously does the Xhighlighting of the selected region (in different patterns, according Xto the type of the selection). Only after all buttons have been Xreleased, the terminal emulator communicates to the host the screen Xcoordinates of the start and end and the type of the selection. X X X\section{Functionality of Mega-Lilith} X X\subsection{Setting point and moving text around} X XWith Mega-Lilith the insertion point can be set anywhere with the mouse. Text Xdisplayed in Emacs windows can be deleted, copied or moved to another Xlocation and/or saved in the kill ring with mouse operations. The mark Xcan be set anywhere and the content of the kill ring can be inserted Xat the location of point. X XTo {\em set point}, click the left button in the buffer area of an XEmacs window. The {\em buffer area} consists of the full window except the Xleftmost column or scroll bar and the lowest line or mode line. The Xmode line is always displayed in inverse video, while the scroll bar Xis separated from the remaining part of the window by a vertical line. X XSetting point in a window automatically selects it, i.e. makes it the current Xwindow. X XIn order to {\em select} a portion of text for deletion, copy or move, Xpress the right mouse button (or {\em selection button}) at the Xbeginning of the text and release the button on the end of the text. XThe selected text is displayed in inverse video. The operation Xperformed on the selected text portion depends on which other buttons Xwere pressed during the selection. In any case the selected region is Xcopied to the kill ring. X XIf no other button is pressed during the selection, the result is a Xplain selection. Pressing the middle button (the {\em copy button}) Xduring the selection marks it a {\em copy selection}, pressing the Xleft button (the {\em delete button}) produces a {\em delete Xselection} and pressing both the copy and delete buttons a {\em move Xselection} (move = copy + delete!). X XWhen the selection is completed, i.e. when all buttons have been released, the Xoperation corresponding to the selection kind is performed. For a delete Xselection this means the selected text is killed and point is set to the place Xof the deletion. A copy selection inserts the selected text at the location of Xpoint, moving point after the newly inserted text. Move works like copy except Xthat the selected text is removed from its original place. X XIf a plain selection is {\em empty} (i.e. starts and ends at the same Xposition) the {\em mark} is set to that position. This allows setting Xthe mark to a character position by simply pointing to the character Xwith the right mouse button (similiar to setting point). Empty copy, Xdelete or move selections are no-ops, i.e. perform no operation. X XA non-empty plain selection does not modify the positions of point and mark. XLike other kinds of selections it has the side effect of copying the selected Xtext to the kill ring, as mentioned earlier. X XText needs not to be selected in the currently active Xwindow. This allows to move or copy a portion of text from one window Xto another, even from one buffer to another. Moreover, the Xselection may span two windows, provided the windows abut and display Xthe same buffer. This allows to select large portions of text Xwhich don't fit into a window. X XThe selected region is highlighted according to the operation to be Xperformed on it. While the contents of a plain selection are uniformly Xdisplayed in inverse video, a copy selection has a {\em ragged top}, a Xdelete selection a {\em ragged bottom}, and a move selection has both. X XSelection kinds can be changed arbirtarily as long as the selection Xprocess is not finished. For example, releasing the copy button in a Xcopy selection changes nothing, pressing it again transforms the Xselection kind back to plain. Similiar transformations are possible Xby pressing and releasing other buttons. To get used to the Xmechanism, play a bit with the mouse while keeping the selection Xbutton pressed and observe the highlighting. X XPreviously selected text can be {\em yanked} at the location of point Xpressing the middle button. Nothing happens if the mouse click did not Xoccur in the active window. X XNote that the current version does not support selections in the Xminibuffer. That is somewhat unelegant and inconsistent but should not Xrepresent a practical problem. X X\subsection{Scrolling and flipping} X XThe text displayed in an Emacs window can be scrolled up, scrolled Xdown and flipped with mouse commands. X XTo {\em scroll} in a window, position the mouse in the {\em scroll Xbar}, i.e. in the leftmost column of the screen, separated by a Xvertical line. If the left button is clicked, the line which the mouse Xpoints to is moved to the top of the window. Analogously, clicking the Xright mouse button in the scroll bar moves the line where the mouse Xwas clicked to the bottom of the screen. A previous part of the buffer Xis shown in the window. X XTo display an arbitrary part of the buffer in a window, click the left Xbutton in its {\em mode line}, i.e. its lowest, inverted line. Clicking in the Xmiddle of the mode line displays the central part of the buffer, Xclicking its rightmost column displays the end of the buffer etc. See Xappendix B if you prefer the {\em flipping} operation to be bound to Xsome button(s) in the scroll bar. X XSome editors, like {\em Sam}, let the operation scroll down be the perfect Xinverse of scroll up. The number of lines scrolled down equals the Xnumber of lines between the mouse position and the top (rather than Xbottom) of the window. If your previously favorite editor scrolls in Xthis way, set the Emacs variable {\tt 5620-symmetric-scrolling} to {\tt t} X(see appendix B). If you really can't live without {\em Sam}'s scrolling Xmechanism call the function {\tt 5620-set-sam-scrolling}. This will bind the Xbuttons in the scroll bar the way {\em Sam} does: right for scrolling up, Xleft for scrolling down (symmetrically) and middle for flipping. X X\subsection{Creating, deleting activating and resizing windows} X XEmacs windows can be splitted, resized, activated and deleted with the Xmouse. X XTo {\em split} a window click the middle button in the window's scroll Xbar. The window will be divided into two, with the upper one's mode Xline lying where the mouse was clicked. The splitting will be Xsuccessful only if the hight of both the resulting windows is greater Xor equal to the variable {\tt window-min-heigth} (which defaults to 4). X XWindows can be {\em resized} by moving their mode line. Position the Xmouse on the mode line, push the middle mouse button, move the mouse Xup or down and release the button. The mode line will be moved to the Xline where the mouse button was released, provided no window becomes Xto small. The bottommost mode line cannot be moved: the echo area Xcannot be resized this way. X XA window can be {\em selected} by clicking the left button in its {\em close box} X(i.e. the part of the scroll bar to the left of the mode line). While Xclicking in the buffer part of a window selects it and sets point Xwhere the mouse was clicked, a click in its close box Xactivates a window without modifying point. X XIf the minibuffer is active it can be selected by clicking the left Xbutton in the {\em echo close box} (the part of the scroll bar to the left of Xthe echo area). X XA window can be {\em deleted} by clicking both the left and right mouse Xbuttons (in either order) in its close box. The freed space is divided among the Xremaining windows. Note that the same buttons are used for deleting text. X X\subsection{Searching} X XMega-Lilith allows to {\em search forward or backward} for strings without Xtyping them in explicitly: clicking the left mouse button in the echo area of a Xwindow makes the editor search for the string at the top of the kill ring. XSince selected text is always copied to the kill Xring, another occurrence of a particular string can be found by first selecting Xit and then clicking the left button in the echo area. Clicking the middle Xbutton works the same, except that the search goes backwards. Clicking again Xsearches for the next occurence of the same string. This even works across Xwindows: the search can be performed in a window different from the one where Xthe string had been selected. X X\subsection{User taylorability and extendability} X XMega-Lilith allows for easy rebinding of mouse buttons (or combinations of Xbuttons) to commands. Programmers familiar with Emacs Lisp can define new Xcommands and bind these to mouse buttons in addition to (or in place of) the Xexisting commands. X XCommands can be bound to buttons with the function {\tt X5620-bind-click}, which takes as parameters the name of the button(s), Xe.g. {\tt left} or {\tt left-middle-right} (in that order!), the site, Xe.g. {\tt scroll-bar}, {\tt mode-line}, {\tt buffer-area}, and the Xname of the operation to be performed, e.g. {\tt 5620-scroll-up}, {\tt X5620-scroll-down}. In order to disable an operation previously Xenabled, bind the corresponding button(s) to nil. See appendix B for Xthe names of the sites, buttons and operations and appendix A for the Xdefault bindings. {\tt 5620-bind-click} can be called either from your X{\tt .emacs} file or interactively. X XThe function {\tt 5620-bind-selection-buttons} allows to define the Xbuttons for the selection operations. It takes as arguments the Xselection button and the copy button. Both must be a single button; X{\tt middle-left} for example is not a valid selection button. XMoreover, for obvious reasons the selection button and the copy button Xmust be different. The delete button is always set to the button which Xis neither selection nor copy. X XThe selection button all by itself must not be bound to a Xcommand with {\tt 5620-bind-\-click}, since every time the selection button is Xthe first button pressed, a selection, not a mouse click, occurs. XHowever, you may bind the selection button together with other buttons Xto an operation. For example, the call {\tt (5620-bind-click 'middle-right X'buffer-area 'set-point)} is legal even if the select button is set to X{\tt right}. If the selection button is pressed first, the selection is Xtreated; if the middle button is pressed first, the set-point command Xis executed. Note that in the scroll bar no selections occur; hence the Xscroll button can freely be bound there. X X\section{The terminal emulator {\em 5620\_mouse}} X XIn order to use Mega-Lilith, a terminal must be available which provides the Xeditor with the required mouse information. To this end we wrote the X5620\_mouse terminal emulator for the Teletype DMD 5620 terminal. This emulator Xis written in {\em C} and cross-compiled on the Unix system into native code Xfor the DMD's processor. It is downloaded to the terminal when the user Xexecutes the shell script {\em 5620\_mouse}. This section describes the Xemulator from the user's point of view; for more detailed information, cf. the XUnix manual page in appendix C. X X\subsection{General features} X XA layer into which 5620\_mouse has been downloaded behaves in most respects Xlike a `plain' layer, i.e. one with no program downloaded into. Almost every Xprogram that runs as a process connected to a plain layer should be able to run Xin exactly the same way in connection with a 5620\_mouse layer. X XOutside any editor, the main difference between a plain layer and a 5620\_mouse Xlayer is an additional menu appearing when pressing the middle mouse button. It Xallows for two selections, one to toggle the layer display between {\em normal} Xand {\em inverse video}, another to {\em clone} a new layer. Cloning is like Xcreating a new layer; the only difference is that the cloned layer is itself a X5620\_mouse layer and hence behaves as if 5620\_mouse had been downloaded into Xit. X XCloning is also done automatically, immediately after 5620\_mouse has been Xdownloaded into a layer. To this end, an {\em init file} can be specified as Xfor the {\em layers} program. Contrary to layers, 5620\_mouse allows to specify Xwindow sizes in character units. This is convenient when defining a window of, Xsay, 80 columns width. Note, however, that the scrollbar reduces the usable Xwindow size by one; in order to have 80 columns available for editing, the layer Xwidth must be specified to be 81. X X\subsection{Display capabilities} X X5620\_mouse supports more advanced display capabilities than a plain layer (or Xa 5620 without layers). In particular the function {\em Define Scrolling XRegion} allows for faster scrolling in Emacs when several editor windows are Xopen. This is because Emacs can now scroll windows individually; this not only Xreduces the amount of screen updating to be performed by the terminal but also Xthe amount of data the editor has to send, which is particularly important Xwhen working over slow transmission lines. X X\subsection{Selections and mouse events} X XThe crucial difference between 5620\_mouse and a plain layer is the handling of Xmouse button clicks when editing. To this end the editor instructs the terminal Xto enter {\em open mode} and provide a scroll bar. The editor also informs the Xterminal about the user's choice of the select and copy buttons. X XWhenever the user presses the selection button (other than in the Xscroll bar) the terminal highlights the area of the screen which is textually in Xbetween the point where the selection button was pressed and the current mouse Xposition. If the user presses the copy or delete button during the selection, Xthe terminal changes the selection feedback to reflect the selection kind as Xdescribed in section 3. When the user finishes the selection by releasing all Xmouse buttons, the terminal reverts the displayed text back to normal video. It Xthen reports the start and end coordinates and the selection kind to the Xeditor. An illegal selection, like releasing the mouse button(s) outside the Xlayer, is reported as `invalid'. X XOther mouse clicks are reported immediately. The terminal communicates Xto the editor the place of the mouse event and the state of the mouse Xbuttons. The only feedback provided by the terminal in this case is a Xshaded cursor indicating the character the user points to. X X\subsection{Setting up the terminal environment} X XThe program {\em mtermcap} is a companion of {\em 5620\_mouse}, used to set up Xthe terminal environment for proper operation of all programs that make use of Xterminal capabilities. It is automatically executed when {\em 5620\_mouse} is Xloaded. The user is advised to run it whenever he/she performs a remote login Xor after resizing a layer. For details on how to run {\em mtermcap} cf. the XUnix manual page in appendix C. X X\begin{thebibliography}{Gut} X X\bibitem[Gut]{Gut}J\"urg Gutknecht. X{\em Concepts of the text editor Lara.} XComm. ACM, Vol.28, September 1985, pp.942--960 X X\end{thebibliography} X X\onecolumn X X\appendix X\section{Mega-Lilith quick reference} X X X\begin{center} X{ X\large X\vspace{5ex} X X{\Large \underline{Clicks}\\[5ex]} X\newlength{\maxcolwidth} X\settowidth{\maxcolwidth}{search backward} X\begin{tabular}{c|*{4}{p{\maxcolwidth}}}\\[0.5ex] X & left & middle & right & left-right \\[0.5ex] \hline X buffer area & set point & yank at point & \em{select} & \\[0.5ex] X mode line & flip & move border & & \\[0.5ex] X scroll bar & scroll up & split window & scroll down & \\[0.5ex] X close box & select window & & & delete window \\[0.5ex] X echo area & search forward & search backward & & \\[0.5ex] X echo close box & select minibuf & & & \\ X\end{tabular} X X\vspace{10ex} X X{\Large \underline{Selections}\\[5ex]} X X\begin{tabular}{c|cccc} X button(s) & right & middle-right & left-right & left-middle-right \\[0.5ex] X \hline X operation & select & copy & delete & move X\end{tabular} X X\vspace{10ex} X} X\end{center} X X X\section{Predefined constants, variables and functions} X X\subsection{Variables to be set by the user} X X\newcommand{\fn}[1]{{\tt`#1'}\\} X X\newcommand{\fnempty}[1]{{\tt`#1'}} X X\newenvironment{flist}{\begin{list}{} X{\setlength{\leftmargin}{2.5cm} \setlength{\itemindent}{-2.5cm}}}% X{\end{list}} X X X\begin{flist} X X\item \fn{5620-symmetric-scrolling} X*if nil, the command 5620-scroll-down is the perfect inverse of X5620-scroll-up; otherwise the line pointed to is scrolled to the bottom X\end{flist} X X\subsection{Functions to change default bindings} X X\begin{flist} X X\item \fn{5620-bind-click} XBinds BUTTONS clicked in SITE to the function COMMAND X X\item \fn{5620-bind-selection-buttons} Xsets the SELECT-BUTTON and COPY-BUTTON; these must be different and Xand either left, middle or right; the delete button is set implicitely Xto the remaining button X X\item \fn{5620-set-sam-scrolling} Xbinds the buttons in the scroll-bar the same way the editor sam does: Xthe right mouse button scrolls up, the left button scrolls down and the middle Xbutton splits the window. The variable X5620-symmetric-scrolling is set to nil. Hence scroll-down scrolls the pointed Xline to the bottom of the window. X X\item \fn{5620-set-lilith-scrolling} XBinds the buttons in the scroll-bar as they are bound per default: Xthe left mouse button scrolls up, the right button scrolls down and the middle Xbutton flips to the appropriate part of text. The variable X5620-symmetric-scrolling is set to t. Moreover, the split operation Xis bound to the buttons left-middle, which is not a sam feature. X X\end{flist} X X\subsection{Predefined button names} X X\begin{flist} X X\item \fnempty{left} X X\item \fnempty{middle} X X\item \fnempty{right} X X\item \fnempty{left-middle} X X\item \fnempty{left-right} X X\item \fnempty{middle-right} X X\item \fnempty{left-middle-right} X X\end{flist} X X\subsection{Predefined site names} X X\begin{flist} X X\item \fn{buffer-area} X*the whole Emacs window except its mode line and scroll bar X X\item \fn{close-box} X*the part of the scroll bar to the left of the mode line X X\item \fn{echo-area} X*the bottom line of the screen or layer X X\item \fn{echo-close-box} X*the part of the scroll bar to the left of the echo area X X\item \fn{mode-line} X*the lowest line of an Emacs window (inverted) X X\item \fn{scroll-bar} X*the leftmost column of the screen (empty and separated by a vertical line) X X\end{flist} X X\subsection{Predefined functions to be bound to mouse events} X X\begin{flist} X X\item \fn{5620-delete-window} Xkills the window clicked with the mouse X X\item \fn{5620-find-backward} XSearches backward for the string in the kill ring X X\item \fn{5620-flip-from-column} Xdisplays in the window the part of the buffer corresponding to the ratio Xbetween the column where the mouse was clicked and the number of columns in Xthe window. Flipping in the first column displays the top of the buffer, flipping Xin the central column the central part of the buffer etc X X\item \fn{5620-flip-from-line} Xdisplays in the window the part of the buffer corresponding to the ratio Xbetween the window line where the mouse was clicked and the number of lines of Xthe screen. Flipping in the top line displays the top of the buffer, fliping Xin the middle of the window the middle part of the buffer etc. XNote: 5620-flip-from-column will usually permit a more precise flipping, since Xmost windows have much more columns than lines X X\item \fn{5620-move-border} Xmoves the border on which the mouse was pressed to the line the mouse Xwas released. Allows to enlarge a window at its neighbour's expense. XThe neighbour window can't be killed with 5620-move-border. The echo area Xcan't be resized with 5620-move-border. X X\item \fn{5620-scroll-up} Xscrolls the line in which the mouse was clicked to the top of its window X X\item \fn{5620-scroll-down} Xscrolls down in the window clicked with the mouse; the semantics depends X on the value of the variable 5620-scroll-line-to-bottom X X\item \fn{5620-select-minibuffer} XSelects the minibuffer if it is active X X\item \fn{5620-select-window} Xselects the window in which the mouse was clicked X X\item \fn{5620-set-point} Xselects the window in which the mouse was clicked and sets point X X\item \fn{5620-split-window} Xsplits the window in which the mouse was clicked at the line of the click X X\item \fn{5620-redraw-display} Xrefreshes the screen after having asked how big the layer currently is X X\item \fn{5620-yank} Xinserts the top of the kill ring at point X X\end{flist} X X\end{document} END_OF_FILE if test 26703 -ne `wc -c <'emacs/mega-lilith.tex'`; then echo shar: \"'emacs/mega-lilith.tex'\" unpacked with wrong size! fi chmod +x 'emacs/mega-lilith.tex' # end of 'emacs/mega-lilith.tex' fi echo shar: End of archive 3 \(of 3\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Gernot Heiser <heiser@iis.UUCP> Phone: +41 1/256 23 48 Integrated Systems Laboratory CSNET/ARPA: heiser%ifi.ethz.ch@relay.cs.net ETH Zuerich EARN/BITNET: GRIDFILE@CZHETH5A CH-8092 Zuerich, Switzerland EUNET/UUCP: {uunet,mcvax,...}!iis!heiser