189 lines
6.0 KiB
Racket
189 lines
6.0 KiB
Racket
#lang racket/base
|
|
(require ffi/unsafe
|
|
ffi/winapi
|
|
racket/class
|
|
"utils.rkt"
|
|
"types.rkt"
|
|
"gl-context.rkt"
|
|
"../../lock.rkt"
|
|
"../common/backing-dc.rkt"
|
|
"../common/delay.rkt"
|
|
racket/draw/unsafe/cairo
|
|
racket/draw/private/dc
|
|
racket/draw/private/bitmap
|
|
racket/draw/private/local
|
|
ffi/unsafe/alloc)
|
|
|
|
(provide
|
|
(protect-out dc%
|
|
win32-bitmap%
|
|
do-backing-flush
|
|
request-flush-delay
|
|
cancel-flush-delay))
|
|
|
|
(define-gdi32 SelectClipRgn (_wfun _pointer _pointer -> _int))
|
|
|
|
(define-gdi32 GetClipBox (_wfun _pointer _RECT-pointer -> _int))
|
|
(define SIMPLEREGION 2)
|
|
|
|
(define-gdi32 BitBlt (_wfun _pointer _int _int _int _int _pointer _int _int _DWORD -> _BOOL))
|
|
(define SRCCOPY #X00cc0020)
|
|
|
|
(define hwnd-param (make-parameter #f))
|
|
|
|
(define need-clip-text-workaround? #t)
|
|
(define need-clip-refresh-workaround? #f) ; patched Cairo
|
|
|
|
(define win32-bitmap%
|
|
(class win32-no-hwnd-bitmap%
|
|
(init w h hwnd [gl-config #f])
|
|
(inherit get-cairo-surface)
|
|
(parameterize ([hwnd-param hwnd])
|
|
(super-new [w w] [h h] [backing-scale (->screen 1.0)]))
|
|
|
|
(define/override (build-cairo-surface w h backing-scale)
|
|
(define hwnd (hwnd-param))
|
|
(if hwnd
|
|
(atomically
|
|
(let ([hdc (GetDC hwnd)])
|
|
(begin0
|
|
(let ([sw (inexact->exact (floor (* backing-scale w)))]
|
|
[sh (inexact->exact (floor (* backing-scale h)))])
|
|
(cairo_win32_surface_create_with_ddb hdc
|
|
CAIRO_FORMAT_RGB24 sw sh))
|
|
(ReleaseDC hwnd hdc))))
|
|
(super build-cairo-surface w h backing-scale)))
|
|
|
|
(define gl (and gl-config
|
|
(let ([hdc (cairo_win32_surface_get_dc (get-cairo-surface))])
|
|
(set-cpointer-tag! hdc 'HDC)
|
|
(create-gl-context hdc
|
|
gl-config
|
|
#t))))
|
|
(define/override (get-bitmap-gl-context) gl)))
|
|
|
|
(define dc%
|
|
(class backing-dc%
|
|
(init [(cnvs canvas)]
|
|
transparent?)
|
|
(inherit end-delay)
|
|
(define canvas cnvs)
|
|
|
|
(super-new [transparent? transparent?])
|
|
|
|
(inherit internal-get-bitmap)
|
|
(define/override (reset-clip cr)
|
|
(super reset-clip cr)
|
|
;; Work around a Cairo(?) bug. When a clipping
|
|
;; region is set, we draw text, and then the clipping
|
|
;; region is changed, the change doesn't take
|
|
;; until we draw more text --- but only with DDB surfaces.
|
|
(when need-clip-text-workaround?
|
|
(let ([bm (internal-get-bitmap)])
|
|
(when (bm . is-a? . win32-bitmap%)
|
|
(SelectClipRgn (cairo_win32_surface_get_dc
|
|
(send bm get-cairo-surface))
|
|
#f)))))
|
|
|
|
(define gl #f)
|
|
(define/override (get-gl-context)
|
|
(or gl
|
|
(let ([v (create-gl-context (GetDC (send canvas get-client-hwnd))
|
|
(send canvas get-gl-config)
|
|
#f)])
|
|
(when v (set! gl v))
|
|
v)))
|
|
|
|
(define/override (make-backing-bitmap w h)
|
|
(if (send canvas get-canvas-background)
|
|
(make-object win32-bitmap% w h (send canvas get-hwnd))
|
|
(make-object bitmap% w h #f #t (->screen 1.0))))
|
|
|
|
(define/override (get-backing-size xb yb)
|
|
(send canvas get-client-size xb yb))
|
|
|
|
(define/override (get-size)
|
|
(let ([xb (box 0)]
|
|
[yb (box 0)])
|
|
(send canvas get-virtual-size xb yb)
|
|
(values (unbox xb) (unbox yb))))
|
|
|
|
(define/override (queue-backing-flush)
|
|
;; Re-enable expose events so that the queued
|
|
;; backing flush will be handled:
|
|
(end-delay)
|
|
(send canvas queue-backing-flush))
|
|
|
|
(define/override (flush)
|
|
(send canvas flush))
|
|
|
|
(define/override (request-delay)
|
|
(request-flush-delay canvas))
|
|
(define/override (cancel-delay req)
|
|
(cancel-flush-delay req))))
|
|
|
|
(define (do-backing-flush canvas dc hdc)
|
|
(send dc on-backing-flush
|
|
(lambda (bm)
|
|
(let ([w (box 0)]
|
|
[h (box 0)])
|
|
(send canvas get-client-size w h)
|
|
(define sw (->screen (unbox w)))
|
|
(define sh (->screen (unbox h)))
|
|
(define r (make-RECT 0 0 sw sh))
|
|
(define clip-type
|
|
(if need-clip-refresh-workaround?
|
|
(GetClipBox hdc r)
|
|
SIMPLEREGION))
|
|
(cond
|
|
[(and need-clip-refresh-workaround?
|
|
(not (and (= clip-type SIMPLEREGION)
|
|
(= (RECT-left r) 0)
|
|
(= (RECT-top r) 0)
|
|
(= (RECT-right r) sw)
|
|
(= (RECT-bottom r) sh))))
|
|
;; Another workaround: a clipping region installed by BeginPaint()
|
|
;; seems to interfere with Cairo drawing. So, draw to a
|
|
;; fresh context and copy back and forth using Win32.
|
|
(define cw (- (RECT-right r) (RECT-left r)))
|
|
(define ch (- (RECT-bottom r) (RECT-top r)))
|
|
(let* ([surface (cairo_win32_surface_create_with_ddb hdc
|
|
CAIRO_FORMAT_RGB24
|
|
cw
|
|
ch)]
|
|
[cr (cairo_create surface)]
|
|
[hdc2 (cairo_win32_surface_get_dc surface)])
|
|
(BitBlt hdc2 0 0 cw ch hdc (RECT-left r) (RECT-top r) SRCCOPY)
|
|
(cairo_scale cr (->screen 1.0) (->screen 1.0))
|
|
(backing-draw-bm bm cr (->normal sw) (->normal sh)
|
|
(->normal (- (RECT-left r))) (->normal (- (RECT-top r)))
|
|
(->screen 1.0))
|
|
(cairo_surface_flush surface)
|
|
(BitBlt hdc (RECT-left r) (RECT-top r) cw ch hdc2 0 0 SRCCOPY)
|
|
(cairo_surface_destroy surface)
|
|
(cairo_destroy cr))]
|
|
[else
|
|
(let* ([surface (cairo_win32_surface_create hdc)]
|
|
[cr (cairo_create surface)])
|
|
(cairo_surface_destroy surface)
|
|
(cairo_scale cr (->screen 1.0) (->screen 1.0))
|
|
(backing-draw-bm bm cr (->normal sw) (->normal sh)
|
|
0 0
|
|
(->screen 1.0))
|
|
(cairo_destroy cr))])))))
|
|
|
|
(define (request-flush-delay canvas)
|
|
(do-request-flush-delay
|
|
canvas
|
|
(lambda (gtk)
|
|
(send canvas suspend-paint-handling))
|
|
(lambda (gtk)
|
|
(send canvas resume-paint-handling))))
|
|
|
|
(define (cancel-flush-delay req)
|
|
(when req
|
|
(do-cancel-flush-delay
|
|
req
|
|
(lambda (canvas)
|
|
(send canvas resume-paint-handling)))))
|