
Fix `get-unscaled-client-size` for Win32 and GTK scaling, and make the docs more generally sensible.
880 lines
31 KiB
Racket
880 lines
31 KiB
Racket
#lang racket/base
|
|
(require ffi/unsafe
|
|
racket/class
|
|
racket/draw
|
|
racket/draw/unsafe/bstr
|
|
"../../syntax.rkt"
|
|
"../common/freeze.rkt"
|
|
"../common/queue.rkt"
|
|
"../common/event.rkt"
|
|
"../common/local.rkt"
|
|
"../../lock.rkt"
|
|
"utils.rkt"
|
|
"types.rkt"
|
|
"const.rkt"
|
|
"wndclass.rkt"
|
|
"queue.rkt"
|
|
"theme.rkt"
|
|
"cursor.rkt"
|
|
"key.rkt"
|
|
"dc.rkt"
|
|
"font.rkt")
|
|
|
|
(provide
|
|
(protect-out window%
|
|
queue-window-event
|
|
queue-window-refresh-event
|
|
location->window
|
|
flush-display
|
|
get-default-control-font
|
|
|
|
GetWindowRect
|
|
GetClientRect
|
|
|
|
_NMHDR))
|
|
|
|
(define (unhide-cursor) (void))
|
|
|
|
(define WM_PRINT #x0317)
|
|
(define WM_PRINTCLIENT #x0318)
|
|
|
|
(define MK_LBUTTON #x0001)
|
|
(define MK_RBUTTON #x0002)
|
|
(define MK_SHIFT #x0004)
|
|
(define MK_CONTROL #x0008)
|
|
(define MK_MBUTTON #x0010)
|
|
(define MK_XBUTTON1 #x0020)
|
|
(define MK_XBUTTON2 #x0040)
|
|
|
|
(define HTHSCROLL 6)
|
|
(define HTVSCROLL 7)
|
|
|
|
(define-user32 GetWindowRect (_wfun _HWND (rect : (_ptr o _RECT)) -> (r : _BOOL) ->
|
|
(if r rect (failed 'GetWindowRect))))
|
|
(define-user32 GetClientRect (_wfun _HWND (rect : (_ptr o _RECT)) -> (r : _BOOL) ->
|
|
(if r rect (failed 'GetClientRect))))
|
|
|
|
(define-user32 ClientToScreen (_wfun _HWND _POINT-pointer -> (r : _BOOL)
|
|
-> (unless r (failed 'ClientToScreen))))
|
|
(define-user32 ScreenToClient (_wfun _HWND _POINT-pointer -> (r : _BOOL)
|
|
-> (unless r (failed 'ClientToScreen))))
|
|
|
|
(define-gdi32 CreateFontIndirectW (_wfun _LOGFONTW-pointer -> _HFONT))
|
|
|
|
(define-shell32 DragAcceptFiles (_wfun _HWND _BOOL -> _void))
|
|
|
|
(define _HDROP _pointer)
|
|
(define-shell32 DragQueryFileW (_wfun _HDROP _UINT _pointer _UINT -> _UINT))
|
|
(define-shell32 DragFinish (_wfun _HDROP -> _void))
|
|
|
|
(define-user32 SetCapture (_wfun _HWND -> _HWND))
|
|
(define-user32 ReleaseCapture (_wfun -> _BOOL))
|
|
|
|
(define-user32 WindowFromPoint (_wfun _POINT -> _HWND))
|
|
(define-user32 GetParent (_wfun _HWND -> _HWND))
|
|
(define-user32 SetParent (_wfun _HWND _HWND -> (r : _HWND)
|
|
-> (unless r (failed 'SetParent))))
|
|
|
|
(define-user32 SetCursorPos (_wfun _int _int -> _BOOL))
|
|
|
|
(define-cstruct _NMHDR
|
|
([hwndFrom _HWND]
|
|
[idFrom _pointer]
|
|
[code _UINT]))
|
|
|
|
(define-user32 GetDialogBaseUnits (_fun -> _LONG))
|
|
(define measure-dc #f)
|
|
|
|
(define theme-hfont #f)
|
|
|
|
#;
|
|
(define-values (dlu-x dlu-y)
|
|
(let ([v (GetDialogBaseUnits)])
|
|
(values (* 1/4 (bitwise-and v #xFFFF))
|
|
(* 1/8 (arithmetic-shift v -16)))))
|
|
|
|
(define-cstruct _LOGBRUSH
|
|
([lbStyle _UINT]
|
|
[lbColor _COLORREF]
|
|
[lbHatch _pointer]))
|
|
|
|
(define BS_NULL 1)
|
|
(define transparent-logbrush (make-LOGBRUSH BS_NULL 0 #f))
|
|
|
|
(define-gdi32 CreateBrushIndirect (_wfun _LOGBRUSH-pointer -> _HBRUSH))
|
|
|
|
(define TRANSPARENT 1)
|
|
(define-gdi32 SetBkMode (_wfun _HDC _int -> (r : _int)
|
|
-> (when (zero? r) (failed 'SetBkMode))))
|
|
|
|
(define-user32 BeginPaint (_wfun _HWND _pointer -> _HDC))
|
|
(define-user32 EndPaint (_wfun _HDC _pointer -> _BOOL))
|
|
|
|
(define WM_IS_GRACKET (cast (scheme_register_process_global "PLT_WM_IS_GRACKET" #f)
|
|
_pointer
|
|
_UINT_PTR))
|
|
(define GRACKET_GUID (cast (scheme_register_process_global "PLT_GRACKET_GUID" #f)
|
|
_pointer
|
|
_bytes))
|
|
(define-cstruct _COPYDATASTRUCT
|
|
([dwData _pointer]
|
|
[cbData _DWORD]
|
|
[lpData _pointer]))
|
|
|
|
(define-cstruct _TRACKMOUSEEVENT
|
|
([cbSize _DWORD]
|
|
[dwFlags _DWORD]
|
|
[hwndTrack _HWND]
|
|
[dwHoverTime _DWORD]))
|
|
|
|
(define TME_LEAVE #x02)
|
|
(define TME_NONCLIENT #x10)
|
|
|
|
(define-user32 TrackMouseEvent (_wfun _TRACKMOUSEEVENT-pointer -> (r : _BOOL)
|
|
-> (unless r (failed 'TrackMouseEvent))))
|
|
(define-user32 GetCursorPos (_wfun _POINT-pointer -> _BOOL))
|
|
|
|
(defclass window% object%
|
|
(init-field parent hwnd)
|
|
(init style
|
|
[extra-hwnds null])
|
|
|
|
(define enabled? #t)
|
|
(define parent-enabled? #t)
|
|
|
|
(super-new)
|
|
|
|
(define eventspace (if parent
|
|
(send parent get-eventspace)
|
|
(current-eventspace)))
|
|
|
|
(set-hwnd-wx! hwnd this)
|
|
(for ([extra-hwnd (in-list extra-hwnds)])
|
|
(set-hwnd-wx! extra-hwnd this))
|
|
|
|
(define/public (get-hwnd) hwnd)
|
|
(define/public (get-client-hwnd) hwnd)
|
|
(define/public (get-content-hwnd) (get-client-hwnd))
|
|
(define/public (get-focus-hwnd) hwnd)
|
|
(define/public (get-eventspace) eventspace)
|
|
|
|
(define/public (is-hwnd? a-hwnd)
|
|
(ptr-equal? hwnd a-hwnd))
|
|
|
|
(define/public (wndproc w msg wParam lParam default)
|
|
(if (try-mouse w msg wParam lParam)
|
|
0
|
|
(cond
|
|
[(= msg WM_SETFOCUS)
|
|
(set-top-focus this null w)
|
|
(queue-window-event this (lambda () (on-set-focus)))
|
|
0]
|
|
[(= msg WM_KILLFOCUS)
|
|
(queue-window-event this (lambda () (on-kill-focus)))
|
|
0]
|
|
[(= msg WM_SYSKEYDOWN)
|
|
(let ([result (if (or (= wParam VK_MENU) (= wParam VK_F4)) ;; F4 is close
|
|
(begin
|
|
(unhide-cursor)
|
|
(default w msg wParam lParam))
|
|
0)])
|
|
(do-key w msg wParam lParam #f #f void)
|
|
result)]
|
|
[(= msg WM_KEYDOWN)
|
|
(do-key w msg wParam lParam #f #f default)]
|
|
[(= msg WM_KEYUP)
|
|
(do-key w msg wParam lParam #f #t default)]
|
|
[(= msg WM_SYSCHAR)
|
|
(let ([result (if (= wParam VK_MENU)
|
|
(begin
|
|
(unhide-cursor)
|
|
(default w msg wParam lParam))
|
|
0)])
|
|
(do-key w msg wParam lParam #t #f void)
|
|
result)]
|
|
[(= msg WM_CHAR)
|
|
(do-key w msg wParam lParam #t #f default)]
|
|
[(= msg WM_MOUSEWHEEL)
|
|
(gen-wheels w msg lParam (HIWORD wParam) 'wheel-down 'wheel-up)
|
|
0]
|
|
[(= msg WM_MOUSEHWHEEL) ; Vista and later
|
|
(gen-wheels w msg lParam (HIWORD wParam) 'wheel-left 'wheel-right)
|
|
0]
|
|
[(= msg WM_COMMAND)
|
|
(let* ([control-hwnd (cast lParam _LPARAM _HWND)]
|
|
[wx (any-hwnd->wx control-hwnd)]
|
|
[cmd (HIWORD wParam)])
|
|
(if (and wx (send wx is-command? cmd))
|
|
(begin
|
|
(send wx do-command cmd control-hwnd)
|
|
0)
|
|
(default w msg wParam lParam)))]
|
|
[(= msg WM_NOTIFY)
|
|
(let* ([nmhdr (cast lParam _LPARAM _NMHDR-pointer)]
|
|
[control-hwnd (NMHDR-hwndFrom nmhdr)]
|
|
[wx (any-hwnd->wx control-hwnd)]
|
|
[cmd (LOWORD (NMHDR-code nmhdr))])
|
|
(if (and wx (send wx is-command? cmd))
|
|
(begin
|
|
(send wx do-command-ex cmd control-hwnd nmhdr)
|
|
0)
|
|
(default w msg wParam lParam)))]
|
|
[(or (= msg WM_HSCROLL)
|
|
(= msg WM_VSCROLL))
|
|
(let* ([control-hwnd (cast lParam _LPARAM _HWND)]
|
|
[wx (any-hwnd->wx control-hwnd)])
|
|
(if wx
|
|
(begin
|
|
(send wx control-scrolled)
|
|
0)
|
|
(default w msg wParam lParam)))]
|
|
[(= msg WM_DROPFILES)
|
|
(handle-drop-files wParam)
|
|
0]
|
|
;; for single-instance applications:
|
|
[(and (= msg WM_IS_GRACKET)
|
|
(positive? WM_IS_GRACKET))
|
|
;; return 79 to indicate that this is a GRacket window
|
|
79]
|
|
;; also for single-instance:
|
|
[(= msg WM_COPYDATA)
|
|
(handle-copydata lParam)
|
|
0]
|
|
[(= msg WM_INPUTLANGCHANGE)
|
|
(reset-key-mapping)
|
|
0]
|
|
[else
|
|
(default w msg wParam lParam)])))
|
|
|
|
(define/public (is-command? cmd) #f)
|
|
(define/public (control-scrolled) #f)
|
|
|
|
(define/public (do-command cmd control-hwnd)
|
|
(void))
|
|
(define/public (do-command-ex cmd control-hwnd nmhdr)
|
|
(do-command cmd control-hwnd))
|
|
|
|
(define/public (show on?)
|
|
(when on? (show-children))
|
|
(atomically (direct-show on?)))
|
|
|
|
(define shown? #f)
|
|
(define/public (direct-show on? [on-mode SW_SHOW])
|
|
;; atomic mode
|
|
(set! shown? (and on? #t))
|
|
(register-child-in-parent on?)
|
|
(unless on? (not-focus-child this))
|
|
(ShowWindow hwnd (if on? on-mode SW_HIDE)))
|
|
(unless (memq 'deleted style)
|
|
(show #t))
|
|
|
|
(define/public (queue-on-size) (void))
|
|
|
|
(define/public (on-set-focus) (void))
|
|
(define/public (on-kill-focus) (void))
|
|
(define/public (get-handle) hwnd)
|
|
(define/public (get-client-handle) (get-content-hwnd))
|
|
|
|
(define/public (enable on?)
|
|
(unless (eq? enabled? (and on? #t))
|
|
(atomically
|
|
(let ([prev? (and enabled? parent-enabled?)])
|
|
(set! enabled? (and on? #t))
|
|
(let ([now? (and parent-enabled? enabled?)])
|
|
(unless (eq? now? prev?)
|
|
(internal-enable now?)))))))
|
|
(define/public (parent-enable on?)
|
|
(unless (eq? on? parent-enabled?)
|
|
(let ([prev? (and enabled? parent-enabled?)])
|
|
(set! parent-enabled? (and on? #t))
|
|
(let ([now? (and parent-enabled? enabled?)])
|
|
(unless (eq? prev? now?)
|
|
(internal-enable now?))))))
|
|
(define/public (internal-enable on?)
|
|
(void (EnableWindow hwnd on?)))
|
|
|
|
(define/public (is-window-enabled?) enabled?)
|
|
(define/public (is-enabled-to-root?)
|
|
(and enabled? parent-enabled?))
|
|
|
|
(define/public (is-shown-to-root?)
|
|
(and shown?
|
|
(send parent is-shown-to-root?)))
|
|
|
|
(define/public (is-shown?)
|
|
shown?)
|
|
|
|
(define/public (get-x)
|
|
(let ([r (GetWindowRect hwnd)]
|
|
[pr (GetWindowRect (send parent get-content-hwnd))])
|
|
(->normal (- (RECT-left r) (RECT-left pr)))))
|
|
(define/public (get-y)
|
|
(let ([r (GetWindowRect hwnd)]
|
|
[pr (GetWindowRect (send parent get-content-hwnd))])
|
|
(->normal (- (RECT-top r) (RECT-top pr)))))
|
|
|
|
(define/public (get-width)
|
|
(let ([r (GetWindowRect hwnd)])
|
|
(->normal (- (RECT-right r) (RECT-left r)))))
|
|
(define/public (get-height)
|
|
(let ([r (GetWindowRect hwnd)])
|
|
(->normal (- (RECT-bottom r) (RECT-top r)))))
|
|
|
|
(define/public (notify-child-extent x y)
|
|
(void))
|
|
|
|
;; Converting from normalized to screen coordinates
|
|
;; with just `->screen` can cause a child's right edge
|
|
;; to extend beyond the parent's right edge, due to
|
|
;; rounding via `ceiling`. Allow controls that would
|
|
;; look bad to round down, instead.
|
|
(define/public (size->screen v) (->screen v))
|
|
|
|
(define/public (set-size x y w h)
|
|
(let-values ([(x y w h)
|
|
(if (or (not x)
|
|
(not y)
|
|
(= w -1)
|
|
(= h -1))
|
|
(let ([r (GetWindowRect hwnd)])
|
|
(values (or x (->normal (RECT-left r)))
|
|
(or y (->normal (RECT-top r)))
|
|
(if (= w -1) (->normal (- (RECT-right r) (RECT-left r))) w)
|
|
(if (= h -1) (->normal (- (RECT-bottom r) (RECT-top r))) h)))
|
|
(values x y w h))])
|
|
(when parent (send parent notify-child-extent (+ x w) (+ y h)))
|
|
(MoveWindow hwnd (->screen x) (->screen y) (size->screen w) (size->screen h) #t))
|
|
(unless (and (= w -1) (= h -1))
|
|
(on-resized))
|
|
(queue-on-size)
|
|
(refresh-one))
|
|
(define/public (move x y)
|
|
(set-size x y -1 -1))
|
|
|
|
(define/public (set-control-font font [hwnd hwnd])
|
|
(unless theme-hfont
|
|
(set! theme-hfont (CreateFontIndirectW (get-theme-logfont))))
|
|
(let ([hfont (if font
|
|
(or (font->hfont font)
|
|
theme-hfont)
|
|
theme-hfont)])
|
|
(SendMessageW hwnd WM_SETFONT (cast hfont _HFONT _LPARAM) 0)))
|
|
|
|
(define/public (auto-size font label min-w min-h dw dh
|
|
[resize
|
|
(lambda (w h) (set-size #f #f w h))]
|
|
#:combine-width [combine-w max]
|
|
#:combine-height [combine-h max]
|
|
#:scale-w [scale-w 1]
|
|
#:scale-h [scale-h 1])
|
|
(atomically
|
|
(unless measure-dc
|
|
(let* ([bm (make-object win32-bitmap% 1 1 #f)]
|
|
[dc (make-object bitmap-dc% bm)])
|
|
(set! measure-dc dc)))
|
|
(send measure-dc set-font (or font
|
|
(get-default-control-font)))
|
|
(let-values ([(w h d a) (let loop ([label label])
|
|
(cond
|
|
[(null? label) (values 0 0 0 0)]
|
|
[(label . is-a? . bitmap%)
|
|
(values (send label get-width)
|
|
(send label get-height)
|
|
0
|
|
0)]
|
|
[(pair? label)
|
|
(let-values ([(w1 h1 d1 a1)
|
|
(loop (car label))]
|
|
[(w2 h2 d2 a2)
|
|
(loop (cdr label))])
|
|
(values (combine-w w1 w2) (combine-h h1 h2)
|
|
(combine-h d1 d1) (combine-h a1 a2)))]
|
|
[else
|
|
(define strs (regexp-split #rx"\n" label))
|
|
(for/fold ([w 0][h 0][d 0] [a 0]) ([str (in-list strs)])
|
|
(define-values (tw th d a) (send measure-dc get-text-extent label #f #t))
|
|
(values (max w tw) (+ h th) 0 0))]))]
|
|
[(->int) (lambda (v) (inexact->exact (ceiling v)))])
|
|
(resize (->int (* scale-h (max (+ w dw) min-w)))
|
|
(->int (* scale-w (max (+ h dh) min-h)))))))
|
|
|
|
(define/public (popup-menu m x y)
|
|
(let ([gx (box x)]
|
|
[gy (box y)])
|
|
(client-to-screen gx gy)
|
|
(send m popup (unbox gx) (unbox gy)
|
|
hwnd
|
|
(lambda (thunk) (queue-window-event this thunk)))))
|
|
|
|
(define/public (center a b) (void))
|
|
|
|
(define/public (get-parent) parent)
|
|
(define/public (set-parent p)
|
|
;; in atomic mode
|
|
(set! parent p)
|
|
(SetParent hwnd (send parent get-content-hwnd)))
|
|
|
|
(define/public (is-frame?) #f)
|
|
|
|
(define/public (refresh-one) (void))
|
|
(define/public (refresh)
|
|
(refresh-one)
|
|
(refresh-all-children))
|
|
(define/public (refresh-all-children) (void))
|
|
|
|
(define/public (on-resized) (void))
|
|
|
|
(define event-position-wrt-wx #f)
|
|
(define/public (set-event-positions-wrt wx)
|
|
(set! event-position-wrt-wx wx))
|
|
|
|
(define/private (adjust-event-position x y)
|
|
(if event-position-wrt-wx
|
|
(let ([xb (box x)]
|
|
[yb (box y)])
|
|
(internal-client-to-screen xb yb)
|
|
(send event-position-wrt-wx internal-screen-to-client xb yb)
|
|
(values (unbox xb) (unbox yb)))
|
|
(values x y)))
|
|
|
|
(define/public (screen-to-client x y)
|
|
(internal-screen-to-client x y))
|
|
(define/public (internal-screen-to-client x y)
|
|
(let ([p (make-POINT (->screen (unbox x)) (->screen (unbox y)))])
|
|
(ScreenToClient (get-client-hwnd) p)
|
|
(set-box! x (->normal (POINT-x p)))
|
|
(set-box! y (->normal (POINT-y p)))))
|
|
(define/public (client-to-screen x y)
|
|
(internal-client-to-screen x y))
|
|
(define/public (internal-client-to-screen x y)
|
|
(let ([p (make-POINT (->screen (unbox x)) (->screen (unbox y)))])
|
|
(ClientToScreen (get-client-hwnd) p)
|
|
(set-box! x (->normal (POINT-x p)))
|
|
(set-box! y (->normal (POINT-y p)))))
|
|
|
|
(define/public (warp-pointer x y)
|
|
(define xb (box x))
|
|
(define yb (box y))
|
|
(client-to-screen xb yb)
|
|
(void (SetCursorPos (->screen (unbox xb)) (->screen (unbox yb)))))
|
|
|
|
(define/public (in-content? p)
|
|
(ScreenToClient (get-client-hwnd) p)
|
|
(let ([r (GetClientRect (get-client-hwnd))])
|
|
(and (< 0 (POINT-x p) (- (RECT-right r)
|
|
(RECT-left r)))
|
|
(< 0 (POINT-y p) (- (RECT-bottom r)
|
|
(RECT-top r))))))
|
|
|
|
(define/public (drag-accept-files on?)
|
|
(DragAcceptFiles (get-hwnd) on?))
|
|
|
|
(define/private (handle-drop-files wParam)
|
|
(let* ([hdrop (cast wParam _WPARAM _HDROP)]
|
|
[count (DragQueryFileW hdrop #xFFFFFFFF #f 0)])
|
|
(for ([i (in-range count)])
|
|
(let* ([len (DragQueryFileW hdrop i #f 0)]
|
|
[b (malloc (add1 len) _int16)])
|
|
(DragQueryFileW hdrop i b (add1 len))
|
|
(let ([s (cast b _gcpointer _string/utf-16)])
|
|
(queue-window-event this (lambda () (on-drop-file (string->path s)))))))
|
|
(DragFinish hdrop)))
|
|
|
|
(define/public (on-drop-file p) (void))
|
|
|
|
(define/public (get-client-size w h)
|
|
(define-values (uw uh) (get-scaled-client-size))
|
|
(set-box! w (->normal uw))
|
|
(set-box! h (->normal uh)))
|
|
|
|
(define/public (get-scaled-client-size)
|
|
(let ([r (GetClientRect (get-client-hwnd))])
|
|
(values (- (RECT-right r) (RECT-left r))
|
|
(- (RECT-bottom r) (RECT-top r)))))
|
|
|
|
(define/public (get-size w h)
|
|
(let ([r (GetWindowRect (get-client-hwnd))])
|
|
(set-box! w (->normal (- (RECT-right r) (RECT-left r))))
|
|
(set-box! h (->normal (- (RECT-bottom r) (RECT-top r))))))
|
|
|
|
(define cursor-handle #f)
|
|
(define/public (set-cursor c)
|
|
(set! cursor-handle (and c (send (send c get-driver) get-handle)))
|
|
(when mouse-in?
|
|
(cursor-updated-here)))
|
|
|
|
(define/public (cursor-updated-here)
|
|
(when mouse-in?
|
|
(send (get-top-frame) reset-cursor (get-arrow-cursor))))
|
|
|
|
(define/public (reset-cursor-in-child child default)
|
|
(send child reset-cursor (or cursor-handle default)))
|
|
|
|
(define effective-cursor-handle #f)
|
|
(define/public (reset-cursor default)
|
|
(let ([c (or cursor-handle default)])
|
|
(set! effective-cursor-handle c)
|
|
(SetCursor c)))
|
|
|
|
(define/public (no-cursor-handle-here)
|
|
(send parent cursor-updated-here))
|
|
|
|
(define/public (set-focus [child-hwnd hwnd])
|
|
(when (can-accept-focus?)
|
|
(set-top-focus this null child-hwnd)))
|
|
|
|
(define/public (can-accept-focus?)
|
|
(child-can-accept-focus?))
|
|
|
|
(define/public (child-can-accept-focus?)
|
|
(and shown?
|
|
(send parent child-can-accept-focus?)))
|
|
|
|
(define/public (set-top-focus win win-path hwnd)
|
|
(send parent set-top-focus win (cons this win-path) hwnd))
|
|
(define/public (not-focus-child v)
|
|
(send parent not-focus-child v))
|
|
|
|
(define/public (gets-focus?) #f)
|
|
|
|
(define/public (register-child child on?)
|
|
(void))
|
|
(define/public (register-child-in-parent on?)
|
|
(when parent
|
|
(send parent register-child this on?)))
|
|
|
|
(define/public (show-children) (void))
|
|
(define/public (paint-children) (void))
|
|
|
|
(define/public (get-top-frame)
|
|
(send parent get-top-frame))
|
|
|
|
(define/private (gen-wheels w msg lParam val down up)
|
|
(let ([orig-delta (quotient val WHEEL_DELTA)])
|
|
(let loop ([delta (abs orig-delta)])
|
|
(unless (zero? delta)
|
|
(do-key w msg (if (negative? orig-delta)
|
|
down
|
|
up)
|
|
lParam #f #f void)
|
|
(loop (sub1 delta))))))
|
|
|
|
(define/private (do-key w msg wParam lParam is-char? is-up? default)
|
|
(let ([e (maybe-make-key-event #f wParam lParam is-char? is-up? hwnd)])
|
|
(if (or (and e
|
|
(if (definitely-wants-event? w msg wParam e)
|
|
(begin
|
|
(queue-window-event this (lambda () (dispatch-on-char/sync e)))
|
|
#t)
|
|
(constrained-reply eventspace
|
|
(lambda () (dispatch-on-char e #t))
|
|
#t)))
|
|
(capture-all-key-events?))
|
|
0
|
|
(default w msg wParam lParam))))
|
|
|
|
(define/public (try-mouse w msg wParam lParam)
|
|
(cond
|
|
[(= msg WM_RBUTTONDOWN)
|
|
(do-mouse w msg #f 'right-down wParam lParam)]
|
|
[(= msg WM_RBUTTONUP)
|
|
(do-mouse w msg #f 'right-up wParam lParam)]
|
|
[(= msg WM_RBUTTONDBLCLK)
|
|
(do-mouse w msg #f 'right-down wParam lParam)]
|
|
[(= msg WM_MBUTTONDOWN)
|
|
(do-mouse w msg #f 'middle-down wParam lParam)]
|
|
[(= msg WM_MBUTTONUP)
|
|
(do-mouse w msg #f 'middle-up wParam lParam)]
|
|
[(= msg WM_MBUTTONDBLCLK)
|
|
(do-mouse w msg #f 'middle-down wParam lParam)]
|
|
[(= msg WM_LBUTTONDOWN)
|
|
(do-mouse w msg #f 'left-down wParam lParam)]
|
|
[(= msg WM_LBUTTONUP)
|
|
(do-mouse w msg #f 'left-up wParam lParam)]
|
|
[(= msg WM_LBUTTONDBLCLK)
|
|
(do-mouse w msg #f 'left-down wParam lParam)]
|
|
[(= msg WM_MOUSEMOVE)
|
|
(do-mouse w msg #f 'motion wParam lParam)]
|
|
[(= msg WM_MOUSELEAVE)
|
|
(let ([p (make-POINT 0 0)])
|
|
(let ([f (and (GetCursorPos p)
|
|
(location->window* (POINT-x p) (POINT-y p)))])
|
|
(unless (and (eq? f (get-top-frame))
|
|
(send f in-content? p))
|
|
(do-mouse w msg #f 'leave wParam lParam))))
|
|
;; send message on to default handling (e.g., for buttons):
|
|
#f]
|
|
[else (try-nc-mouse w msg wParam lParam)]))
|
|
|
|
;; Breaking out NC mouse operations lets us not handle
|
|
;; them for frames (where this method is overridden),
|
|
;; since handling them intereferes with the cursor and
|
|
;; resize handling for frames.
|
|
(define/public (try-nc-mouse w msg wParam lParam)
|
|
(cond
|
|
[(= msg WM_NCRBUTTONDOWN)
|
|
(do-mouse w msg #t 'right-down wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCRBUTTONUP)
|
|
(do-mouse w msg #t 'right-up wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCRBUTTONDBLCLK)
|
|
(do-mouse w msg #t 'right-down wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCMBUTTONDOWN)
|
|
(do-mouse w msg #t 'middle-down wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCMBUTTONUP)
|
|
(do-mouse w msg #t 'middle-up wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCMBUTTONDBLCLK)
|
|
(do-mouse w msg #t 'middle-down wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCLBUTTONDOWN)
|
|
(do-mouse w msg #t 'left-down wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCLBUTTONUP)
|
|
(do-mouse w msg #t 'left-up wParam (lp-screen->client w lParam))]
|
|
[(= msg WM_NCLBUTTONDBLCLK)
|
|
(do-mouse w msg #t 'left-down wParam (lp-screen->client w lParam))]
|
|
[(and (= msg WM_NCMOUSEMOVE)
|
|
(not (= wParam HTVSCROLL))
|
|
(not (= wParam HTHSCROLL)))
|
|
(do-mouse w msg #t 'motion wParam (lp-screen->client w lParam))]
|
|
[else #f]))
|
|
|
|
(define/private (lp-screen->client w lParam)
|
|
(let ([p (make-POINT (LOWORD lParam) (HIWORD lParam))])
|
|
(ScreenToClient w p)
|
|
(MAKELPARAM (POINT-x p) (POINT-y p))))
|
|
|
|
(define/private (do-mouse control-hwnd msg nc? type wParam lParam)
|
|
(let ([x (LOWORD lParam)]
|
|
[y (HIWORD lParam)]
|
|
[flags (if nc? 0 wParam)]
|
|
[bit? (lambda (v b) (not (zero? (bitwise-and v b))))])
|
|
(let ([make-e
|
|
(lambda (type)
|
|
(define-values (mx my) (adjust-event-position (->normal x) (->normal y)))
|
|
(new mouse-event%
|
|
[event-type type]
|
|
[left-down (case type
|
|
[(left-down) #t]
|
|
[(left-up) #f]
|
|
[else (bit? flags MK_LBUTTON)])]
|
|
[middle-down (case type
|
|
[(middle-down) #t]
|
|
[(middle-up) #f]
|
|
[else (bit? flags MK_MBUTTON)])]
|
|
[right-down (case type
|
|
[(right-down) #t]
|
|
[(right-up) #f]
|
|
[else (bit? flags MK_RBUTTON)])]
|
|
[x mx]
|
|
[y my]
|
|
[shift-down (bit? flags MK_SHIFT)]
|
|
[control-down (bit? flags MK_CONTROL)]
|
|
[meta-down #f]
|
|
[alt-down #f]
|
|
[time-stamp 0]
|
|
[caps-down #f]))])
|
|
(if (eq? type 'leave)
|
|
(let ([t (get-top-frame)])
|
|
(send t send-child-leaves make-e)
|
|
(send t send-leaves make-e))
|
|
(begin
|
|
(unless nc?
|
|
(when (wants-mouse-capture? control-hwnd)
|
|
(when (memq type '(left-down right-down middle-down))
|
|
(SetCapture control-hwnd))
|
|
(when (memq type '(left-up right-up middle-up))
|
|
(ReleaseCapture))))
|
|
(if mouse-in?
|
|
(if (send-child-leaves make-e)
|
|
(cursor-updated-here)
|
|
(if (send (get-top-frame) is-wait-cursor-on?)
|
|
(void (SetCursor (get-wait-cursor)))
|
|
(when effective-cursor-handle
|
|
(void (SetCursor effective-cursor-handle)))))
|
|
(let ([c (generate-mouse-ins this (lambda (type) (make-e type)))])
|
|
(TrackMouseEvent (make-TRACKMOUSEEVENT
|
|
(ctype-sizeof _TRACKMOUSEEVENT)
|
|
(bitwise-ior TME_LEAVE)
|
|
control-hwnd
|
|
0))
|
|
(when c
|
|
(set! effective-cursor-handle c)
|
|
(void (SetCursor (if (send (get-top-frame) is-wait-cursor-on?)
|
|
(get-wait-cursor)
|
|
c))))))
|
|
(when (memq type '(left-down right-down middle-down))
|
|
(set-focus))
|
|
(handle-mouse-event control-hwnd msg wParam (make-e type)))))))
|
|
|
|
(define/private (handle-mouse-event w msg wParam e)
|
|
(if (definitely-wants-event? w msg wParam e)
|
|
(begin
|
|
(queue-window-event this (lambda () (dispatch-on-event/sync e)))
|
|
#t)
|
|
(constrained-reply eventspace
|
|
(lambda () (dispatch-on-event e #t))
|
|
#t)))
|
|
|
|
(define skip-enter-leave? #f)
|
|
(define/public (skip-enter-leave-events skip?)
|
|
(set! skip-enter-leave? skip?))
|
|
|
|
(define mouse-in? #f)
|
|
(define/public (generate-mouse-ins in-window mk)
|
|
(if mouse-in?
|
|
effective-cursor-handle
|
|
(begin
|
|
(set! mouse-in? #t)
|
|
(let ([parent-cursor (generate-parent-mouse-ins mk)])
|
|
(unless skip-enter-leave?
|
|
(handle-mouse-event (get-client-hwnd) 0 0 (mk 'enter)))
|
|
(let ([c (or cursor-handle parent-cursor)])
|
|
(set! effective-cursor-handle c)
|
|
c)))))
|
|
|
|
(define/public (generate-parent-mouse-ins mk)
|
|
(send parent generate-mouse-ins this mk))
|
|
|
|
(define/public (send-leaves mk)
|
|
(when mouse-in?
|
|
(set! mouse-in? #f)
|
|
(unless skip-enter-leave?
|
|
(when mk
|
|
(let ([e (mk 'leave)])
|
|
(if (eq? (current-thread)
|
|
(eventspace-handler-thread eventspace))
|
|
(handle-mouse-event (get-client-hwnd) 0 0 e)
|
|
(queue-window-event this
|
|
(lambda () (dispatch-on-event/sync e)))))))))
|
|
|
|
(define/public (send-child-leaves mk)
|
|
#f)
|
|
|
|
(define/public (wants-mouse-capture? control-hwnd)
|
|
#f)
|
|
|
|
(define/public (definitely-wants-event? w msg wParam e)
|
|
#f)
|
|
(define/public (capture-all-key-events?)
|
|
#f)
|
|
|
|
(define/public (dispatch-on-char/sync e)
|
|
(pre-event-refresh #t)
|
|
(dispatch-on-char e #f))
|
|
(define/public (dispatch-on-char e just-pre?)
|
|
(cond
|
|
[(other-modal? this) #t]
|
|
[(call-pre-on-char this e) #t]
|
|
[just-pre? #f]
|
|
[else (when (is-enabled-to-root?) (on-char e)) #t]))
|
|
|
|
(define/public (dispatch-on-event/sync e)
|
|
(pre-event-refresh #f)
|
|
(dispatch-on-event e #f))
|
|
(define/public (dispatch-on-event e just-pre?)
|
|
(cond
|
|
[(other-modal? this e) #t]
|
|
[(call-pre-on-event this e) #t]
|
|
[just-pre? #f]
|
|
[else (when (is-enabled-to-root?) (on-event e)) #t]))
|
|
|
|
(define/public (call-pre-on-event w e)
|
|
(or (send parent call-pre-on-event w e)
|
|
(pre-on-event w e)))
|
|
(define/public (call-pre-on-char w e)
|
|
(or (send parent call-pre-on-char w e)
|
|
(pre-on-char w e)))
|
|
(define/public (pre-on-event w e) #f)
|
|
(define/public (pre-on-char w e) #f)
|
|
|
|
(define/public (on-char e) (void))
|
|
(define/public (on-event e) (void))
|
|
|
|
(define/private (pre-event-refresh key?)
|
|
;; Since we break the connection between the
|
|
;; Win32 queue and event handling, we
|
|
;; re-sync the display in case a stream of
|
|
;; events (e.g., key repeat) have a corresponding
|
|
;; stream of screen updates.
|
|
(flush-display))
|
|
|
|
(define/public (get-dialog-level) (send parent get-dialog-level)))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(define (handle-copydata lParam)
|
|
(let* ([cd (cast lParam _LPARAM _COPYDATASTRUCT-pointer)]
|
|
[data (COPYDATASTRUCT-lpData cd)]
|
|
[guid-len (bytes-length GRACKET_GUID)]
|
|
[data-len (COPYDATASTRUCT-cbData cd)])
|
|
(when (and (data-len
|
|
. >= .
|
|
(+ guid-len 4 (ctype-sizeof _DWORD)))
|
|
(bytes=? GRACKET_GUID
|
|
(scheme_make_sized_byte_string data
|
|
guid-len
|
|
0))
|
|
(bytes=? #"OPEN"
|
|
(scheme_make_sized_byte_string (ptr-add data guid-len)
|
|
4
|
|
0)))
|
|
;; The command line's argv (sans argv[0]) is
|
|
;; expressed as a DWORD for the number of args,
|
|
;; followed by each arg. Each arg is a DWORD
|
|
;; for the number of chars and then the chars
|
|
(let ([args
|
|
(let ([count (ptr-ref data _DWORD 'abs (+ guid-len 4))])
|
|
(let loop ([i 0] [delta (+ guid-len 4 (ctype-sizeof _DWORD))])
|
|
(if (or (= i count)
|
|
((+ delta (ctype-sizeof _DWORD)) . > . data-len))
|
|
null
|
|
(let ([len (ptr-ref (ptr-add data delta) _DWORD)]
|
|
[delta (+ delta (ctype-sizeof _DWORD))])
|
|
(if ((+ delta len) . > . data-len)
|
|
null
|
|
(let ([s (scheme_make_sized_byte_string
|
|
(ptr-add data delta)
|
|
len
|
|
1)])
|
|
(if (or (bytes=? s #"")
|
|
(regexp-match? #rx"\0" s))
|
|
null
|
|
(cons (bytes->path s)
|
|
(loop (add1 i) (+ delta len))))))))))])
|
|
(map queue-file-event args)))))
|
|
|
|
;; ----------------------------------------
|
|
|
|
(define default-control-font #f)
|
|
(define (get-default-control-font)
|
|
(unless default-control-font
|
|
(set! default-control-font
|
|
(make-object font%
|
|
(->normal (get-theme-font-size))
|
|
(logfont->pango-family
|
|
(get-theme-logfont))
|
|
'system
|
|
'normal 'normal #f 'default
|
|
#t)))
|
|
default-control-font)
|
|
|
|
(define (queue-window-event win thunk)
|
|
(queue-event (send win get-eventspace) thunk))
|
|
|
|
(define (queue-window-refresh-event win thunk)
|
|
(queue-refresh-event (send win get-eventspace) thunk))
|
|
|
|
;; arguments in screen coordinates
|
|
(define (location->window* x y)
|
|
(let ([hwnd (WindowFromPoint (make-POINT x y))])
|
|
(let loop ([hwnd hwnd])
|
|
(and hwnd
|
|
(or (let ([wx (any-hwnd->wx hwnd)])
|
|
(and wx (send wx get-top-frame)))
|
|
(loop (GetParent hwnd)))))))
|
|
|
|
(define (location->window x y)
|
|
(location->window* (->screen x) (->screen y)))
|
|
|
|
(define (flush-display)
|
|
(atomically
|
|
(pre-event-sync #t)))
|