;;; Ken Meltsner's patches for Garnet/Lucid LISP to allow for safe ;;; multiprocessing. ;;; ;;; Contact: meltsner@chipman.crd.ge.com ;;; A process is created at initialization time. Call with-pause to do ;;; something with the main-event-loop process paused. ;;; Based, in large part, on Rod William's multiprocessing modifications ;;; to Garnet. ;;; Added a safe event loop pause function, temporary suspension of other ;;; processes to fix errors in the Garnet loop, checks when killing processes ;;; to make sure process isn't already dead, etc. Broke out code ;;; from Garnet files to make it easy to install. ;;; File replaces functions from OPAL and INTERACTORS package (in-package "OPAL") (proclaim '(optimize (compilation-speed 0))) ;bug in Lucid/CLX, fixed in 4.4 ;;; William's mods for multiple process for Garnet #+lcl3.0 (defvar *event-loop-display-list* nil) #+lcl3.0 (defun enable-event-loop-process (display) (let ((process (lcl:make-process :name "Garnet Event Loop" :priority 200 :stack-size 5000 :function #'default-event-handler :args (list display)))) (setq *event-loop-display-list* (push (cons display process) *event-loop-display-list*)))) (defun Disconnect-Garnet () (setq *all-the-windows* (remove-if ; This should remove #'(lambda (w) ; just opal:window and (kr:get-local-value w :update)) ; inter:interactor-window (all-the-instances opal:window))) ; from list of all windows (setq *all-windows-which-have-been-closed* nil) ;;; Make all the windows invisible. (dolist (w *all-the-windows*) (when (kr:g-value w :visible) (push w *all-windows-which-have-been-closed*) (kr:s-value w :visible nil) (funcall (kr:g-value w :update) w))) ; generalized update ;;; Remove all connections to X from the text objects, ;;; (even those hidden in the :update-slots-values slot!) (dolist (txt (all-the-instances opal:text)) (when (kr:g-cached-value txt :xfont) (xlib:close-font (kr:g-cached-value txt :xfont)) (kr:s-value txt :xfont nil)) (when (kr:g-cached-value txt :update-slots-values) (setf (aref (kr:g-cached-value txt :update-slots-values) opal::*text-xfont*) nil))) (dolist (fnt (all-the-instances opal:font-from-file)) (kr:s-value fnt :display-xfont-plist nil)) ;;; Remove all connections to X from the window objects. (setf opal::*display-name-to-display-mapping* nil) (dolist (w *all-the-windows*) (kr:s-value w :drawable nil) (kr:s-value w :event-mask nil) (let ((d (kr:g-cached-value w :display-info))) (when d (kr:s-value w :display-info nil) #+cmu (ext:disable-clx-event-handling (opal::display-info-display d)) #+lcl3.0 (disable-event-loop-process (opal::display-info-display d)) ))) ) #+lcl3.0 (defun disable-event-loop-process (display) (let ((display-process (assoc display *event-loop-display-list*))) (when (lcl:processp (cdr display-process)) (lcl:kill-process (cdr display-process))) (setq *event-loop-display-list* (remove display-process *event-loop-display-list*)))) (defun initialize-display (display-name) (let ((display (cdr (assoc display-name *display-name-to-display-mapping* :test '(lambda (s1 s2) (let ((end (min (length s1) (length s2)))) (string-equal s1 s2 :end1 end))))))) (when (null display) (let* ((x-display (xlib:open-display display-name)) (x-screen (nth user::Garnet-Screen-Number (xlib:display-roots x-display))) (x-root (xlib:screen-root x-screen)) (x-line-style-gc (xlib:create-gcontext :drawable x-root :function 2 :foreground (g-value opal:black :colormap-index) :background (g-value opal:white :colormap-index) :line-width 0 :line-style :solid :cap-style :butt :join-style :miter :fill-style :solid :fill-rule :even-odd)) (x-filling-style-gc (xlib:create-gcontext :drawable x-root :function 2 :foreground (g-value opal:black :colormap-index) :background (g-value opal:white :colormap-index) :line-width 0 :line-style :solid :cap-style :butt :join-style :miter :fill-style :solid :fill-rule :even-odd)) (opal-line-style-gc (make-opal-gc :gcontext x-line-style-gc :opal-style NIL :function 2 :line-width 0 :line-style :solid :cap-style :butt :join-style :miter :dashes NIL :font NIL :fill-style :solid :fill-rule :even-odd :stipple NIL :clip-mask :none)) (opal-filling-style-gc (make-opal-gc :gcontext x-filling-style-gc :opal-style NIL :function 2 :line-width 0 :line-style :solid :cap-style :butt :join-style :miter :dashes NIL :font NIL :fill-style :solid :fill-rule :even-odd :stipple NIL :clip-mask :none))) (setf display (make-display-info :display x-display :screen x-screen :root-window x-root :line-style-gc opal-line-style-gc :filling-style-gc opal-filling-style-gc)) #+cmu (ext:enable-clx-event-handling x-display 'default-event-handler) #+lcl3.0 (enable-event-loop-process x-display) ) (push (cons display-name display) *display-name-to-display-mapping*)) display)) (defun create-x-drawable (a-window) (let* ((display-info (initialize-display (g-value a-window :display))) (title-name (g-value a-window :title)) (left (g-value a-window :left)) (top (g-value a-window :top)) (border-width (g-value a-window :border-width)) (width (g-value a-window :width)) (height (g-value a-window :height)) (parent (get-parent-win a-window display-info)) (screen (display-info-screen display-info)) (white-pixel (xlib:screen-white-pixel screen)) (black-pixel (xlib:screen-black-pixel screen)) (drawable (xlib:create-window :parent parent :x left :y top :width width :height height :background white-pixel :border-width border-width :border black-pixel :override-redirect :off :event-mask *exposure-event-mask* :class :input-output))) (if (g-value a-window :position-by-hand) (xlib:set-standard-properties drawable :name title-name :icon-name (or (g-value a-window :icon-title) title-name) :resource-name "Garnet" :width width :height height :user-specified-position-p nil) (xlib:set-standard-properties drawable :name title-name :icon-name (or (g-value a-window :icon-title) title-name) :resource-name "Garnet" :width width :height height :x left :y top :user-specified-position-p t)) (setf (g-value a-window :drawable) drawable) (setf (g-value a-window :display-info) display-info) (setf (gethash drawable *drawable-to-window-mapping*) a-window) (when (g-value a-window :double-buffered-p) (let* ((buffer (create-x-buffer a-window)) (buffer-gc (xlib:create-gcontext :drawable buffer :foreground black-pixel :background white-pixel))) (s-value a-window :buffer buffer) (s-value a-window :buffer-gcontext buffer-gc) (clear-buffer buffer buffer-gc))) (s-value a-window :top-border-width border-width) (s-value a-window :left-border-width border-width) (s-value a-window :bottom-border-width border-width) (s-value a-window :right-border-width border-width) (setf *windows-that-have-never-been-updated* (delete a-window *windows-that-have-never-been-updated*)) ;; set the cursor to hemlock's cursor or specified cursor/mask combo ;; (cursor-file . mask-file) (set-window-cursor display-info drawable (g-value a-window :cursor)) ;;; WE NO LONGER PROPAGATE DOWN INTO OBJECTS!!! ;; if an aggregate is specified, propagate the drawable, and ;; display-info structures down through it ;;;(let ((a-aggregate (g-value a-window :aggregate))) ;;;(when a-aggregate ;;;(propagate-down a-aggregate drawable display-info))) ;; bring up the window, and display it (when (g-value a-window :visible) (xlib:map-window drawable) #-(or lcl3.0 cmu) (xlib:display-force-output (display-info-display display-info)) ;; Wait until map-notify actually takes place #-lcl3.0 (xlib:event-case ((display-info-display display-info) :discard-p nil :peek-p t :timeout 15) (:map-notify (event-window) (eq event-window drawable))) ) drawable)) (in-package "INTER") (defun handle-error-in-loop (condition) (lcl:using-initial-io (format t "~%Suspending other processes to fix Garnet error~%") (lcl:invoke-debugger condition))) (defvar *pause-loop-safely* nil) (defmacro with-pause (&body stuff) `(progn (setq *pause-loop-safely* t) (unwind-protect (progn ,@stuff) (setq *pause-loop-safely* nil)))) (defun pause-loop () (setq *pause-loop-safely* t)) (defun resume-loop () (setq *pause-loop-safely* nil)) (export '(*pause-loop-safely* with-pause pause-loop resume-loop)) (defun opal::default-event-handler (display) (loop (cond (*pause-loop-safely* (sys:process-wait-allowing-scheduling "Loading something big" #'(lambda () (null *pause-loop-safely*)) )) (t (opal::default-event-handler2 display))))) (defun opal::default-event-handler2 (display) "Event handler for the interactor windows" (lcl:handler-bind ((lcl::error #'inter::handle-error-in-loop)) (xlib:event-case (display :discard-p t) (:MAP-NOTIFY (event-window) (opal::Map-Notify (debug-p :event) event-window) ) (:UNMAP-NOTIFY (event-window) (opal::Unmap-Notify (debug-p :event) event-window) ) (:REPARENT-NOTIFY (event-window x y) (opal::Reparent-Notify (debug-p :event) event-window x y) ) (:CIRCULATE-NOTIFY () (opal::Circulate-Notify (debug-p :event)) ) (:GRAVITY-NOTIFY () (opal::Gravity-Notify (debug-p :event)) ) (:DESTROY-NOTIFY (event-window) (opal::Destroy-Notify (debug-p :event) event-window) ) (:CONFIGURE-NOTIFY (x y width height event-window above-sibling) (opal::Configure-Notify (debug-p :event) x y width height event-window above-sibling) ) (:EXPOSURE (event-window count x y width height) (opal::Exposure (debug-p :event) event-window count x y width height display) ) (:KEY-PRESS (event-window x y state code time) (if *trans-from-file* T ; ignore events when read transcript (Key-Press event-window x y state code time)) ) (:BUTTON-PRESS (event-window x y state code event-key time) (if *trans-from-file* T ; ignore events when read transcript (Button-Press event-window x y state code event-key time)) ) (:BUTTON-RELEASE (event-window x y state code event-key time) (if *trans-from-file* T ; ignore events when read transcript (Button-Release event-window x y state code event-key time)) ) (:MOTION-NOTIFY (event-window x y) (if *trans-from-file* T ; ignore events when read transcript (Motion-Notify event-window x y display)) ) (:NO-EXPOSURE () t ) (OTHERWISE () (format t "illegal event") t )))) (defun main-event-loop (&optional window) "Event handler for the interactor windows" #+(or lcl3.0 cmu) (declare (ignore window)) #+(or cmu lcl3.0) (format t "Calling main-event-loop in Lucid or CMU Common Lisp is not necessary") #-(or lcl3.0 cmu) (let ((display (if window (opal::display-info-display (g-value window :display-info)) (let ((win1 (caar (opal::get-table-contents)))) (if win1 (xlib:window-display win1) opal::*default-x-display*))))) ; Changed call to xlib:discard-current-event to xlib:event-case ; (because the former was having no effect in Lucid). -- Pervin 4/9/90 (unwind-protect (catch 'exit-main-loop-exception (opal::default-event-handler display))) (xlib:event-case (display :discard-p t :timeout 5) ; discard current event (otherwise () t))) ; which was the typing ) ; of the *garnet-break-key* (defun exit-main-event-loop () #-(or lcl3.0 cmu) (throw 'exit-main-loop-exception t))