(in-package :cl-smoke.repl) (defvar *qt-eval*) (defclass qt-eval (qt:object) ((eval-slot :initform (qt:make-slot #'(lambda (form) (setf (result *qt-eval*) (multiple-value-list (eval form)) (new-package *qt-eval*) *package*)) '(t)) :reader eval-slot) (eval-signal :initform (qt:make-signal t) :reader eval-signal :documentation "Send an form to evaluate.") (result :accessor result) (package :accessor new-package :initarg :package)) (:metaclass cxx:class)) (defmethod initialize-instance :after ((qt-eval qt-eval) &key &allow-other-keys) (qt:connect (eval-signal qt-eval) (eval-slot qt-eval) qt:+blocking-queued-connection+)) (defun qt-eval (form) "Returns the result of FORM evaluated in the event loop thread." (funcall (eval-signal *qt-eval*) form) (setf *package* (new-package *qt-eval*)) (values-list (result *qt-eval*))) (let ((eval-region-orig)) (defun end-event-loop () #+slime (progn (assert eval-region-orig) (setf (fdefinition 'swank::eval-region) eval-region-orig) (setf eval-region-orig nil)) (cxx:quit (qt:app))) (defun start-event-loop-in-background () "Starts the QApplication event loop in a new thread and sends the forms entered at the REPL to this thread to be evaluated." ;; Like eval-region of Slimes swank.lisp #+slime (progn (assert (null eval-region-orig)) (setf eval-region-orig #'swank::eval-region) (setf (fdefinition 'swank::eval-region) #'(lambda (string) (with-input-from-string (stream string) (let (- values) (loop (let ((form (read stream nil stream))) (when (eq form stream) (fresh-line) (finish-output) (return (values values -))) (setq - form) (setq values (multiple-value-list (qt-eval form))) (finish-output)))))))) (let ((standard-input *standard-input*) (standard-output *standard-output*) (debug-io *debug-io*) (trace-output *trace-output*) (error-output *error-output*) (query-io *query-io*) (terminal-io *terminal-io*) (ready-lock (bt:make-lock)) (ready (bt:make-condition-variable))) (bt:make-thread #'(lambda () #+slime (setf *standard-input* standard-input *standard-output* standard-output *debug-io* debug-io *trace-output* trace-output *error-output* error-output *query-io* query-io *terminal-io* terminal-io) (qt:with-app () (qt:application.set-quit-on-last-window-closed nil) (setf *qt-eval* (make-instance 'qt-eval :package *package*)) (qt:do-delayed-initialize (bt:condition-notify ready)) (qt:connect (qt:get-signal (qt:app) "aboutToQuit()") #'(lambda () (setf *qt-eval* nil))) (qt:exec))) :name "Qt Event Loop") (bt:with-lock-held (ready-lock) (bt:condition-wait ready ready-lock))))) #+slime (defun start-event-loop-in-repl () "Start the QApplication event loop in the current REPL thread. When idle the event loop updates the REPL. While the idea is fine, it is a hack and the displaying of the argument list in SLIME is fluky." (funcall swank::*send-repl-results-function* '(nil)) (qt:with-app () (qt:application.set-quit-on-last-window-closed nil) (let ((idle (make-instance 'qt:timer))) (qt:connect (qt:get-signal idle "timeout()") #'(lambda () (swank::process-requests t) ;; it's a wonder this even works! (sb-impl::serve-event 0.1))) (cxx:start idle) (qt:exec) (format *debug-io* "New-repl end~%"))))