blob: dc4d28dc699b4829bb582e0deb2477a5cce284df [file] [log] [blame]
// 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
// #include "windriver.h"
import "C"
import (
"image"
"image/color"
"image/draw"
"sync"
"golang.org/x/exp/shiny/driver/internal/pump"
"golang.org/x/exp/shiny/screen"
"golang.org/x/image/math/f64"
"golang.org/x/mobile/event/key"
"golang.org/x/mobile/event/mouse"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/geom"
)
var (
windows = map[C.HWND]*window{}
windowsLock sync.Mutex
)
type window struct {
hwnd C.HWND
pump pump.Pump
}
func newWindow(opts *screen.NewWindowOptions) (screen.Window, error) {
var hwnd C.HWND
hr := C.createWindow(&hwnd)
if hr != C.S_OK {
return nil, winerror("error creating window", hr)
}
w := &window{
hwnd: hwnd,
pump: pump.Make(),
}
windowsLock.Lock()
windows[hwnd] = w
windowsLock.Unlock()
return w, nil
}
func (w *window) Release() {
if w.hwnd == nil { // already released?
return
}
windowsLock.Lock()
delete(windows, w.hwnd)
windowsLock.Unlock()
// TODO(andlabs): check for errors from this?
C.destroyWindow(w.hwnd)
w.hwnd = nil
w.pump.Release()
}
func (w *window) Events() <-chan interface{} { return w.pump.Events() }
func (w *window) Send(event interface{}) { w.pump.Send(event) }
func (w *window) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle, sender screen.Sender) {
// TODO
}
func (w *window) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
// TODO
}
func (w *window) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
// TODO
}
func (w *window) EndPaint(p paint.Event) {
// TODO
}
//export sendSizeEvent
func sendSizeEvent(hwnd C.HWND, r *C.RECT) {
windowsLock.Lock()
w := windows[hwnd]
windowsLock.Unlock()
width := int(r.right - r.left)
height := int(r.bottom - r.top)
// TODO(andlabs): don't assume that PixelsPerPt == 1
w.Send(size.Event{
WidthPx: width,
HeightPx: height,
WidthPt: geom.Pt(width),
HeightPt: geom.Pt(height),
PixelsPerPt: 1,
})
}
//export sendMouseEvent
func sendMouseEvent(hwnd C.HWND, uMsg C.UINT, x C.int, y C.int) {
var dir mouse.Direction
var button mouse.Button
windowsLock.Lock()
w := windows[hwnd]
windowsLock.Unlock()
switch uMsg {
case C.WM_MOUSEMOVE:
dir = mouse.DirNone
case C.WM_LBUTTONDOWN, C.WM_MBUTTONDOWN, C.WM_RBUTTONDOWN:
dir = mouse.DirPress
case C.WM_LBUTTONUP, C.WM_MBUTTONUP, C.WM_RBUTTONUP:
dir = mouse.DirRelease
default:
panic("sendMouseEvent() called on non-mouse message")
}
switch uMsg {
case C.WM_MOUSEMOVE:
button = mouse.ButtonNone
case C.WM_LBUTTONDOWN, C.WM_LBUTTONUP:
button = mouse.ButtonLeft
case C.WM_MBUTTONDOWN, C.WM_MBUTTONUP:
button = mouse.ButtonMiddle
case C.WM_RBUTTONDOWN, C.WM_RBUTTONUP:
button = mouse.ButtonRight
}
// TODO(andlabs): mouse wheel
w.Send(mouse.Event{
X: float32(x),
Y: float32(y),
Button: button,
Modifiers: keyModifiers(),
Direction: dir,
})
}
// Precondition: this is called in immediate response to the message that triggered the event (so not after w.Send).
func keyModifiers() (m key.Modifiers) {
down := func(x C.int) bool {
// GetKeyState gets the key state at the time of the message, so this is what we want.
return C.GetKeyState(x)&0x80 != 0
}
if down(C.VK_CONTROL) {
m |= key.ModControl
}
if down(C.VK_MENU) {
m |= key.ModAlt
}
if down(C.VK_SHIFT) {
m |= key.ModShift
}
if down(C.VK_LWIN) || down(C.VK_RWIN) {
m |= key.ModMeta
}
return m
}