fix: main loop never closed, disconnect-daemon ran every iteration

The main loop's closing paren was missing — (sleep 0.1)) only closed
sleep and the minibuffer let, but NOT the loop itself. The next form
(progn (disconnect-daemon)) was INSIDE the loop body, called on every
iteration. On first call it added '* Disconnected *' and cleared the
daemon stream, making the TUI permanently disconnected.

Fix: add ) to close the loop. Also:
- Connect-daemon runs synchronously BEFORE with-terminal (3 ports, 6s
  max). If daemon is already running, the TUI starts connected.
- If sync connect fails, background thread retries every 5 seconds.
- start-daemon in background (no blocking wait for daemon startup),
  so TUI appears immediately.
This commit is contained in:
2026-05-15 12:08:40 -04:00
parent bd1e8a92be
commit df33e8d6db
2 changed files with 27 additions and 19 deletions

View File

@@ -757,23 +757,18 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
** Connection ** Connection
#+BEGIN_SRC lisp :tangle /home/user/.local/share/passepartout/lisp/channel-tui-main.lisp #+BEGIN_SRC lisp :tangle /home/user/.local/share/passepartout/lisp/channel-tui-main.lisp
(defun connect-daemon (&optional (host "127.0.0.1") (start-port 9105) (end-port 9115)) (defun connect-daemon (&optional (host "127.0.0.1") (start-port 9105) (end-port 9115))
"Connect to daemon, trying ports START-PORT to END-PORT." "Try to connect to daemon once across START-PORT to END-PORT.
(add-msg :system (format nil "* Connecting to daemon... *")) Returns T on success, nil on failure. Does NOT wait or retry."
(loop for port from start-port to end-port (loop for port from start-port to end-port
do (handler-case do (handler-case
(let ((s (usocket:socket-connect host port :timeout 2))) (let ((s (usocket:socket-connect host port :timeout 2)))
(setf (st :stream) (usocket:socket-stream s) (setf (st :stream) (usocket:socket-stream s)
(st :connected) t) (st :connected) t)
(add-msg :system (format nil "* Connected to daemon on port ~d *" port)) (bt:make-thread (lambda () (reader-loop (st :stream)))
(bt:make-thread (lambda () (reader-loop (st :stream))) :name "tui-reader")
:name "tui-reader") (return-from connect-daemon t))
(return-from connect-daemon t)) (usocket:connection-refused-error () nil)
(usocket:connection-refused-error () (error (c) nil)))
(when (= port end-port)
(add-msg :system (format nil "* No daemon on ports ~d-~d *" start-port end-port))))
(error (c)
(when (= port end-port)
(add-msg :system (format nil "* No daemon on ports ~d-~d (error: ~a) *" start-port end-port c))))))
nil) nil)
(defun disconnect-daemon () (defun disconnect-daemon ()
@@ -884,7 +879,10 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
(parse-integer (uiop:getenv "TUI_SWANK_PORT"))) (parse-integer (uiop:getenv "TUI_SWANK_PORT")))
4006))) 4006)))
(setf (st :dirty) (list t t t)) (setf (st :dirty) (list t t t))
(connect-daemon) ;; Quick sync connect attempt (just 3 ports, 6s max)
(let ((connected (connect-daemon "127.0.0.1" 9105 9107)))
(unless connected
(add-msg :system "* Daemon not found — will retry in background... *")))
(when (> swank-port 0) (when (> swank-port 0)
(handler-case (handler-case
(progn (progn
@@ -902,10 +900,19 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
(add-msg :system (format nil "* ~a backend ~dx~d *" (add-msg :system (format nil "* ~a backend ~dx~d *"
(if (typep be 'cl-tty.backend:modern-backend) "modern" "simple") (if (typep be 'cl-tty.backend:modern-backend) "modern" "simple")
w h)) w h))
;; Initial dirty all to trigger first redraw in loop ;; Initial dirty all to trigger first redraw in loop
(setq w (or (and (numberp w) (> w 0) w) 80) (setq w (or (and (numberp w) (> w 0) w) 80)
h (or (and (numberp h) (> h 0) h) 24)) h (or (and (numberp h) (> h 0) h) 24))
(loop while (st :running) do ;; Retry daemon connection in background if sync attempt failed
(unless (st :connected)
(add-msg :system "* Connecting to daemon... *")
(bt:make-thread
(lambda ()
(loop while (and (st :running) (not (st :connected)))
do (connect-daemon)
(unless (st :connected) (sleep 5))))
:name "daemon-auto-connect"))
(loop while (st :running) do
(dolist (ev (drain-queue)) (dolist (ev (drain-queue))
(cond (cond
((eq (getf ev :type) :daemon) ((eq (getf ev :type) :daemon)
@@ -1077,8 +1084,8 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
(format nil "> ~a" (or filter "")) (format nil "> ~a" (or filter ""))
(theme-color :input-prompt) nil)) (theme-color :input-prompt) nil))
(cl-tty.backend:end-sync be)) (cl-tty.backend:end-sync be))
(sleep 0.1)) (sleep 0.1)))
(progn (disconnect-daemon)))))) (progn (disconnect-daemon)))))
#+END_SRC #+END_SRC
* Test Suite * Test Suite

View File

@@ -378,8 +378,9 @@ case "$COMMAND" in
check_dependencies check_dependencies
export PASSEPARTOUT_DATA_DIR="${PASSEPARTOUT_DATA_DIR:-$SCRIPT_DIR}" export PASSEPARTOUT_DATA_DIR="${PASSEPARTOUT_DATA_DIR:-$SCRIPT_DIR}"
if ! ss -tln 2>/dev/null | grep -q 9105 && ! netstat -tln 2>/dev/null | grep -q 9105; then if ! ss -tln 2>/dev/null | grep -q 9105 && ! netstat -tln 2>/dev/null | grep -q 9105; then
echo "Starting daemon first..." echo "Starting daemon..."
$0 daemon # Start daemon in background — don't block TUI startup
$0 daemon &>/dev/null &
fi fi
# Build TUI load script with proper paths # Build TUI load script with proper paths
cat > /tmp/tui-load.lisp << LISPEOF cat > /tmp/tui-load.lisp << LISPEOF