Update Jerboa discovery data
ober
60b00bb2abe2cb27b759670a18d7d1b7e6d436d3
--- a/data/cookbooks.sexp +++ b/data/cookbooks.sexp @@ -1786,10 +1786,10 @@ ("title" . "Write string to file (with overwrite support)")) (("code" . - ";; When a long-running operation blocks the Qt event loop (e.g. terminal\n;; rendering with many FFI calls), the UI becomes unresponsive — M-x,\n;; keyboard shortcuts, etc. stop working.\n;;\n;; Fix: call qt-app-process-events! periodically to pump pending events.\n\n;; Store a global reference to the Qt application:\n(def *qt-app-ref* #f)\n;; Set it during init:\n(set! *qt-app-ref* qt-app)\n\n;; Then in your long-running loop or after heavy processing:\n(when *qt-app-ref*\n (qt-app-process-events! *qt-app-ref*))\n\n;; Real example: vterm rendering after vtscreen-feed! (3840+ FFI calls\n;; for row-diff + per-cell colors). Without process-events, keystrokes\n;; queue up and M-x never fires during flooding commands like `find ~/ -ls`.\n\n;; Also useful in minibuffer polling loops:\n;; (let loop ()\n;; (qt-app-process-events! app)\n;; (when (not done?) (loop)))") ("id" . "qt-process-events-responsiveness") ("imports") + ";; When a long-running operation blocks the Qt event loop (e.g. terminal\n;; rendering with many FFI calls), the UI becomes unresponsive — M-x,\n;; keyboard shortcuts, etc. stop working.\n;;\n;; Fix: call qt-pump-events! periodically to pump pending events.\n\n;; Store a global reference to the Qt application:\n(def *qt-app-ref* #f)\n;; Set it during init:\n(set! *qt-app-ref* qt-app)\n\n;; Then in your long-running loop or after heavy processing:\n(when *qt-app-ref*\n (qt-pump-events! *qt-app-ref*))\n\n;; Real example: vterm rendering after vtscreen-feed! (3840+ FFI calls\n;; for row-diff + per-cell colors). Without process-events, keystrokes\n;; queue up and M-x never fires during flooding commands like `find ~/ -ls`.\n\n;; Also useful in minibuffer polling loops:\n;; (let loop ()\n;; (qt-pump-events! app)\n;; (when (not done?) (loop)))") ("id" . "qt-process-events-responsiveness") ("imports") ("notes" . - "qt-app-process-events! dispatches all pending events (key presses, mouse, paint, timers). Call it between chunks of work, not inside tight inner loops (each call has FFI overhead). Combine with a byte/time budget to cap how much work happens per tick before pumping events.") + "qt-pump-events! dispatches all pending events (key presses, mouse, paint, timers). Call it between chunks of work, not inside tight inner loops (each call has FFI overhead). Combine with a byte/time budget to cap how much work happens per tick before pumping events.") ("tags" "qt" "process-events" "responsive" "event-loop" "freeze" "ui" "blocking") ("title" @@ -1800,7 +1800,7 @@ ";; When a PTY reader thread posts data chunks to a channel, the main-thread\n;; timer tick drains all available chunks. For flooding commands (find ~/ -ls),\n;; this can accumulate megabytes per tick, blocking the event loop.\n;;\n;; Fix: set a byte budget per tick. Process up to the budget, then stop\n;; and let the next tick handle the rest.\n\n(def *pty-batch-budget* 65536) ;; 64KB per tick\n\n(let drain ((chunks []) (bytes 0) (done-msg #f))\n (if (and (> bytes 0) (>= bytes *pty-batch-budget*))\n ;; Budget exhausted — render what we have, leave rest for next tick\n (let ((combined (apply string-append (reverse chunks))))\n (process-batch! combined))\n ;; Try to get more data (non-blocking)\n (let ((msg (channel-try-get ch)))\n (cond\n ((not msg)\n ;; Channel empty — render accumulated chunks\n (unless (null? chunks)\n (let ((combined (apply string-append (reverse chunks))))\n (process-batch! combined))))\n ((string? msg)\n (drain (cons msg chunks)\n (+ bytes (string-length msg))\n done-msg))\n (else\n ;; Control message (e.g. process-done sentinel)\n (drain chunks bytes msg))))))") ("id" . "pty-batch-budget-throttle") ("imports") ("notes" . - "The byte budget alone may not be sufficient for UI responsiveness if the rendering step itself is expensive (e.g. vtscreen row-diff with per-cell FFI color calls). Combine with qt-app-process-events! (see qt-process-events-responsiveness recipe) to pump the event queue between render batches.") + "The byte budget alone may not be sufficient for UI responsiveness if the rendering step itself is expensive (e.g. vtscreen row-diff with per-cell FFI color calls). Combine with qt-pump-events! (see qt-process-events-responsiveness recipe) to pump the event queue between render batches.") ("related" "qt-process-events-responsiveness") ("tags" "pty" "terminal" "vterm" "throttle" "budget" "batch" "responsiveness") @@ -1850,7 +1850,7 @@ ";; PITFALL: qt-plain-text-edit-set-text! writes to whatever Scintilla\n;; document is currently active on the editor widget. In async callbacks,\n;; the user may have switched buffers, so the active document is WRONG.\n;;\n;; WRONG — writes into whatever buffer is currently displayed:\n;; (async-read-file! path\n;; (lambda (text)\n;; (let ((ed (qt-current-editor fr)))\n;; (qt-plain-text-edit-set-text! ed text)))) ;; BUG!\n;;\n;; CORRECT — temporarily switch to the target document:\n(let ((target-doc (buffer-doc-pointer target-buf)))\n (async-read-file! path\n (lambda (text)\n (let* ((ed (qt-current-editor (app-state-frame app)))\n (current-doc (sci-send ed SCI_GETDOCPOINTER 0)))\n ;; Switch to target document\n (sci-send ed SCI_SETDOCPOINTER 0 target-doc)\n (qt-plain-text-edit-set-text! ed text)\n ;; Switch back\n (sci-send ed SCI_SETDOCPOINTER 0 current-doc)))))") ("id" . "scintilla-async-document-safety") ("imports") ("notes" . - "Same pattern applies to ANY async or timer callback that writes text to a Scintilla editor. The vterm render path has a related issue: qt-app-process-events! can process switch-buffer events mid-render, changing the active document. Always re-check window-buffer association after processEvents.") + "Same pattern applies to ANY async or timer callback that writes text to a Scintilla editor. The vterm render path has a related issue: qt-pump-events! can process switch-buffer events mid-render, changing the active document. Always re-check window-buffer association after processEvents.") ("tags" "scintilla" "async" "document" "SCI_SETDOCPOINTER" "set-text" "buffer-switch") ("title" @@ -1858,16 +1858,16 @@ "Safe async text writes to Scintilla: switch to target document first")) (("code" . - ";; PITFALL: qt-app-process-events! pumps the Qt event queue, which can\n;; fire key event callbacks. If a switch-buffer keystroke is queued, it\n;; fires MID-OPERATION, changing the editor's active document.\n;;\n;; WRONG — assumes state is unchanged after processEvents:\n;; (let ((ed (find-editor-for-terminal-buffer)))\n;; (vtscreen-feed! vt data)\n;; (qt-app-process-events! app) ;; may fire switch-buffer!\n;; (qt-plain-text-edit-set-text! ed rendered)) ;; writes to WRONG doc!\n;;\n;; CORRECT — re-check after processEvents:\n(let ((ed (qt-edit-window-editor win)))\n (vtscreen-feed! vt data)\n (qt-app-process-events! app)\n ;; Re-check: window may now show a different buffer\n (when (eq? (qt-edit-window-buffer win) terminal-buf)\n (qt-plain-text-edit-set-text! ed rendered)))") ("id" . "qt-process-events-recheck-state") ("imports") + ";; PITFALL: qt-pump-events! pumps the Qt event queue, which can\n;; fire key event callbacks. If a switch-buffer keystroke is queued, it\n;; fires MID-OPERATION, changing the editor's active document.\n;;\n;; WRONG — assumes state is unchanged after processEvents:\n;; (let ((ed (find-editor-for-terminal-buffer)))\n;; (vtscreen-feed! vt data)\n;; (qt-pump-events! app) ;; may fire switch-buffer!\n;; (qt-plain-text-edit-set-text! ed rendered)) ;; writes to WRONG doc!\n;;\n;; CORRECT — re-check after processEvents:\n(let ((ed (qt-edit-window-editor win)))\n (vtscreen-feed! vt data)\n (qt-pump-events! app)\n ;; Re-check: window may now show a different buffer\n (when (eq? (qt-edit-window-buffer win) terminal-buf)\n (qt-plain-text-edit-set-text! ed rendered)))") ("id" . "qt-process-events-recheck-state") ("imports") ("notes" . - "This applies to ANY long-running operation that calls qt-app-process-events! for responsiveness. The processEvents call is necessary for UI responsiveness (M-x during vterm flood), but it introduces re-entrancy. Always re-validate mutable state after the call.") + "This applies to ANY long-running operation that calls qt-pump-events! for responsiveness. The processEvents call is necessary for UI responsiveness (M-x during vterm flood), but it introduces re-entrancy. Always re-validate mutable state after the call.") ("related" "qt-process-events-responsiveness") ("tags" "qt" "process-events" "state" "reentrant" "buffer-switch" "race-condition") ("title" . - "Re-check mutable state after qt-app-process-events! (can trigger buffer switches)")) + "Re-check mutable state after qt-pump-events! (can trigger buffer switches)")) (("code" . ";; Key chords should be case-sensitive and use UPPERCASE characters.\n;; This ensures lowercase typing in terminals has zero interference.\n;;\n;; Registration (case-sensitive, both orderings):\n(def (key-chord-define-global two-char-str cmd)\n (let ((c1 (string-ref two-char-str 0))\n (c2 (string-ref two-char-str 1)))\n (hash-put! *chord-map* (string c1 c2) cmd)\n (when (not (char=? c1 c2))\n (hash-put! *chord-map* (string c2 c1) cmd))\n (hash-put! *chord-first-chars* c1 #t)\n (hash-put! *chord-first-chars* c2 #t)))\n\n;; Example bindings (all uppercase):\n;; chord MT vterm\n;; chord EE eshell\n;; chord GG keyboard-quit\n\n;; IMPORTANT: During chord detection, IGNORE bare modifier key releases\n;; (Shift, Ctrl, Alt, Meta). When typing uppercase TM, the Shift release\n;; arrives between T and M. Without filtering, it cancels the chord.\n;; Qt key codes for modifiers: #x01000020 to #x01000025, #x01000053, #x01000054") ("id" . "key-chord-case-sensitive-uppercase") ("imports") @@ -5537,7 +5537,7 @@ ("title" . "Wrap a String to a Fixed Character Width")) (("code" . - "(import (jerboa prelude))\n(import (chezscheme))\n(import (jerboa-qt qt))\n\n(def immediate-exit (foreign-procedure \"_exit\" (int) void))\n\n(def (finish status)\n (flush-output-port (current-output-port))\n (flush-output-port (current-error-port))\n (immediate-exit status))\n\n(def (pump! app n)\n (when (> n 0)\n (qt-app-process-events! app)\n (pump! app (- n 1))))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def canvas (qt-paint-widget-create))\n(def paint-count (vector 0))\n(def key-code (vector #f))\n\n(qt-widget-set-minimum-size! canvas 120 160)\n(qt-on-paint! canvas\n (lambda ()\n (vector-set! paint-count 0 (+ (vector-ref paint-count 0) 1))\n (let ((p (qt-paint-widget-painter canvas)))\n (when p\n (qt-painter-fill-rect! p 0 0 120 160 24 26 32 255)\n (qt-painter-fill-rect! p 20 20 30 30 239 83 80 255)))))\n(qt-main-window-set-central-widget! win canvas)\n(qt-on-key-press! win\n (lambda ()\n (vector-set! key-code 0 (qt-last-key-code))))\n(qt-widget-show! win)\n(qt-widget-set-focus! win)\n(qt-paint-widget-update! canvas)\n(pump! app 8)\n(unless (> (vector-ref paint-count 0) 0)\n (error 'smoke \"paint callback did not run\"))\n(unless (qt-widget-screenshot! win \"/tmp/jerboa-qt-smoke.png\")\n (error 'smoke \"screenshot failed\"))\n(qt-send-key-press! win QT_KEY_LEFT QT_MOD_NONE \"\")\n(pump! app 4)\n(unless (equal? (vector-ref key-code 0) QT_KEY_LEFT)\n (error 'smoke \"synthetic key press was not observed\"))\n(displayln \"qt smoke passed\")\n(finish 0)\n") ("id" . "jerboa-qt-offscreen-screenshot-smoke") + "(import (jerboa prelude))\n(import (chezscheme))\n(import (jerboa-qt qt))\n\n(def immediate-exit (foreign-procedure \"_exit\" (int) void))\n\n(def (finish status)\n (flush-output-port (current-output-port))\n (flush-output-port (current-error-port))\n (immediate-exit status))\n\n(def (pump! app n)\n (when (> n 0)\n (qt-pump-events! app)\n (pump! app (- n 1))))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def canvas (qt-paint-widget-create))\n(def paint-count (vector 0))\n(def key-code (vector #f))\n\n(qt-widget-set-minimum-size! canvas 120 160)\n(qt-on-paint! canvas\n (lambda ()\n (vector-set! paint-count 0 (+ (vector-ref paint-count 0) 1))\n (let ((p (qt-paint-widget-painter canvas)))\n (when p\n (qt-painter-fill-rect! p 0 0 120 160 24 26 32 255)\n (qt-painter-fill-rect! p 20 20 30 30 239 83 80 255)))))\n(qt-main-window-set-central-widget! win canvas)\n(qt-on-key-press! win\n (lambda ()\n (vector-set! key-code 0 (qt-last-key-code))))\n(qt-widget-show! win)\n(qt-widget-set-focus! win)\n(qt-paint-widget-update! canvas)\n(pump! app 8)\n(unless (> (vector-ref paint-count 0) 0)\n (error 'smoke \"paint callback did not run\"))\n(unless (qt-widget-screenshot! win \"/tmp/jerboa-qt-smoke.png\")\n (error 'smoke \"screenshot failed\"))\n(qt-send-key-press! win QT_KEY_LEFT QT_MOD_NONE \"\")\n(pump! app 4)\n(unless (equal? (vector-ref key-code 0) QT_KEY_LEFT)\n (error 'smoke \"synthetic key press was not observed\"))\n(displayln \"qt smoke passed\")\n(finish 0)\n") ("id" . "jerboa-qt-offscreen-screenshot-smoke") ("imports" "(jerboa prelude)" "(chezscheme)" @@ -5574,7 +5574,7 @@ "Run a jerboa-qt example with jerbuild and shim environment")) (("code" . - "(import (jerboa prelude))\n(import (chezscheme))\n(import (jerboa-qt qt))\n\n(def immediate-exit (foreign-procedure \"_exit\" (int) void))\n\n(def (finish status)\n (flush-output-port (current-output-port))\n (flush-output-port (current-error-port))\n (immediate-exit status))\n\n(def (pump! app n)\n (when (> n 0)\n (qt-app-process-events! app)\n (pump! app (- n 1))))\n\n(def (draw-cell! painter col row r g b)\n (qt-painter-fill-rect! painter\n (+ 12 (* col 18))\n (+ 12 (* row 18))\n 16 16 r g b 255))\n\n(def (draw-board! painter)\n (qt-painter-fill-rect! painter 0 0 220 400 24 26 32 255)\n (let row-loop ((row 0))\n (when (< row 20)\n (let col-loop ((col 0))\n (when (< col 10)\n (draw-cell! painter col row 44 48 58)\n (col-loop (+ col 1))))\n (row-loop (+ row 1)))))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def canvas (qt-paint-widget-create))\n(def pos (vector 4 3))\n(def paint-count (vector 0))\n\n(qt-widget-set-minimum-size! canvas 220 400)\n(qt-on-paint! canvas\n (lambda ()\n (vector-set! paint-count 0 (+ (vector-ref paint-count 0) 1))\n (let ((p (qt-paint-widget-painter canvas)))\n (when p\n (draw-board! p)\n (let ((col (vector-ref pos 0))\n (row (vector-ref pos 1)))\n (draw-cell! p col row 132 204 22)\n (draw-cell! p (+ col 1) row 132 204 22)\n (draw-cell! p col (+ row 1) 132 204 22)\n (draw-cell! p (+ col 1) (+ row 1) 132 204 22))))))\n\n(qt-on-key-press! win\n (lambda ()\n (let ((key (qt-last-key-code))\n (col (vector-ref pos 0)))\n (cond\n ((= key QT_KEY_LEFT)\n (vector-set! pos 0 (max 0 (- col 1))))\n ((= key QT_KEY_RIGHT)\n (vector-set! pos 0 (min 8 (+ col 1))))))\n (qt-paint-widget-update! canvas)))\n\n(qt-main-window-set-title! win \"Stateful Board\")\n(qt-main-window-set-central-widget! win canvas)\n(qt-widget-resize! win 240 460)\n(qt-widget-show! win)\n(qt-widget-set-focus! win)\n(qt-paint-widget-update! canvas)\n(pump! app 8)\n\n(qt-send-key-press! win QT_KEY_RIGHT QT_MOD_NONE \"\")\n(pump! app 4)\n(unless (= (vector-ref pos 0) 5)\n (error 'stateful-board \"right key did not move piece\"))\n\n(qt-send-key-press! win QT_KEY_LEFT QT_MOD_NONE \"\")\n(qt-send-key-press! win QT_KEY_LEFT QT_MOD_NONE \"\")\n(pump! app 4)\n(unless (= (vector-ref pos 0) 3)\n (error 'stateful-board \"left keys did not move piece\"))\n\n(unless (> (vector-ref paint-count 0) 0)\n (error 'stateful-board \"paint callback did not run\"))\n(unless (qt-widget-screenshot! win \"/tmp/jerboa-qt-stateful-board.png\")\n (error 'stateful-board \"screenshot failed\"))\n\n(displayln \"stateful board passed\")\n(finish 0)\n") ("id" . "jerboa-qt-stateful-board-key-redraw") + "(import (jerboa prelude))\n(import (chezscheme))\n(import (jerboa-qt qt))\n\n(def immediate-exit (foreign-procedure \"_exit\" (int) void))\n\n(def (finish status)\n (flush-output-port (current-output-port))\n (flush-output-port (current-error-port))\n (immediate-exit status))\n\n(def (pump! app n)\n (when (> n 0)\n (qt-pump-events! app)\n (pump! app (- n 1))))\n\n(def (draw-cell! painter col row r g b)\n (qt-painter-fill-rect! painter\n (+ 12 (* col 18))\n (+ 12 (* row 18))\n 16 16 r g b 255))\n\n(def (draw-board! painter)\n (qt-painter-fill-rect! painter 0 0 220 400 24 26 32 255)\n (let row-loop ((row 0))\n (when (< row 20)\n (let col-loop ((col 0))\n (when (< col 10)\n (draw-cell! painter col row 44 48 58)\n (col-loop (+ col 1))))\n (row-loop (+ row 1)))))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def canvas (qt-paint-widget-create))\n(def pos (vector 4 3))\n(def paint-count (vector 0))\n\n(qt-widget-set-minimum-size! canvas 220 400)\n(qt-on-paint! canvas\n (lambda ()\n (vector-set! paint-count 0 (+ (vector-ref paint-count 0) 1))\n (let ((p (qt-paint-widget-painter canvas)))\n (when p\n (draw-board! p)\n (let ((col (vector-ref pos 0))\n (row (vector-ref pos 1)))\n (draw-cell! p col row 132 204 22)\n (draw-cell! p (+ col 1) row 132 204 22)\n (draw-cell! p col (+ row 1) 132 204 22)\n (draw-cell! p (+ col 1) (+ row 1) 132 204 22))))))\n\n(qt-on-key-press! win\n (lambda ()\n (let ((key (qt-last-key-code))\n (col (vector-ref pos 0)))\n (cond\n ((= key QT_KEY_LEFT)\n (vector-set! pos 0 (max 0 (- col 1))))\n ((= key QT_KEY_RIGHT)\n (vector-set! pos 0 (min 8 (+ col 1))))))\n (qt-paint-widget-update! canvas)))\n\n(qt-main-window-set-title! win \"Stateful Board\")\n(qt-main-window-set-central-widget! win canvas)\n(qt-widget-resize! win 240 460)\n(qt-widget-show! win)\n(qt-widget-set-focus! win)\n(qt-paint-widget-update! canvas)\n(pump! app 8)\n\n(qt-send-key-press! win QT_KEY_RIGHT QT_MOD_NONE \"\")\n(pump! app 4)\n(unless (= (vector-ref pos 0) 5)\n (error 'stateful-board \"right key did not move piece\"))\n\n(qt-send-key-press! win QT_KEY_LEFT QT_MOD_NONE \"\")\n(qt-send-key-press! win QT_KEY_LEFT QT_MOD_NONE \"\")\n(pump! app 4)\n(unless (= (vector-ref pos 0) 3)\n (error 'stateful-board \"left keys did not move piece\"))\n\n(unless (> (vector-ref paint-count 0) 0)\n (error 'stateful-board \"paint callback did not run\"))\n(unless (qt-widget-screenshot! win \"/tmp/jerboa-qt-stateful-board.png\")\n (error 'stateful-board \"screenshot failed\"))\n\n(displayln \"stateful board passed\")\n(finish 0)\n") ("id" . "jerboa-qt-stateful-board-key-redraw") ("imports" "(jerboa prelude)" "(chezscheme)" @@ -5589,14 +5589,14 @@ "Jerboa Qt stateful board redraw from keyboard input")) (("code" . - "(import (jerboa prelude))\n(import (chezscheme))\n(import (jerboa-qt qt))\n\n(def immediate-exit (foreign-procedure \"_exit\" (int) void))\n\n(def (finish status)\n (flush-output-port (current-output-port))\n (flush-output-port (current-error-port))\n (immediate-exit status))\n\n(def (pump! app n)\n (when (> n 0)\n (qt-app-process-events! app)\n (pump! app (- n 1))))\n\n(def (pump-wait! app n delay-ms)\n (when (> n 0)\n (sleep-ms delay-ms)\n (qt-app-process-events! app)\n (pump-wait! app (- n 1) delay-ms)))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def canvas (qt-paint-widget-create))\n(def timer (qt-timer-create))\n(def piece-row (vector 0))\n(def paint-count (vector 0))\n\n(qt-widget-set-minimum-size! canvas 220 400)\n(qt-on-paint! canvas\n (lambda ()\n (vector-set! paint-count 0 (+ (vector-ref paint-count 0) 1))\n (let ((p (qt-paint-widget-painter canvas)))\n (when p\n (qt-painter-fill-rect! p 0 0 220 400 24 26 32 255)\n (qt-painter-fill-rect! p 84 (+ 12 (* (vector-ref piece-row 0) 18))\n 34 34 132 204 22 255)))))\n\n(qt-on-timeout! timer\n (lambda ()\n (vector-set! piece-row 0 (min 18 (+ (vector-ref piece-row 0) 1)))\n (qt-paint-widget-update! canvas)))\n\n(qt-main-window-set-title! win \"Timer Redraw\")\n(qt-main-window-set-central-widget! win canvas)\n(qt-widget-resize! win 240 460)\n(qt-widget-show! win)\n(qt-paint-widget-update! canvas)\n(pump! app 4)\n\n(qt-timer-start! timer 20)\n(pump-wait! app 10 25)\n(qt-timer-stop! timer)\n\n(unless (> (vector-ref piece-row 0) 0)\n (error 'timer-redraw \"timer did not advance state\"))\n(unless (> (vector-ref paint-count 0) 0)\n (error 'timer-redraw \"paint callback did not run\"))\n\n(let ((stopped-row (vector-ref piece-row 0)))\n (pump-wait! app 4 25)\n (unless (= (vector-ref piece-row 0) stopped-row)\n (error 'timer-redraw \"timer kept firing after stop\")))\n\n(unless (qt-widget-screenshot! win \"/tmp/jerboa-qt-timer-redraw.png\")\n (error 'timer-redraw \"screenshot failed\"))\n\n(qt-timer-destroy! timer)\n(displayln \"timer redraw passed\")\n(finish 0)\n") ("id" . "jerboa-qt-offscreen-timer-redraw") + "(import (jerboa prelude))\n(import (chezscheme))\n(import (jerboa-qt qt))\n\n(def immediate-exit (foreign-procedure \"_exit\" (int) void))\n\n(def (finish status)\n (flush-output-port (current-output-port))\n (flush-output-port (current-error-port))\n (immediate-exit status))\n\n(def (pump! app n)\n (when (> n 0)\n (qt-pump-events! app)\n (pump! app (- n 1))))\n\n(def (pump-wait! app n delay-ms)\n (when (> n 0)\n (sleep-ms delay-ms)\n (qt-pump-events! app)\n (pump-wait! app (- n 1) delay-ms)))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def canvas (qt-paint-widget-create))\n(def timer (qt-timer-create))\n(def piece-row (vector 0))\n(def paint-count (vector 0))\n\n(qt-widget-set-minimum-size! canvas 220 400)\n(qt-on-paint! canvas\n (lambda ()\n (vector-set! paint-count 0 (+ (vector-ref paint-count 0) 1))\n (let ((p (qt-paint-widget-painter canvas)))\n (when p\n (qt-painter-fill-rect! p 0 0 220 400 24 26 32 255)\n (qt-painter-fill-rect! p 84 (+ 12 (* (vector-ref piece-row 0) 18))\n 34 34 132 204 22 255)))))\n\n(qt-on-timeout! timer\n (lambda ()\n (vector-set! piece-row 0 (min 18 (+ (vector-ref piece-row 0) 1)))\n (qt-paint-widget-update! canvas)))\n\n(qt-main-window-set-title! win \"Timer Redraw\")\n(qt-main-window-set-central-widget! win canvas)\n(qt-widget-resize! win 240 460)\n(qt-widget-show! win)\n(qt-paint-widget-update! canvas)\n(pump! app 4)\n\n(qt-timer-start! timer 20)\n(pump-wait! app 10 25)\n(qt-timer-stop! timer)\n\n(unless (> (vector-ref piece-row 0) 0)\n (error 'timer-redraw \"timer did not advance state\"))\n(unless (> (vector-ref paint-count 0) 0)\n (error 'timer-redraw \"paint callback did not run\"))\n\n(let ((stopped-row (vector-ref piece-row 0)))\n (pump-wait! app 4 25)\n (unless (= (vector-ref piece-row 0) stopped-row)\n (error 'timer-redraw \"timer kept firing after stop\")))\n\n(unless (qt-widget-screenshot! win \"/tmp/jerboa-qt-timer-redraw.png\")\n (error 'timer-redraw \"screenshot failed\"))\n\n(qt-timer-destroy! timer)\n(displayln \"timer redraw passed\")\n(finish 0)\n") ("id" . "jerboa-qt-offscreen-timer-redraw") ("imports" "(jerboa prelude)" "(chezscheme)" "(jerboa-qt qt)") ("notes" . - "For offscreen timer tests, start the timer with `(qt-timer-start! timer msec)`, then alternate `sleep-ms` with `qt-app-process-events!` so wall-clock timer events can fire. Timeout handlers should mutate authoritative state and then call `qt-paint-widget-update!`. Stop the timer before asserting it no longer moves. `_exit` is harness-only for Qt smoke tests that must avoid C++ teardown crashes; normal apps should use the Qt event loop normally.") + "For offscreen timer tests, start the timer with `(qt-timer-start! timer msec)`, then alternate `sleep-ms` with `qt-pump-events!` so wall-clock timer events can fire. Timeout handlers should mutate authoritative state and then call `qt-paint-widget-update!`. Stop the timer before asserting it no longer moves. `_exit` is harness-only for Qt smoke tests that must avoid C++ teardown crashes; normal apps should use the Qt event loop normally.") ("tags" "jerboa-qt" "qt" "timer" "offscreen" "paint-widget" "sleep-ms") ("title" @@ -5675,7 +5675,7 @@ "Set PTY child as foreground process group after TIOCSCTTY")) (("code" . - "(import (jerboa prelude))\n(import (jerboa-qt qt))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def state (vector 0))\n\n(def (move-left!)\n (vector-set! state 0 (- (vector-ref state 0) 1))\n #t)\n\n(def (move-right!)\n (vector-set! state 0 (+ (vector-ref state 0) 1))\n #t)\n\n(qt-on-key-press! win\n (lambda ()\n (let ((key (qt-last-key-code)))\n (cond\n ((= key QT_KEY_LEFT) (move-left!))\n ((= key QT_KEY_RIGHT) (move-right!))\n (else #t)))))\n\n(qt-widget-show! win)\n(qt-app-process-events! app)") ("id" . "jerboa-qt-key-code-cond-dispatch") + "(import (jerboa prelude))\n(import (jerboa-qt qt))\n\n(def app (qt-app-create))\n(def win (qt-main-window-create))\n(def state (vector 0))\n\n(def (move-left!)\n (vector-set! state 0 (- (vector-ref state 0) 1))\n #t)\n\n(def (move-right!)\n (vector-set! state 0 (+ (vector-ref state 0) 1))\n #t)\n\n(qt-on-key-press! win\n (lambda ()\n (let ((key (qt-last-key-code)))\n (cond\n ((= key QT_KEY_LEFT) (move-left!))\n ((= key QT_KEY_RIGHT) (move-right!))\n (else #t)))))\n\n(qt-widget-show! win)\n(qt-pump-events! app)") ("id" . "jerboa-qt-key-code-cond-dispatch") ("imports" "(jerboa prelude)" "(jerboa-qt qt)") ("notes" .