- (remove-fd-handler handler))))))
- (setq *periodic-polling-function* #'main-iterate-all)
- (setq *max-event-to-sec* 0)
- (setq *max-event-to-usec* *event-poll-interval*))
- #+(and clisp readline)
- ;; Readline will call the event hook at most ten times per second
- (setf readline:event-hook #'main-iterate-all)
- #+clisp
- ;; When running in Slime we need to hook into the Swank server
- ;; to handle events asynchronously
- (if (find-package "SWANK")
- (let ((read-from-emacs (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK")))
- (stream (funcall (find-symbol "CONNECTION.SOCKET-IO" "SWANK") (symbol-value (find-symbol "*EMACS-CONNECTION*" "SWANK")))))
- (setf (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK"))
- #'(lambda ()
- (loop
- (case (socket:socket-status (cons stream :input) 0 *event-poll-interval*)
- (:input (return (funcall read-from-emacs)))
- (:eof (read-char stream))
- (otherwise (main-iterate-all)))))))
- #-readline(warn "Not running in Slime and Readline support is missing, so the Gtk main loop has to be invoked explicit."))
-
- (gdk:display-open display)))
-
-
-
-#+sbcl
-(defun clg-init-with-threading (&optional display)
- "Initializes the system and starts event handling"
- (unless (gdk:display-get-default)
- #?(pkg-exists-p "gtk+-2.0" :atleast-version "2.8.0")
- (progn
- #+sbcl(sb-int:set-floating-point-modes :traps nil)
- #+cmu(ext:set-floating-point-modes :traps nil))
+ (remove-fd-handler handler))))))))
+
+ #?(or (featurep :cmu) (sbcl< 1 0 6) (sbcl>= 1 0 15 6))
+ (progn
+ (setq *periodic-polling-function* #'main-iterate-all)
+ #?(or (featurep :cmu) (sbcl< 1 0 6))
+ (multiple-value-setq (*max-event-to-sec* *max-event-to-usec*)
+ (decompose-time *event-polling-interval*))
+ #?(sbcl>= 1 0 15 6)
+ (setq *periodic-polling-period* *event-polling-interval*))
+
+ #?(or (featurep :clisp) (and (sbcl>= 1 0 6) (sbcl< 1 0 15 6)))
+ ;; When running in CLISP or certain versions of SBCL in Slime we need
+ ;; to hook into the Swank server to handle events asynchronously.
+ (cond
+ ((and (find-package "SWANK") (find-symbol "CHECK-SLIME-INTERRUPTS" "SWANK"))
+ (let ((check-slime-interrupts
+ (symbol-function (find-symbol "CHECK-SLIME-INTERRUPTS" "SWANK"))))
+ (setf
+ (symbol-function (find-symbol "CHECK-SLIME-INTERRUPTS" "SWANK"))
+ #'(lambda ()
+ (main-iterate-all)
+ (funcall check-slime-interrupts)))))
+ ((and (find-package "SWANK")
+ (find-symbol "READ-FROM-EMACS" "SWANK")
+ (find-symbol "*EMACS-CONNECTION*" "SWANK")
+ (find-symbol "CONNECTION.SOCKET-IO" "SWANK"))
+ (let ((connection (symbol-value (find-symbol "*EMACS-CONNECTION*" "SWANK"))))
+ (when connection
+ (let ((read-from-emacs (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK")))
+ (stream (funcall (find-symbol "CONNECTION.SOCKET-IO" "SWANK") connection)))
+ (multiple-value-bind (sec usec)
+ (decompose-time *event-polling-interval*)
+ (setf (symbol-function (find-symbol "READ-FROM-EMACS" "SWANK"))
+ #'(lambda ()
+ (loop
+ (case (socket-status (cons stream :input) sec usec)
+ ((:input :eof) (return (funcall read-from-emacs)))
+ (otherwise (main-iterate-all)))))))))))
+ ((flet ((warn-main-loop ()
+ (warn "Asynchronous event handling not supported on this platform. An explicit main loop has to be started.")))
+ #+(and clisp readline)
+ (if (find-package "SWANK")
+ (warn-main-loop) ; assuming we're running in SLIME
+ ;; Readline will call the event hook at most ten times per second
+ (setf readline:event-hook #'main-iterate-all))
+ #-(and clisp readline)(warn-main-loop))))
+
+ (gdk:display-open display))
+
+#+sb-thread
+(progn
+ (defvar *main-thread* nil)
+
+ ;; Hopefully, when threading support is added to the Win32 port of
+ ;; SBCL in the future, this will work just out of the box.
+ #+win32
+ (let ((done (sb-thread:make-waitqueue))
+ (functions ())
+ (results ()))
+
+ ;; In Win32 all GDK calls have to be made from the main loop
+ ;; thread, so we add a timeout function which will poll for code and
+ ;; execute it.
+
+ (defun funcall-in-main (function)
+ (if (or
+ (not *main-thread*)
+ (eq sb-thread:*current-thread* *main-thread*))
+ (funcall function)
+ (gdk:with-global-lock
+ (push function functions)
+ (sb-thread:condition-wait done gdk:*global-lock*)
+ (pop results))))
+
+ ;; Will lock REPL on error, need to be fixed!
+ (defun %funcall-in-main-poll ()
+ (when functions
+ (loop
+ for n from 0
+ while functions
+ do (push (funcall (pop functions)) results)
+ finally (sb-thread:condition-notify done n)))
+ t))
+
+ (defmacro within-main-loop (&body body)
+ #-win32 `(gdk:with-global-lock ,@body)
+ #+win32 `(funcall-in-main #'(lambda () ,@body)))
+
+ (defun %init-multi-threaded-event-handling (display)
+ (when (and
+ (find-package "SWANK")
+ (not (eq (symbol-value (find-symbol "*COMMUNICATION-STYLE*" "SWANK")) :spawn)))
+ (error "When running clg in Slime, the communication style :spawn must be used in combination with multi threaded event handling. See the README file and <http://common-lisp.net/project/slime/doc/html/slime_45.html> for more information."))
+ (gdk:threads-init)
+ (let ((main-running (sb-thread:make-waitqueue)))
+ (gdk:with-global-lock
+ (setf *main-thread*
+ (sb-thread:make-thread
+ #'(lambda ()
+ (gdk:with-global-lock
+ (gdk:display-open display)
+ #+win32(gdk:timeout-add-with-lock (/ *event-poll-interval* 1000)
+ #'%funcall-in-main-poll)
+ (sb-thread:condition-notify main-running)
+ (main)))
+ :name "gtk event loop"))
+ (sb-thread:condition-wait main-running gdk:*global-lock*)))
+
+ ;; We need to hook into the Swank server to protect calls to GDK properly.
+ ;; This will *only* protect code entered directly in the REPL.
+ (when (find-package "SWANK")
+ (let ((repl-eval-hook (find-symbol "*SLIME-REPL-EVAL-HOOKS*" "SWANK")))
+ (if repl-eval-hook
+ (push #'(lambda (form)
+ (within-main-loop (eval form)))
+ (symbol-value (find-symbol "*SLIME-REPL-EVAL-HOOKS*" "SWANK")))
+ (warn "Your version of Slime does not have *SLIME-REPL-EVAL-HOOKS* so all calls to Gtk+ functions have to be explicit protected by wrapping them in a WITHIN-MAIN-LOOP form"))))))
+
+#-sb-thread
+(defmacro within-main-loop (&body body)
+ `(progn ,@body))