678 lines
27 KiB
Racket
678 lines
27 KiB
Racket
#lang racket/base
|
|
(require ffi/unsafe
|
|
racket/draw/private/utils
|
|
ffi/unsafe/atomic
|
|
racket/class
|
|
racket/port
|
|
"rbtree.rkt"
|
|
"../../lock.rkt"
|
|
"handlers.rkt"
|
|
"once.rkt")
|
|
|
|
(provide
|
|
(protect-out queue-evt
|
|
set-check-queue!
|
|
set-queue-wakeup!
|
|
|
|
add-event-boundary-callback!
|
|
add-event-boundary-sometimes-callback!
|
|
remove-event-boundary-callback!
|
|
pre-event-sync
|
|
boundary-tasks-ready-evt
|
|
sometimes-delay-msec
|
|
set-platform-queue-sync!
|
|
|
|
eventspace?
|
|
current-eventspace
|
|
queue-event
|
|
queue-refresh-event
|
|
yield
|
|
yield/no-sync
|
|
yield-refresh
|
|
eventspace-event-evt
|
|
(rename-out [make-new-eventspace make-eventspace])
|
|
|
|
event-dispatch-handler
|
|
eventspace-shutdown?
|
|
main-eventspace?
|
|
eventspace-handler-thread
|
|
eventspace-event-evt
|
|
eventspace-wait-cursor-count
|
|
eventspace-extra-table
|
|
eventspace-adjust-external-modal!
|
|
|
|
queue-callback
|
|
middle-queue-key
|
|
|
|
make-timer-callback
|
|
add-timer-callback
|
|
remove-timer-callback
|
|
|
|
register-frame-shown
|
|
get-top-level-windows
|
|
other-modal?
|
|
|
|
queue-quit-event
|
|
queue-prefs-event
|
|
queue-about-event
|
|
queue-file-event
|
|
queue-start-empty-event
|
|
|
|
begin-busy-cursor
|
|
end-busy-cursor
|
|
is-busy?)
|
|
|
|
scheme_register_process_global)
|
|
|
|
;; ------------------------------------------------------------
|
|
;; Create a Scheme evt that is ready when a queue is nonempty
|
|
|
|
(define _Scheme_Type _short)
|
|
(define-mz scheme_make_type (_fun _string -> _Scheme_Type))
|
|
(define event-queue-type (scheme_make_type "event-queue"))
|
|
|
|
(define-mz scheme_add_evt (_fun _Scheme_Type
|
|
(_fun #:atomic? #t _scheme -> _int)
|
|
(_fun #:atomic? #t _scheme _gcpointer -> _void)
|
|
_pointer
|
|
_int
|
|
-> _void))
|
|
|
|
(define (do-check-queue) #f)
|
|
(define (do-queue-wakeup fds) #f)
|
|
|
|
(define (check-queue o)
|
|
(if (do-check-queue) 1 0))
|
|
(define (queue-wakeup o fds)
|
|
(do-queue-wakeup fds))
|
|
(scheme_add_evt event-queue-type check-queue queue-wakeup #f 0)
|
|
(define queue-evt (let ([p (malloc 16)]
|
|
[p2 (malloc 'nonatomic _pointer)])
|
|
(memset p 0 16)
|
|
(ptr-set! p _Scheme_Type event-queue-type)
|
|
(ptr-set! p2 _pointer p)
|
|
(ptr-ref p2 _scheme)))
|
|
|
|
(define (set-check-queue! check)
|
|
(set! do-check-queue check))
|
|
(define (set-queue-wakeup! wake)
|
|
(set! do-queue-wakeup wake))
|
|
|
|
;; ------------------------------------------------------------
|
|
;; Pre-event sync
|
|
|
|
(define boundary-ht (make-hasheq))
|
|
(define sometimes-boundary-ht (make-hasheq))
|
|
|
|
(define tasks-ready? #f)
|
|
(define task-ready-sema (make-semaphore))
|
|
(define boundary-tasks-ready-evt (semaphore-peek-evt task-ready-sema))
|
|
|
|
(define (alert-tasks-ready)
|
|
(let ([ready? (or (positive? (hash-count boundary-ht))
|
|
(positive? (hash-count sometimes-boundary-ht)))])
|
|
(unless (eq? ready? tasks-ready?)
|
|
(set! tasks-ready? ready?)
|
|
(if ready?
|
|
(semaphore-post task-ready-sema)
|
|
(semaphore-wait task-ready-sema)))))
|
|
|
|
(define (add-event-boundary-callback! v proc)
|
|
(atomically
|
|
(hash-set! boundary-ht v proc)
|
|
(alert-tasks-ready)))
|
|
(define (add-event-boundary-sometimes-callback! v proc)
|
|
(atomically
|
|
(when (zero? (hash-count sometimes-boundary-ht))
|
|
(set! last-time (current-inexact-milliseconds)))
|
|
(hash-set! sometimes-boundary-ht v proc)
|
|
(alert-tasks-ready)))
|
|
|
|
(define (remove-event-boundary-callback! v)
|
|
(atomically
|
|
(hash-remove! boundary-ht v)
|
|
(hash-remove! sometimes-boundary-ht v)
|
|
(alert-tasks-ready)))
|
|
|
|
(define last-time -inf.0)
|
|
(define sometimes-delay-msec 100)
|
|
|
|
;; Call this function only in atomic mode:
|
|
(define (pre-event-sync force?)
|
|
(let ([now (current-inexact-milliseconds)])
|
|
(when (or (now . > . (+ last-time sometimes-delay-msec))
|
|
force?)
|
|
(set! last-time now)
|
|
(hash-for-each sometimes-boundary-ht
|
|
(lambda (v p) (hash-remove! sometimes-boundary-ht v) (p v)))))
|
|
(hash-for-each boundary-ht (lambda (v p) (hash-remove! boundary-ht v) (p v)))
|
|
(alert-tasks-ready))
|
|
|
|
;; ------------------------------------------------------------
|
|
;; Eventspaces
|
|
|
|
(define-struct eventspace (handler-thread
|
|
queue-proc
|
|
frames-hash
|
|
done-evt
|
|
[shutdown? #:mutable]
|
|
done-sema
|
|
[wait-cursor-count #:mutable]
|
|
extra-table
|
|
[external-modal #:mutable])
|
|
#:property prop:evt (lambda (v)
|
|
(wrap-evt (eventspace-done-evt v)
|
|
(lambda (_) v))))
|
|
(define-struct timed (alarm-evt msecs val [id #:mutable]))
|
|
|
|
(define (make-timer-callback msecs thunk)
|
|
(make-timed (alarm-evt msecs)
|
|
msecs
|
|
thunk
|
|
0))
|
|
|
|
(define (timed-compare a b)
|
|
(if (eq? a b)
|
|
0
|
|
(let ([am (timed-msecs a)]
|
|
[bm (timed-msecs b)])
|
|
(cond
|
|
[(= am bm) (if ((timed-id a) . < . (timed-id b))
|
|
-1
|
|
1)]
|
|
[(< am bm) -1]
|
|
[else 1]))))
|
|
|
|
;; This table refers to handle threads of eventspaces
|
|
;; that have an open window, etc., so that the eventspace
|
|
;; isn't GCed
|
|
(define active-eventspaces (make-hasheq))
|
|
|
|
(define current-cb-box (make-parameter #f))
|
|
|
|
(define-mz scheme_add_managed (_fun _racket ; custodian
|
|
_racket ; object
|
|
(_fun #:atomic? #t #:keep (lambda (v) (set-box! (current-cb-box) v))
|
|
_racket _racket -> _void)
|
|
_racket ; data
|
|
_int ; strong?
|
|
-> _gcpointer))
|
|
|
|
(define (shutdown-eventspace! e ignored)
|
|
;; atomic mode
|
|
(unless (eventspace-shutdown? e)
|
|
(set-eventspace-shutdown?! e #t)
|
|
(semaphore-post (eventspace-done-sema e))
|
|
(for ([f (in-list (get-top-level-windows e))])
|
|
(send f destroy))
|
|
(hash-remove! active-eventspaces (eventspace-handler-thread e))))
|
|
|
|
(define platform-queue-sync void)
|
|
(define (set-platform-queue-sync! proc)
|
|
(set! platform-queue-sync proc))
|
|
|
|
(define (make-eventspace* th)
|
|
(let ([done-sema (make-semaphore 1)]
|
|
[done-set? #t]
|
|
[frames (make-hasheq)])
|
|
(let ([e
|
|
(make-eventspace th
|
|
(let ([count 0])
|
|
(let ([lo (mcons #f #f)]
|
|
[refresh (mcons #f #f)]
|
|
[med (mcons #f #f)]
|
|
[hi (mcons #f #f)]
|
|
[timer (box '())]
|
|
[timer-counter 0]
|
|
[newly-posted-sema (make-semaphore)])
|
|
(let* ([check-done
|
|
(lambda ()
|
|
(if (or (positive? count)
|
|
(positive? (hash-count frames))
|
|
(not (null? (unbox timer))))
|
|
(when done-set?
|
|
(hash-set! active-eventspaces th #t)
|
|
(set! done-set? #f)
|
|
(semaphore-try-wait? done-sema))
|
|
(unless done-set?
|
|
(hash-remove! active-eventspaces th)
|
|
(set! done-set? #t)
|
|
(semaphore-post done-sema))))]
|
|
[enqueue (lambda (v q)
|
|
(set! count (add1 count))
|
|
(check-done)
|
|
(let ([p (mcons v #f)])
|
|
(if (mcdr q)
|
|
(set-mcdr! (mcdr q) p)
|
|
(set-mcar! q p))
|
|
(set-mcdr! q p)))]
|
|
[first (lambda (q peek?)
|
|
(and (mcar q)
|
|
(if peek?
|
|
always-evt
|
|
(wrap-evt
|
|
always-evt
|
|
(lambda (_)
|
|
(start-atomic)
|
|
(set! count (sub1 count))
|
|
(check-done)
|
|
(let ([result (mcar (mcar q))])
|
|
(set-mcar! q (mcdr (mcar q)))
|
|
(unless (mcar q)
|
|
(set-mcdr! q #f))
|
|
(end-atomic)
|
|
result))))))]
|
|
[remove-timer
|
|
(lambda (v timer)
|
|
(set-box! timer (rbtree-remove
|
|
timed-compare
|
|
v
|
|
(unbox timer)))
|
|
(check-done))]
|
|
[timer-first-ready
|
|
(lambda (timer peek?)
|
|
(let ([rb (unbox timer)])
|
|
(and (not (null? rb))
|
|
(let* ([v (rbtree-min (unbox timer))]
|
|
[evt (timed-alarm-evt v)])
|
|
(and (sync/timeout 0 evt)
|
|
;; It's ready
|
|
(if peek?
|
|
always-evt
|
|
(wrap-evt
|
|
always-evt
|
|
(lambda (_)
|
|
(start-atomic)
|
|
(remove-timer v timer)
|
|
(end-atomic)
|
|
(timed-val v)))))))))]
|
|
[timer-first-wait
|
|
(lambda (timer peek?)
|
|
(let ([rb (unbox timer)])
|
|
(and (not (null? rb))
|
|
(wrap-evt
|
|
(timed-alarm-evt (rbtree-min (unbox timer)))
|
|
(lambda (_) #f)))))]
|
|
[make-event-choice
|
|
(lambda (peek? sync?)
|
|
(choice-evt
|
|
(wrap-evt (semaphore-peek-evt newly-posted-sema)
|
|
(lambda (_) #f))
|
|
(or (first hi peek?)
|
|
(timer-first-ready timer peek?)
|
|
(first refresh peek?)
|
|
(first med peek?)
|
|
(and (not peek?)
|
|
sync?
|
|
;; before going with low-priority events,
|
|
;; make sure we're sync'ed up with the
|
|
;; GUI platform's event queue:
|
|
(platform-queue-sync)
|
|
(first med peek?))
|
|
(first lo peek?)
|
|
(timer-first-wait timer peek?)
|
|
;; nothing else ready...
|
|
never-evt)))])
|
|
(case-lambda
|
|
[(v)
|
|
;; Enqueue
|
|
(start-atomic)
|
|
(let ([val (cdr v)])
|
|
(case (car v)
|
|
[(lo) (enqueue val lo)]
|
|
[(refresh) (enqueue val refresh)]
|
|
[(med) (enqueue val med)]
|
|
[(hi) (enqueue val hi)]
|
|
[(timer-add)
|
|
(set! timer-counter (add1 timer-counter))
|
|
(set-timed-id! val timer-counter)
|
|
(set-box! timer
|
|
(rbtree-insert
|
|
timed-compare
|
|
val
|
|
(unbox timer)))
|
|
(check-done)]
|
|
[(timer-remove) (remove-timer val timer)]
|
|
[(frame-add) (hash-set! frames val #t) (check-done)]
|
|
[(frame-remove) (hash-remove! frames val) (check-done)]))
|
|
(semaphore-post newly-posted-sema)
|
|
(set! newly-posted-sema (make-semaphore))
|
|
(check-done)
|
|
(end-atomic)]
|
|
[()
|
|
;; Dequeue as evt
|
|
(start-atomic)
|
|
(begin0
|
|
(make-event-choice #f #t)
|
|
(end-atomic))]
|
|
[(only-refresh? peek? sync?)
|
|
(start-atomic)
|
|
(begin0
|
|
(cond
|
|
[only-refresh?
|
|
;; Dequeue only refresh event
|
|
(or (first refresh peek?) never-evt)]
|
|
[else
|
|
(make-event-choice peek? sync?)])
|
|
(end-atomic))]))))
|
|
frames
|
|
(semaphore-peek-evt done-sema)
|
|
#f
|
|
done-sema
|
|
0
|
|
(make-hash)
|
|
0)]
|
|
[cb-box (box #f)])
|
|
(parameterize ([current-cb-box cb-box])
|
|
(scheme_add_managed (current-custodian)
|
|
e
|
|
shutdown-eventspace!
|
|
cb-box ; retain callback until it's called
|
|
0))
|
|
e)))
|
|
|
|
(define main-eventspace (make-eventspace* (current-thread)))
|
|
(define current-eventspace (make-parameter main-eventspace))
|
|
|
|
;; So we can get from a thread to the eventspace that
|
|
;; it handles (independent of the `current-eventspace'
|
|
;; parameter):
|
|
(define handler-thread-of (make-thread-cell #f))
|
|
(thread-cell-set! handler-thread-of main-eventspace)
|
|
|
|
(define make-new-eventspace
|
|
(let ([make-eventspace
|
|
(lambda ()
|
|
(define pause (make-semaphore))
|
|
(define break-paramz (current-break-parameterization))
|
|
(define es
|
|
(make-eventspace*
|
|
(parameterize-break
|
|
#f ; disable breaks until we're in the yield loop
|
|
(thread
|
|
(lambda ()
|
|
(sync pause) ; wait until `es' has a value
|
|
(thread-cell-set! handler-thread-of es)
|
|
(current-eventspace es)
|
|
(let loop ()
|
|
(call-with-continuation-prompt
|
|
(lambda ()
|
|
;; re-enable breaks (if they are supposed to be enabled):
|
|
(call-with-break-parameterization
|
|
break-paramz
|
|
(lambda ()
|
|
;; yield; any abort (including a break exception)
|
|
;; will get caught and the loop will yield again
|
|
(yield (make-semaphore))))))
|
|
(loop)))))))
|
|
(semaphore-post pause) ; `es' has a value
|
|
es)])
|
|
make-eventspace))
|
|
|
|
(define (queue-event eventspace thunk [level 'med])
|
|
((eventspace-queue-proc eventspace) (cons level thunk)))
|
|
|
|
(define (queue-refresh-event eventspace thunk)
|
|
((eventspace-queue-proc eventspace) (cons 'refresh thunk)))
|
|
|
|
(define dispatch-event-prompt (make-continuation-prompt-tag))
|
|
(define dispatch-event-key (gensym))
|
|
|
|
(define (really-dispatch-event e)
|
|
(let ([b (continuation-mark-set-first
|
|
#f
|
|
dispatch-event-key
|
|
#f
|
|
dispatch-event-prompt)])
|
|
(unless b
|
|
(error 'default-event-dispatch-handler
|
|
"not in an event-dispatch context"))
|
|
(let ([thunk (atomically
|
|
(begin0
|
|
(unbox b)
|
|
(set-box! b #f)))])
|
|
(unless thunk
|
|
(error 'default-event-dispatch-handler
|
|
"event in current context was already dispatched"))
|
|
(thunk))))
|
|
|
|
(define event-dispatch-handler (make-parameter really-dispatch-event))
|
|
|
|
(define event-logger (make-logger 'gui-event (current-logger)))
|
|
;; start? : boolean -- indicates if this is a start of an event being handled or not
|
|
;; msec : start time if start? is #t, delta from start to end if start? is #f
|
|
;; name : (or/c #f symbol?)
|
|
(struct gui-event (start end name) #:prefab)
|
|
|
|
(define (handle-event thunk e)
|
|
(call-with-continuation-prompt ; to delimit continuations
|
|
(lambda ()
|
|
(call-with-continuation-prompt ; to delimit search for dispatch-event-key
|
|
(lambda ()
|
|
;; communicate the thunk to `really-dispatch-event':
|
|
(define before (current-inexact-milliseconds))
|
|
(when (log-level? event-logger 'debug)
|
|
(log-message event-logger 'debug
|
|
(format "starting to handle an event from ~a" (object-name thunk))
|
|
(gui-event before #f (object-name thunk))))
|
|
(let ([b (box thunk)])
|
|
;; use the event-dispatch handler:
|
|
(with-continuation-mark dispatch-event-key b
|
|
((event-dispatch-handler) e))
|
|
;; if the event-dispatch handler doesn't chain
|
|
;; to the original one, then do so now:
|
|
(when (unbox b)
|
|
(set-box! b #f)
|
|
(thunk)))
|
|
(define after (current-inexact-milliseconds))
|
|
(when (log-level? event-logger 'debug)
|
|
(log-message event-logger 'debug
|
|
(format "handled an event: ~a msec"
|
|
(- after before))
|
|
(gui-event before after (object-name thunk)))))
|
|
dispatch-event-prompt))))
|
|
|
|
(define yield
|
|
(case-lambda
|
|
[()
|
|
(let ([e (current-eventspace)])
|
|
(if (eq? (current-thread) (eventspace-handler-thread e))
|
|
(let ([v (sync/timeout 0 ((eventspace-queue-proc e)))])
|
|
(if v
|
|
(begin (handle-event v e) #t)
|
|
#f))
|
|
#f))]
|
|
[(evt)
|
|
(unless (or (evt? evt)
|
|
(eq? evt 'wait))
|
|
(raise-type-error 'yield "evt or 'wait" evt))
|
|
(let* ([e (current-eventspace)]
|
|
[handler? (eq? (current-thread) (eventspace-handler-thread e))])
|
|
(cond
|
|
[(and (eq? evt 'wait)
|
|
(not handler?))
|
|
#t]
|
|
[else
|
|
(define (wait-now)
|
|
(if handler?
|
|
(sync (if (eq? evt 'wait)
|
|
(wrap-evt e (lambda (_) #t))
|
|
evt)
|
|
(handle-evt ((eventspace-queue-proc e))
|
|
(lambda (v)
|
|
(when v (handle-event v e))
|
|
(yield evt))))
|
|
(sync evt)))
|
|
(if (evt? evt)
|
|
;; `yield' is supposed to return immediately if the
|
|
;; event is already ready:
|
|
(sync/timeout wait-now evt)
|
|
(wait-now))]))]))
|
|
|
|
(define (yield/no-sync)
|
|
(let ([e (current-eventspace)])
|
|
(when (eq? (current-thread) (eventspace-handler-thread e))
|
|
(let ([v (sync/timeout 0 ((eventspace-queue-proc e) #f #f #f))])
|
|
(if v
|
|
(begin (handle-event v e) #t)
|
|
#f)))))
|
|
|
|
(define yield-refresh
|
|
(lambda ()
|
|
(let ([e (current-eventspace)])
|
|
(and (eq? (current-thread) (eventspace-handler-thread e))
|
|
(let loop ([result #f])
|
|
(let ([v (sync/timeout 0 ((eventspace-queue-proc e) #t #f #t))])
|
|
(if v
|
|
(begin
|
|
(handle-event v e)
|
|
(loop #t))
|
|
result)))))))
|
|
|
|
(define (eventspace-event-evt [e (current-eventspace)])
|
|
(unless (eventspace? e)
|
|
(raise-type-error 'eventspace-event-evt "eventspace" e))
|
|
(wrap-evt ((eventspace-queue-proc e) #f #t #t)
|
|
(lambda (_) e)))
|
|
|
|
(define (main-eventspace? e)
|
|
(eq? e main-eventspace))
|
|
|
|
(define (queue-callback thunk [high? #t])
|
|
(let ([es (current-eventspace)])
|
|
(when (eventspace-shutdown? es)
|
|
(error 'queue-callback "eventspace is shutdown: ~e" es))
|
|
(queue-event es thunk (cond
|
|
[(not high?) 'lo]
|
|
[(eq? high? middle-queue-key) 'med]
|
|
[else 'hi]))))
|
|
|
|
(define middle-queue-key (gensym 'middle))
|
|
|
|
|
|
(define (add-timer-callback cb es)
|
|
;; in atomic mode
|
|
(queue-event es cb 'timer-add))
|
|
(define (remove-timer-callback cb es)
|
|
;; in atomic mode
|
|
(unless (eventspace-shutdown? es)
|
|
(queue-event es cb 'timer-remove)))
|
|
|
|
(define (register-frame-shown f on?)
|
|
(queue-event (send f get-eventspace) f (if on?
|
|
'frame-add
|
|
'frame-remove)))
|
|
|
|
(define (get-top-level-windows [e (current-eventspace)])
|
|
;; called in event-pump thread
|
|
(hash-map (eventspace-frames-hash e)
|
|
(lambda (k v) k)))
|
|
|
|
(define (other-modal? win [e #f] [ignore-win #f])
|
|
;; called in atomic mode in eventspace's thread
|
|
(and
|
|
;; deliver mouse-motion events even if a modal window
|
|
;; is open
|
|
(or (not e)
|
|
(not (or (send e leaving?)
|
|
(send e entering?)
|
|
(send e moving?))))
|
|
;; for any other kind of mouse or key event, deliver only
|
|
;; if no model dialog is open
|
|
(let ([es (send win get-eventspace)])
|
|
(or (positive? (eventspace-external-modal es))
|
|
(let loop ([frames (get-top-level-windows es)])
|
|
(and (pair? frames)
|
|
(let ([status (if (eq? ignore-win (car frames))
|
|
#f
|
|
(send (car frames) frame-relative-dialog-status win))])
|
|
(case status
|
|
[(#f) (loop (cdr frames))]
|
|
[(same) (loop (cdr frames))]
|
|
[(other) #t]))))))))
|
|
|
|
(define (eventspace-adjust-external-modal! es amt)
|
|
(atomically
|
|
(set-eventspace-external-modal!
|
|
es
|
|
(+ (eventspace-external-modal es) amt))))
|
|
|
|
(define (queue-quit-event)
|
|
;; called in event-pump thread
|
|
(queue-event main-eventspace (application-quit-handler) 'med))
|
|
|
|
(define (queue-prefs-event)
|
|
;; called in event-pump thread
|
|
(queue-event main-eventspace (application-pref-handler) 'med))
|
|
|
|
(define (queue-about-event)
|
|
;; called in event-pump thread
|
|
(queue-event main-eventspace (application-about-handler) 'med))
|
|
|
|
(define (queue-file-event file)
|
|
;; called in event-pump thread
|
|
(queue-event main-eventspace (lambda ()
|
|
((application-file-handler) file))
|
|
'med))
|
|
|
|
(define (queue-start-empty-event)
|
|
;; called in event-pump thread
|
|
(queue-event main-eventspace (application-start-empty-handler)
|
|
'med))
|
|
|
|
(define (begin-busy-cursor)
|
|
(let ([e (current-eventspace)])
|
|
(atomically
|
|
(set-eventspace-wait-cursor-count!
|
|
e
|
|
(add1 (eventspace-wait-cursor-count e)))
|
|
(when (= (eventspace-wait-cursor-count e) 1)
|
|
(for ([e (in-list (get-top-level-windows))])
|
|
(send e set-wait-cursor-mode #t))))))
|
|
|
|
(define (end-busy-cursor)
|
|
(let ([e (current-eventspace)])
|
|
(atomically
|
|
(set-eventspace-wait-cursor-count!
|
|
e
|
|
(sub1 (eventspace-wait-cursor-count e)))
|
|
(when (zero? (eventspace-wait-cursor-count e))
|
|
(for ([e (in-list (get-top-level-windows))])
|
|
(send e set-wait-cursor-mode #f))))))
|
|
|
|
(define (is-busy?) (positive? (eventspace-wait-cursor-count (current-eventspace))))
|
|
|
|
;; ----------------------------------------
|
|
|
|
;; Before exiting, wait until frames are closed, etc.:
|
|
(executable-yield-handler
|
|
(let ([old-eyh (executable-yield-handler)])
|
|
(lambda (v)
|
|
(yield main-eventspace)
|
|
(old-eyh v))))
|
|
|
|
;; When using a REPL in a thread that has an eventspace,
|
|
;; yield to events when the port would block.
|
|
(current-get-interaction-input-port
|
|
(let ([orig (current-get-interaction-input-port)])
|
|
(lambda ()
|
|
(let ([e (thread-cell-ref handler-thread-of)])
|
|
(if e
|
|
(let ([filter (lambda (v)
|
|
(cond
|
|
[(eq? v 0) (yield) 0]
|
|
[(evt? v)
|
|
(parameterize ([current-eventspace e])
|
|
(yield))
|
|
(choice-evt v
|
|
(wrap-evt (eventspace-event-evt e)
|
|
(lambda (_) 0)))]
|
|
[else v]))])
|
|
(filter-read-input-port
|
|
(orig)
|
|
(lambda (str v)
|
|
(filter v))
|
|
(lambda (s skip evt v)
|
|
(filter v))))
|
|
(orig))))))
|