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  }