racket/collects/mred/private/wx/win32/dc.rkt
Matthew Flatt d5024f0f20 win64 racket/gui: another work around for Cairo clipping issues
Cairo doesn't seem to deal correctly with an HDC produced
by BeginPaint() that has a clipping region. The problem affects
only Win64. Work around the problem by drawing to a separate
HDC and copying to/from the screen. (To see the problem before
this patch, draw the DrRacket window to the edge of the screen
and back, and observe tha the toolbar doesn't update correctly.)

This change could affect performance, but it should mostly
be limited to refresh when a window moves.
2012-08-08 09:56:44 -06:00

178 lines
5.5 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 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]))
(define/override (build-cairo-surface w h)
(define hwnd (hwnd-param))
(if hwnd
(atomically
(let ([hdc (GetDC hwnd)])
(begin0
(cairo_win32_surface_create_with_ddb hdc
CAIRO_FORMAT_RGB24 w h)
(ReleaseDC hwnd hdc))))
(super build-cairo-surface w h)))
(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 under Win64,
;; and only with DDB surfaces.
(when win64?
(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))
(super make-backing-bitmap w h)))
(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 r (make-RECT 0 0 (unbox w) (unbox h)))
(define clip-type
(if win64?
(GetClipBox hdc r)
SIMPLEREGION))
(cond
[(and win64?
(not (and (= clip-type SIMPLEREGION)
(= (RECT-left r) 0)
(= (RECT-top r) 0)
(= (RECT-right r) (unbox w))
(= (RECT-bottom r) (unbox h)))))
;; 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)
(backing-draw-bm bm cr (unbox w) (unbox h) (- (RECT-left r)) (- (RECT-top r)))
(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)
(backing-draw-bm bm cr (unbox w) (unbox h))
(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)))))