github.com/as/shiny@v0.8.2/driver/windriver/window.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build windows 6 7 package windriver 8 9 import ( 10 "fmt" 11 12 "github.com/as/shiny/driver/internal/drawer" 13 14 "github.com/as/shiny/driver/internal/swizzle" 15 "github.com/as/shiny/driver/win32" 16 "github.com/as/shiny/math/f64" 17 "github.com/as/shiny/screen" 18 19 "github.com/as/shiny/event/lifecycle" 20 21 "image" 22 "image/color" 23 "image/draw" 24 "math" 25 "syscall" 26 "unsafe" 27 28 "github.com/as/shiny/event/size" 29 ) 30 31 type windowImpl struct { 32 dc syscall.Handle 33 hwnd syscall.Handle 34 //TODO(as): device should be here 35 sz size.Event 36 lifecycleStage lifecycle.Stage 37 } 38 39 func (w *windowImpl) Device() *screen.Device { 40 return screen.Dev 41 } 42 43 func (w *windowImpl) Release() { 44 win32.Release(w.hwnd) 45 } 46 47 func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) { 48 b := src.(*bufferImpl).buf 49 b2 := src.(*bufferImpl).buf2 50 swizzle.Swizzle(b2, b) 51 w.execCmd(&cmd{ 52 id: cmdUpload, 53 dp: dp, 54 buffer: src.(*bufferImpl), 55 sr: sr, 56 }) 57 } 58 59 func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) { 60 w.execCmd(&cmd{ 61 id: cmdFill, 62 dr: dr, 63 color: src, 64 op: op, 65 }) 66 } 67 68 func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 69 if op != draw.Src && op != draw.Over { 70 // TODO: 71 return 72 } 73 w.execCmd(&cmd{ 74 id: cmdDraw, 75 src2dst: src2dst, 76 texture: src.(*textureImpl).bitmap, 77 sr: sr, 78 op: op, 79 }) 80 } 81 82 func (w *windowImpl) DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 83 if op != draw.Src && op != draw.Over { 84 // TODO: 85 return 86 } 87 w.execCmd(&cmd{ 88 id: cmdDrawUniform, 89 src2dst: src2dst, 90 color: src, 91 sr: sr, 92 op: op, 93 }) 94 } 95 96 func drawWindow(dc syscall.Handle, src2dst f64.Aff3, src interface{}, sr image.Rectangle, op draw.Op) (retErr error) { 97 var dr image.Rectangle 98 if src2dst[1] != 0 || src2dst[3] != 0 { 99 // general drawing 100 dr = sr.Sub(sr.Min) 101 102 x := win32.Xform{ 103 M11: +float32(src2dst[0]), 104 M12: -float32(src2dst[1]), 105 M21: -float32(src2dst[3]), 106 M22: +float32(src2dst[4]), 107 Dx: +float32(src2dst[2]), 108 Dy: +float32(src2dst[5]), 109 } 110 err := win32.SetWorldTransform(dc, &x) 111 if err != nil { 112 return err 113 } 114 defer func() { 115 err := win32.ModifyWorldTransform(dc, nil, win32.MWTIdentity) 116 if retErr == nil { 117 retErr = err 118 } 119 }() 120 } else if src2dst[0] == 1 && src2dst[4] == 1 { 121 // copy bitmap 122 dr = sr.Add(image.Point{int(src2dst[2]), int(src2dst[5])}) 123 } else { 124 // scale bitmap 125 dstXMin := float64(sr.Min.X)*src2dst[0] + src2dst[2] 126 dstXMax := float64(sr.Max.X)*src2dst[0] + src2dst[2] 127 if dstXMin > dstXMax { 128 // TODO: check if this (and below) works when src2dst[0] < 0. 129 dstXMin, dstXMax = dstXMax, dstXMin 130 } 131 dstYMin := float64(sr.Min.Y)*src2dst[4] + src2dst[5] 132 dstYMax := float64(sr.Max.Y)*src2dst[4] + src2dst[5] 133 if dstYMin > dstYMax { 134 // TODO: check if this (and below) works when src2dst[4] < 0. 135 dstYMin, dstYMax = dstYMax, dstYMin 136 } 137 dr = image.Rectangle{ 138 image.Point{int(math.Floor(dstXMin)), int(math.Floor(dstYMin))}, 139 image.Point{int(math.Ceil(dstXMax)), int(math.Ceil(dstYMax))}, 140 } 141 } 142 switch s := src.(type) { 143 case syscall.Handle: 144 return copyBitmapToDC(dc, dr, s, sr, op) 145 case color.Color: 146 return fill(dc, dr, s, op) 147 } 148 return fmt.Errorf("unsupported type %T", src) 149 } 150 151 func (w *windowImpl) Copy(dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 152 drawer.Copy(w, dp, src, sr, op, opts) 153 } 154 155 func (w *windowImpl) Scale(dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) { 156 drawer.Scale(w, dr, src, sr, op, opts) 157 } 158 159 func (w *windowImpl) Publish() screen.PublishResult { 160 return screen.PublishResult{} 161 } 162 163 func init() { 164 win32.LifecycleEvent = lifecycleEvent 165 win32.SizeEvent = sizeEvent 166 } 167 168 func lifecycleEvent(hwnd syscall.Handle, to lifecycle.Stage) { 169 w := theScreen.windows 170 171 if w.lifecycleStage == to { 172 return 173 } 174 select { 175 default: 176 case w.Device().Lifecycle <- lifecycle.Event{ 177 From: w.lifecycleStage, 178 To: to, 179 }: 180 } 181 w.lifecycleStage = to 182 } 183 184 func sizeEvent(hwnd syscall.Handle, e size.Event) { 185 w := theScreen.windows 186 w.Device().Size <- e 187 if e != w.sz { 188 w.sz = e 189 } 190 } 191 192 // cmd is used to carry parameters between user code 193 // and Windows message pump thread. 194 type cmd struct { 195 id int 196 err error 197 198 src2dst f64.Aff3 199 sr image.Rectangle 200 dp image.Point 201 dr image.Rectangle 202 color color.Color 203 op draw.Op 204 texture syscall.Handle 205 buffer *bufferImpl 206 } 207 208 const ( 209 cmdDraw = iota 210 cmdFill 211 cmdUpload 212 cmdDrawUniform 213 ) 214 215 var msgCmd = win32.AddWindowMsg(handleCmd) 216 217 func (w *windowImpl) execCmd(c *cmd) { 218 win32.SendMessage(w.hwnd, msgCmd, uintptr(w.dc), uintptr(unsafe.Pointer(c))) 219 if c.err != nil { 220 println(fmt.Sprintf("execCmd faild for cmd.id=%d: %v", c.id, c.err)) // TODO handle errors 221 } 222 } 223 224 func handleCmd(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) { 225 c := (*cmd)(unsafe.Pointer(lParam)) 226 dc := syscall.Handle(wParam) 227 228 switch c.id { 229 case cmdDraw: 230 c.err = drawWindow(dc, c.src2dst, c.texture, c.sr, c.op) 231 case cmdDrawUniform: 232 c.err = drawWindow(dc, c.src2dst, c.color, c.sr, c.op) 233 case cmdFill: 234 c.err = fill(dc, c.dr, c.color, c.op) 235 case cmdUpload: 236 // TODO: adjust if dp is outside dst bounds, or sr is outside buffer bounds. 237 dr := c.sr.Add(c.dp.Sub(c.sr.Min)) 238 c.err = copyBitmapToDC(dc, dr, c.buffer.hbitmap, c.sr, draw.Src) 239 default: 240 c.err = fmt.Errorf("unknown command id=%d", c.id) 241 } 242 }