Fix QTerminalWidget key interception and font size propagation

ober

bfe3fe0f127e7ab2c9ab98d19b0f783de735b92d

diff --git a/lib/jerboa-emacs/qt/app.sls b/lib/jerboa-emacs/qt/app.sls
index 44af117..40fb299 100644
--- a/lib/jerboa-emacs/qt/app.sls
+++ b/lib/jerboa-emacs/qt/app.sls
@@ -752,7 +752,8 @@
            "commands registered: "
            (number->string (hash-length *all-commands*))
            " total")
-         (let ([image-key-installed (make-hash-table-eq)])
+         (let ([image-key-installed (make-hash-table-eq)]
+               [terminal-key-installed (make-hash-table-eq)])
            (add-hook!
              'post-buffer-attach-hook
              (lambda (editor buf)
@@ -771,11 +772,15 @@
                             (let* ([container (qt-edit-window-container
                                                 win)]
                                    [count (qt-stacked-widget-count
-                                            container)])
+                                            container)]
+                                   [tw (qt-terminal-widget term)])
                               (qt-stacked-widget-set-current-index!
                                 container
                                 (- count 1))
-                              (qt-terminal-focus! term)))))]
+                              (unless (hash-get terminal-key-installed tw)
+                                ((app-state-key-handler app) tw)
+                                (hash-put! terminal-key-installed tw #t))
+                              (qt-widget-set-focus! tw)))))]
                      [(image-buffer? buf)
                       (qt-show-image-buffer! editor buf)
                       (let ([win (hash-get *editor-window-map* editor)])
diff --git a/lib/jerboa-emacs/qt/commands-config.sls b/lib/jerboa-emacs/qt/commands-config.sls
index 12334d2..c512798 100644
--- a/lib/jerboa-emacs/qt/commands-config.sls
+++ b/lib/jerboa-emacs/qt/commands-config.sls
@@ -19,10 +19,10 @@
    *profiler-data* cmd-profiler-start cmd-profiler-stop
    cmd-show-tab-count cmd-show-trailing-whitespace-count
    *SCI_SETCODEPAGE* *SC_CP_UTF8* qt-insert-prompt!
-   terminal-buffer-counter *terminal-widget-map* cmd-term
-   cmd-terminal-send cmd-term-interrupt cmd-term-send-eof
-   cmd-term-send-tab cmd-multi-vterm *terminal-copy-mode*
-   cmd-vterm-copy-mode cmd-vterm-copy-done get-terminal-buffers
+   terminal-buffer-counter cmd-term cmd-terminal-send
+   cmd-term-interrupt cmd-term-send-eof cmd-term-send-tab
+   cmd-multi-vterm *terminal-copy-mode* cmd-vterm-copy-mode
+   cmd-vterm-copy-done get-terminal-buffers
    qt-switch-to-terminal! cmd-term-list cmd-term-next
    cmd-term-prev cmd-ediff-files cmd-comment-dwim)
   (import
@@ -223,6 +223,13 @@
                    (loop (+ i 1))))
                (restore-margin-colors! ed)))
            (qt-frame-windows fr)))
+       (hash-for-each
+         (lambda (_buf term)
+           (qt-terminal-set-font!
+             term
+             *default-font-family*
+             *default-font-size*))
+         *terminal-widget-map*)
        (when *qt-app-ptr*
          (qt-app-set-style-sheet! *qt-app-ptr* (theme-stylesheet))))
   (def (cmd-set-frame-font app)
@@ -893,8 +900,6 @@
                    (sci-send ed SCI_SETSTYLING text-len style))
                  (loop (cdr segs) (+ pos text-len)))))))
   (define terminal-buffer-counter--cell (vector 0))
-  (define *terminal-widget-map*--cell
-    (vector (make-hash-table-eq)))
   (def (cmd-term app)
        "Open a QTerminalWidget-backed terminal buffer.\n   Uses libvterm for proper VT100 terminal emulation with full color support."
        (verbose-log! "cmd-term: begin (QTerminalWidget)")
@@ -933,6 +938,7 @@
                             (qt-terminal-widget term))])
                  (qt-stacked-widget-set-current-index! container idx))
                (hash-put! *terminal-widget-map* buf term)
+               ((app-state-key-handler app) (qt-terminal-widget term))
                (qt-terminal-spawn! term "")
                (qt-terminal-focus! term)
                (verbose-log! "cmd-term: QTerminalWidget spawned")
@@ -1410,13 +1416,6 @@
                        terminal-buffer-counter--cell
                        0
                        val)]))
-  (define-syntax *terminal-widget-map*
-    (identifier-syntax
-      [id (vector-ref *terminal-widget-map*--cell 0)]
-      [(set! id val) (vector-set!
-                       *terminal-widget-map*--cell
-                       0
-                       val)]))
   (define-syntax *terminal-copy-mode*
     (identifier-syntax
       [id (vector-ref *terminal-copy-mode*--cell 0)]
diff --git a/lib/jerboa-emacs/qt/commands-shell.sls b/lib/jerboa-emacs/qt/commands-shell.sls
index d86ef94..d2e0407 100644
--- a/lib/jerboa-emacs/qt/commands-shell.sls
+++ b/lib/jerboa-emacs/qt/commands-shell.sls
@@ -3,30 +3,31 @@
 ;;; Source: src/jerboa-emacs/qt/commands-shell.ss
 
 (library (jerboa-emacs qt commands-shell)
-  (export directory-exists? apply-font-size-to-all-editors!
-   cmd-increase-font-size cmd-decrease-font-size
-   cmd-reset-font-size cmd-goto-first-non-blank
-   cmd-goto-last-non-blank cmd-move-to-window-top
-   cmd-move-to-window-middle cmd-move-to-window-bottom
-   cmd-scroll-left cmd-scroll-right cmd-insert-let
-   cmd-insert-lambda cmd-insert-defun cmd-insert-cond
-   cmd-insert-when cmd-insert-unless cmd-insert-match
-   cmd-insert-import cmd-insert-export cmd-insert-include
-   cmd-insert-file-header cmd-insert-header-guard
-   cmd-insert-box-comment cmd-insert-file-contents
-   cmd-insert-register-string *auto-indent* *backup-files*
-   *version-control* *debug-mode* *debug-on-quit*
-   *visible-bell* *transient-mark* *electric-indent*
-   cmd-toggle-auto-indent cmd-toggle-backup-files
-   cmd-toggle-version-control cmd-toggle-debug-mode
-   cmd-toggle-debug-on-quit cmd-toggle-visible-bell
-   cmd-toggle-transient-mark cmd-toggle-electric-indent
-   cmd-toggle-auto-revert cmd-toggle-auto-revert-global
-   cmd-auto-revert-tail-mode *view-mode-buffers* cmd-view-mode
-   *so-long-threshold* *so-long-buffers* check-so-long!
-   cmd-so-long-mode *follow-mode* cmd-follow-mode
-   *command-history-file* savehist-save! savehist-load!
-   auto-fill-check! qt-aggressive-indent-line!
+  (export directory-exists? *terminal-widget-map*
+   apply-font-size-to-all-editors! cmd-increase-font-size
+   cmd-decrease-font-size cmd-reset-font-size
+   cmd-goto-first-non-blank cmd-goto-last-non-blank
+   cmd-move-to-window-top cmd-move-to-window-middle
+   cmd-move-to-window-bottom cmd-scroll-left cmd-scroll-right
+   cmd-insert-let cmd-insert-lambda cmd-insert-defun
+   cmd-insert-cond cmd-insert-when cmd-insert-unless
+   cmd-insert-match cmd-insert-import cmd-insert-export
+   cmd-insert-include cmd-insert-file-header
+   cmd-insert-header-guard cmd-insert-box-comment
+   cmd-insert-file-contents cmd-insert-register-string
+   *auto-indent* *backup-files* *version-control* *debug-mode*
+   *debug-on-quit* *visible-bell* *transient-mark*
+   *electric-indent* cmd-toggle-auto-indent
+   cmd-toggle-backup-files cmd-toggle-version-control
+   cmd-toggle-debug-mode cmd-toggle-debug-on-quit
+   cmd-toggle-visible-bell cmd-toggle-transient-mark
+   cmd-toggle-electric-indent cmd-toggle-auto-revert
+   cmd-toggle-auto-revert-global cmd-auto-revert-tail-mode
+   *view-mode-buffers* cmd-view-mode *so-long-threshold*
+   *so-long-buffers* check-so-long! cmd-so-long-mode
+   *follow-mode* cmd-follow-mode *command-history-file*
+   savehist-save! savehist-load! auto-fill-check!
+   qt-aggressive-indent-line!
    cmd-toggle-delete-trailing-whitespace-on-save
    uniquify-parent-suffix uniquify-buffer-name!
    cmd-recentf-open-files cmd-toggle-frame-fullscreen
@@ -115,6 +116,8 @@
    (jerboa runtime))
   (def (directory-exists? path)
        (and (file-exists? path) (file-directory? path)))
+  (define *terminal-widget-map*--cell
+    (vector (make-hash-table-eq)))
   (def (apply-font-size-to-all-editors! app)
        "Apply the current global font size to all open editors."
        (let ([fr (app-state-frame app)]
@@ -131,6 +134,13 @@
                (sci-send ed SCI_SETMARGINWIDTHN 0 margin-w)
                (qt-apply-editor-theme! ed)))
            (qt-frame-windows fr)))
+       (hash-for-each
+         (lambda (_buf term)
+           (qt-terminal-set-font!
+             term
+             *default-font-family*
+             *default-font-size*))
+         *terminal-widget-map*)
        (when *qt-app-ptr*
          (qt-app-set-style-sheet! *qt-app-ptr* (theme-stylesheet))))
   (def (cmd-increase-font-size app)
@@ -2263,6 +2273,13 @@
                 (or (char=? (string-ref str i) #\q)
                     (char=? (string-ref str i) (integer->char 3))
                     (scan (+ i 1)))))))
+  (define-syntax *terminal-widget-map*
+    (identifier-syntax
+      [id (vector-ref *terminal-widget-map*--cell 0)]
+      [(set! id val) (vector-set!
+                       *terminal-widget-map*--cell
+                       0
+                       val)]))
   (define-syntax *auto-indent*
     (identifier-syntax
       [id (vector-ref *auto-indent*--cell 0)]
diff --git a/src/jerboa-emacs/qt/app.ss b/src/jerboa-emacs/qt/app.ss
index 68e998c..658afc7 100644
--- a/src/jerboa-emacs/qt/app.ss
+++ b/src/jerboa-emacs/qt/app.ss
@@ -815,7 +815,8 @@
       ;; When showing an image, install key handler on the scroll area and
       ;; set focus there — the Scintilla editor is hidden by QStackedWidget
       ;; so it can't receive key events.
-      (let ((image-key-installed (make-hash-table-eq)))
+      (let ((image-key-installed (make-hash-table-eq))
+            (terminal-key-installed (make-hash-table-eq)))
         (add-hook! 'post-buffer-attach-hook
           (lambda (editor buf)
             (with-catch
@@ -830,12 +831,16 @@
                         (let ((win (hash-get *editor-window-map* editor)))
                           (when win
                             (let* ((container (qt-edit-window-container win))
-                                   ;; Find the terminal widget's index in the stacked widget
-                                   ;; It was added after the editor (0) and possibly image (1)
-                                   (count (qt-stacked-widget-count container)))
+                                   (count (qt-stacked-widget-count container))
+                                   (tw (qt-terminal-widget term)))
                               ;; Switch to the last index (terminal)
                               (qt-stacked-widget-set-current-index! container (- count 1))
-                              (qt-terminal-focus! term))))))
+                              ;; Install consuming key filter on terminal widget if not yet done
+                              ;; (guards against double-install which would double-fire events)
+                              (unless (hash-get terminal-key-installed tw)
+                                ((app-state-key-handler app) tw)
+                                (hash-put! terminal-key-installed tw #t))
+                              (qt-widget-set-focus! tw))))))
                   ;; Image buffers
                   ((image-buffer? buf)
                    (qt-show-image-buffer! editor buf)
diff --git a/src/jerboa-emacs/qt/commands-config.ss b/src/jerboa-emacs/qt/commands-config.ss
index 92086b2..b9ee224 100644
--- a/src/jerboa-emacs/qt/commands-config.ss
+++ b/src/jerboa-emacs/qt/commands-config.ss
@@ -194,6 +194,11 @@
           ;; Restore margin and default colors (font loop may have corrupted them)
           (restore-margin-colors! ed)))
       (qt-frame-windows fr)))
+  ;; Apply font to QTerminalWidget buffers
+  (hash-for-each
+    (lambda (_buf term)
+      (qt-terminal-set-font! term *default-font-family* *default-font-size*))
+    *terminal-widget-map*)
   ;; Update Qt stylesheet so chrome widgets match
   (when *qt-app-ptr*
     (qt-app-set-style-sheet! *qt-app-ptr* (theme-stylesheet))))
@@ -749,8 +754,7 @@ modified so the next save uses the new encoding."
 
 (def terminal-buffer-counter 0)
 
-;; Map buffer → QTerminalWidget pointer for the libvterm-based terminal
-(def *terminal-widget-map* (make-hash-table-eq))
+;; *terminal-widget-map* is defined in commands-shell (which this module imports)
 
 (def (cmd-term app)
   "Open a QTerminalWidget-backed terminal buffer.
@@ -791,6 +795,10 @@ modified so the next save uses the new encoding."
             (qt-stacked-widget-set-current-index! container idx))
           ;; Store widget mapping for key forwarding and buffer switching
           (hash-put! *terminal-widget-map* buf term)
+          ;; Install consuming key filter so all keys go through Scheme first
+          ;; (same pattern as image scroll widgets — without this, Qt sends keys
+          ;; directly to QTerminalWidget::keyPressEvent, bypassing jemacs entirely)
+          ((app-state-key-handler app) (qt-terminal-widget term))
           ;; Spawn the user's shell
           (qt-terminal-spawn! term "")
           ;; Focus the terminal widget
diff --git a/src/jerboa-emacs/qt/commands-shell.ss b/src/jerboa-emacs/qt/commands-shell.ss
index e6426f7..85ffb86 100644
--- a/src/jerboa-emacs/qt/commands-shell.ss
+++ b/src/jerboa-emacs/qt/commands-shell.ss
@@ -60,6 +60,11 @@
 ;;; Batch 8: Remaining missing commands
 ;;;============================================================================
 
+;; Map buffer → QTerminalWidget pointer for the libvterm-based terminal
+;; Defined here (commands-shell) because commands-config imports commands-shell,
+;; so moving it here makes it visible to both without circular dependency.
+(def *terminal-widget-map* (make-hash-table-eq))
+
 ;; --- Font size ---
 ;; Note: Font size state is now in face.ss (*default-font-size*)
 
@@ -77,6 +82,11 @@
           ;; Re-apply theme (STYLECLEARALL resets colors)
           (qt-apply-editor-theme! ed)))
       (qt-frame-windows fr)))
+  ;; Apply font size to QTerminalWidget buffers
+  (hash-for-each
+    (lambda (_buf term)
+      (qt-terminal-set-font! term *default-font-family* *default-font-size*))
+    *terminal-widget-map*)
   ;; Update Qt stylesheet so chrome widgets match
   (when *qt-app-ptr*
     (qt-app-set-style-sheet! *qt-app-ptr* (theme-stylesheet))))