Fix select-all-kill-region and cx0-removes-current-window behavioral test failures

ober

6f617fd119f607243b4d412f6b5fe9ed8c5f952b

diff --git a/src/jerboa-emacs/qt/automation.ss b/src/jerboa-emacs/qt/automation.ss
index dae9492..551bee3 100644
--- a/src/jerboa-emacs/qt/automation.ss
+++ b/src/jerboa-emacs/qt/automation.ss
@@ -206,13 +206,20 @@
 (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))
-           (target (if *minibuffer-active?*
-                     (and *mb-input* *mb-input*)
-                     (qt-current-editor fr))))
+           (get-target (lambda ()
+                         (if *minibuffer-active?*
+                           (and *mb-input* *mb-input*)
+                           (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)
+        ;; Re-fetch target after drain: the key handler may have destroyed the
+        ;; pressed window (e.g. C-x 0). Sending key release to a destroyed
+        ;; widget causes a segfault via Qt's sendPostedEvents / event dispatch.
+        (let ((release-target (get-target)))
+          (when release-target
+            (qt-send-key-release! release-target code mods text)))
         (when drain? (qt-drain-pending-callbacks!))))))
 
 ;;;============================================================================
diff --git a/src/jerboa-emacs/qt/commands-core.ss b/src/jerboa-emacs/qt/commands-core.ss
index dc35cef..faebde7 100644
--- a/src/jerboa-emacs/qt/commands-core.ss
+++ b/src/jerboa-emacs/qt/commands-core.ss
@@ -1187,8 +1187,15 @@ Returns (path . line) or #f. Handles file:line format."
 ;;;============================================================================
 
 (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)))
+    ;; SCI_SELECTALL sets caret=0, anchor=length (caret at start).
+    ;; Use set-selection! to place anchor=0, caret=len (caret at end)
+    ;; so that kill-region/copy-region see mark=0 and pos=len correctly.
+    (qt-plain-text-edit-set-selection! ed 0 len)
+    (set! (buffer-mark buf) 0)
+    (echo-message! (app-state-echo app) "Mark set (whole buffer)")))
 
 ;;;============================================================================
 ;;; Goto line
diff --git a/vendor/qt_shim.cpp b/vendor/qt_shim.cpp
index 598b73a..2ffb5cd 100644
--- a/vendor/qt_shim.cpp
+++ b/vendor/qt_shim.cpp
@@ -907,8 +907,12 @@ static void qt_cleanup_extra_selections(void* w);
 extern "C" void qt_widget_destroy(qt_widget_t w) {
     QT_NULL_CHECK_VOID(w);  // H1: null guard
     QT_VOID(
+        QWidget* widget = static_cast<QWidget*>(w);
         qt_cleanup_extra_selections(w);  // L1: clean up extra selections
-        delete static_cast<QWidget*>(w)
+        // Use deleteLater() instead of delete to avoid use-after-free:
+        // pending Qt events (resize, paint, focus) may still reference this
+        // widget and will crash if processed after synchronous deletion.
+        widget->deleteLater()
     );
 }