|  | // Copyright 2015 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // +build windows | 
|  |  | 
|  | package windriver | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "image" | 
|  | "image/color" | 
|  | "image/draw" | 
|  | "sync" | 
|  | "syscall" | 
|  | "unsafe" | 
|  |  | 
|  | "golang.org/x/exp/shiny/driver/internal/win32" | 
|  | "golang.org/x/exp/shiny/screen" | 
|  | ) | 
|  |  | 
|  | type textureImpl struct { | 
|  | size   image.Point | 
|  | dc     syscall.Handle | 
|  | bitmap syscall.Handle | 
|  |  | 
|  | mu       sync.Mutex | 
|  | released bool | 
|  | } | 
|  |  | 
|  | type handleCreateTextureParams struct { | 
|  | size   image.Point | 
|  | dc     syscall.Handle | 
|  | bitmap syscall.Handle | 
|  | err    error | 
|  | } | 
|  |  | 
|  | var msgCreateTexture = win32.AddScreenMsg(handleCreateTexture) | 
|  |  | 
|  | func newTexture(size image.Point) (screen.Texture, error) { | 
|  | p := handleCreateTextureParams{size: size} | 
|  | win32.SendScreenMessage(msgCreateTexture, 0, uintptr(unsafe.Pointer(&p))) | 
|  | if p.err != nil { | 
|  | return nil, p.err | 
|  | } | 
|  | return &textureImpl{ | 
|  | size:   size, | 
|  | dc:     p.dc, | 
|  | bitmap: p.bitmap, | 
|  | }, nil | 
|  | } | 
|  |  | 
|  | func handleCreateTexture(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) { | 
|  | // This code needs to run on Windows message pump thread. | 
|  | // Firstly, it calls GetDC(nil) and, according to Windows documentation | 
|  | // (https://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx), | 
|  | // has to be released on the same thread. | 
|  | // Secondly, according to Windows documentation | 
|  | // (https://msdn.microsoft.com/en-us/library/windows/desktop/dd183489(v=vs.85).aspx), | 
|  | // ... thread that calls CreateCompatibleDC owns the HDC that is created. | 
|  | // When this thread is destroyed, the HDC is no longer valid. ... | 
|  | // So making Windows message pump thread own returned HDC makes DC | 
|  | // live as long as we want to. | 
|  | p := (*handleCreateTextureParams)(unsafe.Pointer(lParam)) | 
|  |  | 
|  | screenDC, err := win32.GetDC(0) | 
|  | if err != nil { | 
|  | p.err = err | 
|  | return | 
|  | } | 
|  | defer win32.ReleaseDC(0, screenDC) | 
|  |  | 
|  | dc, err := _CreateCompatibleDC(screenDC) | 
|  | if err != nil { | 
|  | p.err = err | 
|  | return | 
|  | } | 
|  | bitmap, err := _CreateCompatibleBitmap(screenDC, int32(p.size.X), int32(p.size.Y)) | 
|  | if err != nil { | 
|  | _DeleteDC(dc) | 
|  | p.err = err | 
|  | return | 
|  | } | 
|  | p.dc = dc | 
|  | p.bitmap = bitmap | 
|  | } | 
|  |  | 
|  | func (t *textureImpl) Bounds() image.Rectangle { | 
|  | return image.Rectangle{Max: t.size} | 
|  | } | 
|  |  | 
|  | func (t *textureImpl) Fill(r image.Rectangle, c color.Color, op draw.Op) { | 
|  | err := t.update(func(dc syscall.Handle) error { | 
|  | return fill(dc, r, c, op) | 
|  | }) | 
|  | if err != nil { | 
|  | panic(err) // TODO handle error | 
|  | } | 
|  | } | 
|  |  | 
|  | func (t *textureImpl) Release() { | 
|  | if err := t.release(); err != nil { | 
|  | panic(err) // TODO handle error | 
|  | } | 
|  | } | 
|  |  | 
|  | func (t *textureImpl) release() error { | 
|  | t.mu.Lock() | 
|  | defer t.mu.Unlock() | 
|  |  | 
|  | if t.released { | 
|  | return nil | 
|  | } | 
|  | t.released = true | 
|  |  | 
|  | err := _DeleteObject(t.bitmap) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | return _DeleteDC(t.dc) | 
|  | } | 
|  |  | 
|  | func (t *textureImpl) Size() image.Point { | 
|  | return t.size | 
|  | } | 
|  |  | 
|  | func (t *textureImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) { | 
|  | err := t.update(func(dc syscall.Handle) error { | 
|  | return src.(*bufferImpl).blitToDC(dc, dp, sr) | 
|  | }) | 
|  | if err != nil { | 
|  | panic(err) // TODO handle error | 
|  | } | 
|  | } | 
|  |  | 
|  | // update prepares texture t for update and executes f over texture device | 
|  | // context dc in a safe manner. | 
|  | func (t *textureImpl) update(f func(dc syscall.Handle) error) (retErr error) { | 
|  | t.mu.Lock() | 
|  | defer t.mu.Unlock() | 
|  |  | 
|  | if t.released { | 
|  | return errors.New("windriver: Texture.Upload called after Texture.Release") | 
|  | } | 
|  |  | 
|  | // Select t.bitmap into t.dc, so our drawing gets recorded | 
|  | // into t.bitmap and not into 1x1 default bitmap created | 
|  | // during CreateCompatibleDC call. | 
|  | prev, err := _SelectObject(t.dc, t.bitmap) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | defer func() { | 
|  | _, err2 := _SelectObject(t.dc, prev) | 
|  | if retErr == nil { | 
|  | retErr = err2 | 
|  | } | 
|  | }() | 
|  |  | 
|  | return f(t.dc) | 
|  | } |