Fix Phase 5 terminal key interception and static build infrastructure
ober
20454f01145efda1d15f466bfa0899b2a9d952fe
--- a/Makefile +++ b/Makefile @@ -256,7 +256,9 @@ PCRE2_SRC ?= $(HOME)/mine/chez-pcre2 SCI_SRC ?= $(HOME)/mine/chez-scintilla QT_SRC ?= $(CURDIR)/vendor/chez-qt QTSHIM_SRC ?= $(HOME)/mine/gerbil-qt -JSH_COREUTILS_LIB ?= $(JSH_SRC)/rust-coreutils/target/x86_64-unknown-linux-musl/release/libjsh_coreutils.a +# Use stub if the Rust musl build hasn't been compiled yet (regular file check) +_RUST_COREUTILS := $(JSH_SRC)/rust-coreutils/target/x86_64-unknown-linux-musl/release/libjsh_coreutils.a +JSH_COREUTILS_LIB ?= $(shell test -f $(_RUST_COREUTILS) && echo $(_RUST_COREUTILS) || echo $(CURDIR)/vendor/libjsh_coreutils_stub.a) DEPS_IMAGE := jemacs-deps:$(ARCH) @@ -320,6 +322,9 @@ build-jemacs-qt-static: check-root ar rcs /deps/gerbil-qt/vendor/libqt_shim.a \ /deps/gerbil-qt/vendor/qt_shim_static.o; \ fi && \ + cp -a /src/vendor/chez-qt/. /deps/chez-qt/ && \ + find /deps/chez-qt -name '*.so' -delete && \ + find /deps/chez-qt -name '*.wpo' -delete && \ /opt/chez/bin/scheme --libdirs /deps/chez-qt \ --compile-imported-libraries --script /deps/chez-qt/compile-libs.ss && \ rm -f /deps/chez-qt/chez-qt/*.wpo && \ --- a/lib/jerboa-emacs/async.sls +++ b/lib/jerboa-emacs/async.sls @@ -552,8 +552,8 @@ (let ([v (hashtable-ref cache key - '#{miss l89txjuduef6khmnseoln0n2k-1})]) - (if (eq? v '#{miss l89txjuduef6khmnseoln0n2k-2}) + '#{miss j40h7yge6hu7zd2spbldcd8pr-1})]) + (if (eq? v '#{miss j40h7yge6hu7zd2spbldcd8pr-2}) (if (null? default) #f (car default)) v))) (def (weak-cache-set! cache key value) --- a/lib/jerboa-emacs/qt/app.sls +++ b/lib/jerboa-emacs/qt/app.sls @@ -1806,7 +1806,15 @@ (app-state-key-handler-set! app (lambda (editor) - (qt-on-key-press-consuming! editor key-handler)))) + (qt-on-key-press-consuming! editor key-handler))) + (automation-set-key-target-fn! + (lambda (fr) + (let* ([buf (qt-current-buffer fr)] + [term (and buf + (hash-get *terminal-widget-map* buf))]) + (if term + (qt-terminal-widget term) + (qt-current-editor fr)))))) (schedule-periodic! 'repl-poll 50 --- a/lib/jerboa-emacs/qt/automation.sls +++ b/lib/jerboa-emacs/qt/automation.sls @@ -5,7 +5,8 @@ (library (jerboa-emacs qt automation) (export automation-send-keys! automation-send-keys-async! automation-screenshot! automation-state automation-wait! - automation-set-qt-app! emacs-key->qt-event) + automation-set-qt-app! automation-set-key-target-fn! + emacs-key->qt-event) (import (except (chezscheme) make-hash-table hash-table? iota \x31;+ \x31;- getenv path-extension path-absolute? thread? make-mutex @@ -162,13 +163,20 @@ (let-values ([(code mods text) (emacs-key->qt-event key-str)]) (let* ([fr (app-state-frame app)] - [target (if *minibuffer-active?* - (and *mb-input* *mb-input*) - (qt-current-editor fr))]) + [get-target (lambda () + (cond + [*minibuffer-active?* + (and *mb-input* *mb-input*)] + [*automation-key-target-fn* + (*automation-key-target-fn* fr)] + [else (qt-current-editor fr)]))] + [target (get-target)]) (when target (qt-send-key-press! target code mods text) (when drain? (qt-drain-pending-callbacks!)) - (qt-send-key-release! target code mods text) + (let ([release-target (get-target)]) + (when release-target + (qt-send-key-release! release-target code mods text))) (when drain? (qt-drain-pending-callbacks!)))))) (def (automation-screenshot! app path) (let* ([fr (app-state-frame app)] @@ -220,4 +228,7 @@ (+ (* (time-second t) 1000) (quotient (time-nanosecond t) 1000000)))) (def *qt-app-ref* #f) - (def (automation-set-qt-app! app) (set! *qt-app-ref* app))) + (def (automation-set-qt-app! app) (set! *qt-app-ref* app)) + (def *automation-key-target-fn* #f) + (def (automation-set-key-target-fn! fn) + (set! *automation-key-target-fn* fn))) --- a/lib/jerboa-emacs/qt/commands-core.sls +++ b/lib/jerboa-emacs/qt/commands-core.sls @@ -1130,10 +1130,14 @@ (echo-message! echo (string-append "Reverted " path)))) (echo-error! echo "Buffer is not visiting a file")))) (def (cmd-select-all app) - (qt-plain-text-edit-select-all! (current-qt-editor app)) - (echo-message! - (app-state-echo app) - "Mark set (whole buffer)")) + (let* ([ed (current-qt-editor app)] + [buf (current-qt-buffer app)] + [len (qt-plain-text-edit-text-length ed)]) + (qt-plain-text-edit-set-selection! ed 0 len) + (buffer-mark-set! buf 0) + (echo-message! + (app-state-echo app) + "Mark set (whole buffer)"))) (def (cmd-goto-line app) (let* ([echo (app-state-echo app)] [input (qt-echo-read-string app "Goto line: ")]) --- a/src/jerboa-emacs/qt/app.ss +++ b/src/jerboa-emacs/qt/app.ss @@ -1378,7 +1378,20 @@ ;; Store installer so split-window can install on new editors (set! (app-state-key-handler app) (lambda (editor) - (qt-on-key-press-consuming! editor key-handler)))) + (qt-on-key-press-consuming! editor key-handler))) + + ;; Tell automation which widget to target for key events. + ;; When a terminal buffer is active, the QTerminalWidget is the visible + ;; focused widget; the QScintilla editor is hidden behind it in the + ;; QStackedWidget. Sending sendEvent to a non-current QStackedWidget + ;; page is unreliable — use the visible QTerminalWidget instead. + (automation-set-key-target-fn! + (lambda (fr) + (let* ((buf (qt-current-buffer fr)) + (term (and buf (hash-get *terminal-widget-map* buf)))) + (if term + (qt-terminal-widget term) + (qt-current-editor fr)))))) ;; ================================================================ ;; Periodic tasks — registered with schedule-periodic!, driven --- a/src/jerboa-emacs/qt/automation.ss +++ b/src/jerboa-emacs/qt/automation.ss @@ -11,6 +11,7 @@ automation-state automation-wait! automation-set-qt-app! + automation-set-key-target-fn! emacs-key->qt-event) (import :std/sugar @@ -203,13 +204,24 @@ ;;; If the minibuffer is active, sends to the minibuffer input widget instead. ;;; When drain? is #t, drains the deferred callback queue after each event ;;; so the Scheme key handler fires immediately. +;;; +;;; Target selection priority: +;;; 1. Minibuffer input widget (when minibuffer is active) +;;; 2. *automation-key-target-fn* override (e.g. QTerminalWidget when +;;; terminal buffer is active — the QScintilla is hidden behind it in +;;; the QStackedWidget and sendEvent to a non-current page is unreliable) +;;; 3. qt-current-editor (the QScintilla editor for the current window) (def (send-one-key! app key-str drain?) (let-values (((code mods text) (emacs-key->qt-event key-str))) (let* ((fr (app-state-frame app)) (get-target (lambda () - (if *minibuffer-active?* - (and *mb-input* *mb-input*) - (qt-current-editor fr)))) + (cond + (*minibuffer-active?* + (and *mb-input* *mb-input*)) + (*automation-key-target-fn* + (*automation-key-target-fn* fr)) + (else + (qt-current-editor fr))))) (target (get-target))) (when target (qt-send-key-press! target code mods text) @@ -300,3 +312,14 @@ (def (automation-set-qt-app! app) (set! *qt-app-ref* app)) + +;;; Key-target override: called with (fr) to find the active input widget. +;;; When #f, falls back to (qt-current-editor fr). +;;; Set from app.ss after terminal widget map is available. +;;; Purpose: when a terminal buffer is active, the focused widget is the +;;; QTerminalWidget (not the hidden QScintilla). Sending key events to a +;;; hidden widget via sendEvent may not reliably trigger event filters. +(def *automation-key-target-fn* #f) + +(def (automation-set-key-target-fn! fn) + (set! *automation-key-target-fn* fn)) new file mode 100644 Binary files /dev/null and b/vendor/libjsh_coreutils_stub.a differ