github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/shiny/driver/internal/win32/win32.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 win32 implements a partial shiny screen driver using the Win32 API. 8 // It provides window, lifecycle, key, and mouse management, but no drawing. 9 // That is left to windriver (using GDI) or gldriver (using DirectX via ANGLE). 10 package win32 11 12 import ( 13 "fmt" 14 "runtime" 15 "syscall" 16 "unsafe" 17 18 "golang.org/x/exp/shiny/screen" 19 "golang.org/x/mobile/event/key" 20 "golang.org/x/mobile/event/lifecycle" 21 "golang.org/x/mobile/event/mouse" 22 "golang.org/x/mobile/event/paint" 23 "golang.org/x/mobile/event/size" 24 "golang.org/x/mobile/geom" 25 ) 26 27 // screenHWND is the handle to the "Screen window". 28 // The Screen window encapsulates all screen.Screen operations 29 // in an actual Windows window so they all run on the main thread. 30 // Since any messages sent to a window will be executed on the 31 // main thread, we can safely use the messages below. 32 var screenHWND HWND 33 34 const ( 35 msgCreateWindow = _WM_USER + iota 36 msgMainCallback 37 msgShow 38 msgQuit 39 msgLast 40 ) 41 42 var nextWM uint32 = msgLast 43 44 func newWindow(opts *screen.NewWindowOptions) (HWND, error) { 45 // TODO(brainman): convert windowClass to *uint16 once (in initWindowClass) 46 wcname, err := syscall.UTF16PtrFromString(windowClass) 47 if err != nil { 48 return 0, err 49 } 50 title, err := syscall.UTF16PtrFromString("Shiny Window") 51 if err != nil { 52 return 0, err 53 } 54 hwnd, err := _CreateWindowEx(0, 55 wcname, title, 56 _WS_OVERLAPPEDWINDOW, 57 _CW_USEDEFAULT, _CW_USEDEFAULT, 58 _CW_USEDEFAULT, _CW_USEDEFAULT, 59 0, 0, hThisInstance, 0) 60 if err != nil { 61 return 0, err 62 } 63 // TODO(andlabs): use proper nCmdShow 64 // TODO(andlabs): call UpdateWindow() 65 66 return hwnd, nil 67 } 68 69 // Show shows a newly created window. 70 // It sends the appropriate lifecycle events, makes the window appear 71 // on the screen, and sends an initial size event. 72 // 73 // This is a separate step from NewWindow to give the driver a chance 74 // to setup its internal state for a window before events start being 75 // delivered. 76 func Show(hwnd HWND) { 77 SendMessage(hwnd, msgShow, 0, 0) 78 } 79 80 func Release(hwnd HWND) { 81 // TODO(andlabs): check for errors from this? 82 // TODO(andlabs): remove unsafe 83 _DestroyWindow(hwnd) 84 // TODO(andlabs): what happens if we're still painting? 85 } 86 87 func sendFocus(hwnd HWND, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 88 switch uMsg { 89 case _WM_SETFOCUS: 90 LifecycleEvent(hwnd, lifecycle.StageFocused) 91 case _WM_KILLFOCUS: 92 LifecycleEvent(hwnd, lifecycle.StageVisible) 93 default: 94 panic(fmt.Sprintf("unexpected focus message: %d", uMsg)) 95 } 96 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 97 } 98 99 func sendShow(hwnd HWND, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 100 LifecycleEvent(hwnd, lifecycle.StageVisible) 101 _ShowWindow(hwnd, _SW_SHOWDEFAULT) 102 sendSize(hwnd) 103 return 0 104 } 105 106 func sendSizeEvent(hwnd HWND, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 107 wp := (*_WINDOWPOS)(unsafe.Pointer(lParam)) 108 if wp.Flags&_SWP_NOSIZE != 0 { 109 return 0 110 } 111 sendSize(hwnd) 112 return 0 113 } 114 115 func sendSize(hwnd HWND) { 116 var r _RECT 117 if err := _GetClientRect(hwnd, &r); err != nil { 118 panic(err) // TODO(andlabs) 119 } 120 121 width := int(r.Right - r.Left) 122 height := int(r.Bottom - r.Top) 123 124 // TODO(andlabs): don't assume that PixelsPerPt == 1 125 SizeEvent(hwnd, size.Event{ 126 WidthPx: width, 127 HeightPx: height, 128 WidthPt: geom.Pt(width), 129 HeightPt: geom.Pt(height), 130 PixelsPerPt: 1, 131 }) 132 } 133 134 func sendMouseEvent(hwnd HWND, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 135 e := mouse.Event{ 136 X: float32(_GET_X_LPARAM(lParam)), 137 Y: float32(_GET_Y_LPARAM(lParam)), 138 Modifiers: keyModifiers(), 139 } 140 141 switch uMsg { 142 case _WM_MOUSEMOVE, _WM_MOUSEWHEEL: 143 e.Direction = mouse.DirNone 144 case _WM_LBUTTONDOWN, _WM_MBUTTONDOWN, _WM_RBUTTONDOWN: 145 e.Direction = mouse.DirPress 146 case _WM_LBUTTONUP, _WM_MBUTTONUP, _WM_RBUTTONUP: 147 e.Direction = mouse.DirRelease 148 default: 149 panic("sendMouseEvent() called on non-mouse message") 150 } 151 152 switch uMsg { 153 case _WM_MOUSEMOVE: 154 switch { 155 case wParam&_MK_LBUTTON == _MK_LBUTTON: 156 e.Button = mouse.ButtonLeft 157 case wParam&_MK_MBUTTON == _MK_MBUTTON: 158 e.Button = mouse.ButtonMiddle 159 case wParam&_MK_RBUTTON == _MK_RBUTTON: 160 e.Button = mouse.ButtonRight 161 default: 162 // TODO: send move events when no buttons are held down? 163 return 0 164 } 165 case _WM_LBUTTONDOWN, _WM_LBUTTONUP: 166 e.Button = mouse.ButtonLeft 167 case _WM_MBUTTONDOWN, _WM_MBUTTONUP: 168 e.Button = mouse.ButtonMiddle 169 case _WM_RBUTTONDOWN, _WM_RBUTTONUP: 170 e.Button = mouse.ButtonRight 171 case _WM_MOUSEWHEEL: 172 delta := _GET_WHEEL_DELTA_WPARAM(wParam) / _WHEEL_DELTA 173 switch { 174 case delta > 0: 175 e.Button = mouse.ButtonWheelUp 176 case delta < 0: 177 e.Button = mouse.ButtonWheelDown 178 delta = -delta 179 default: 180 return 181 } 182 for delta > 0 { 183 MouseEvent(hwnd, e) 184 delta-- 185 } 186 return 187 } 188 189 MouseEvent(hwnd, e) 190 191 return 0 192 } 193 194 // Precondition: this is called in immediate response to the message that triggered the event (so not after w.Send). 195 func keyModifiers() (m key.Modifiers) { 196 down := func(x int32) bool { 197 // GetKeyState gets the key state at the time of the message, so this is what we want. 198 return _GetKeyState(x)&0x80 != 0 199 } 200 201 if down(_VK_CONTROL) { 202 m |= key.ModControl 203 } 204 if down(_VK_MENU) { 205 m |= key.ModAlt 206 } 207 if down(_VK_SHIFT) { 208 m |= key.ModShift 209 } 210 if down(_VK_LWIN) || down(_VK_RWIN) { 211 m |= key.ModMeta 212 } 213 return m 214 } 215 216 var ( 217 MouseEvent func(hwnd HWND, e mouse.Event) 218 PaintEvent func(hwnd HWND, e paint.Event) 219 SizeEvent func(hwnd HWND, e size.Event) 220 KeyEvent func(hwnd HWND, e key.Event) 221 LifecycleEvent func(hwnd HWND, e lifecycle.Stage) 222 ) 223 224 func sendPaint(hwnd HWND, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 225 PaintEvent(hwnd, paint.Event{}) 226 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 227 } 228 229 func screenWindowWndProc(hwnd HWND, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 230 switch uMsg { 231 case msgCreateWindow: 232 p := (*newWindowParams)(unsafe.Pointer(lParam)) 233 p.w, p.err = newWindow(p.opts) 234 case msgMainCallback: 235 go func() { 236 mainCallback() 237 SendMessage(screenHWND, msgQuit, 0, 0) 238 }() 239 case msgQuit: 240 _PostQuitMessage(0) 241 default: 242 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 243 } 244 return 0 245 } 246 247 var windowMsgs = map[uint32]func(hwnd HWND, uMsg uint32, wParam, lParam uintptr) (lResult uintptr){ 248 _WM_SETFOCUS: sendFocus, 249 _WM_KILLFOCUS: sendFocus, 250 _WM_PAINT: sendPaint, 251 msgShow: sendShow, 252 _WM_WINDOWPOSCHANGED: sendSizeEvent, 253 254 _WM_LBUTTONDOWN: sendMouseEvent, 255 _WM_LBUTTONUP: sendMouseEvent, 256 _WM_MBUTTONDOWN: sendMouseEvent, 257 _WM_MBUTTONUP: sendMouseEvent, 258 _WM_RBUTTONDOWN: sendMouseEvent, 259 _WM_RBUTTONUP: sendMouseEvent, 260 _WM_MOUSEMOVE: sendMouseEvent, 261 _WM_MOUSEWHEEL: sendMouseEvent, 262 263 _WM_KEYDOWN: sendKeyEvent, 264 _WM_KEYUP: sendKeyEvent, 265 // TODO case _WM_SYSKEYDOWN, _WM_SYSKEYUP: 266 } 267 268 func AddWindowMsg(fn func(hwnd HWND, uMsg uint32, wParam, lParam uintptr)) uint32 { 269 uMsg := nextWM 270 nextWM++ 271 windowMsgs[uMsg] = func(hwnd HWND, uMsg uint32, wParam, lParam uintptr) uintptr { 272 fn(hwnd, uMsg, wParam, lParam) 273 return 0 274 } 275 return uMsg 276 } 277 278 func windowWndProc(hwnd HWND, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 279 fn := windowMsgs[uMsg] 280 if fn != nil { 281 return fn(hwnd, uMsg, wParam, lParam) 282 } 283 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 284 } 285 286 type newWindowParams struct { 287 opts *screen.NewWindowOptions 288 w HWND 289 err error 290 } 291 292 func NewWindow(opts *screen.NewWindowOptions) (HWND, error) { 293 var p newWindowParams 294 p.opts = opts 295 SendMessage(screenHWND, msgCreateWindow, 0, uintptr(unsafe.Pointer(&p))) 296 return p.w, p.err 297 } 298 299 const windowClass = "shiny_Window" 300 301 func initWindowClass() (err error) { 302 wcname, err := syscall.UTF16PtrFromString(windowClass) 303 if err != nil { 304 return err 305 } 306 _, err = _RegisterClass(&_WNDCLASS{ 307 LpszClassName: wcname, 308 LpfnWndProc: syscall.NewCallback(windowWndProc), 309 HIcon: hDefaultIcon, 310 HCursor: hDefaultCursor, 311 HInstance: hThisInstance, 312 // TODO(andlabs): change this to something else? NULL? the hollow brush? 313 HbrBackground: syscall.Handle(_COLOR_BTNFACE + 1), 314 }) 315 return err 316 } 317 318 func initScreenWindow() (err error) { 319 const screenWindowClass = "shiny_ScreenWindow" 320 swc, err := syscall.UTF16PtrFromString(screenWindowClass) 321 if err != nil { 322 return err 323 } 324 emptyString, err := syscall.UTF16PtrFromString("") 325 if err != nil { 326 return err 327 } 328 wc := _WNDCLASS{ 329 LpszClassName: swc, 330 LpfnWndProc: syscall.NewCallback(screenWindowWndProc), 331 HIcon: hDefaultIcon, 332 HCursor: hDefaultCursor, 333 HInstance: hThisInstance, 334 HbrBackground: syscall.Handle(_COLOR_BTNFACE + 1), 335 } 336 _, err = _RegisterClass(&wc) 337 if err != nil { 338 return err 339 } 340 screenHWND, err = _CreateWindowEx(0, 341 swc, emptyString, 342 _WS_OVERLAPPEDWINDOW, 343 _CW_USEDEFAULT, _CW_USEDEFAULT, 344 _CW_USEDEFAULT, _CW_USEDEFAULT, 345 _HWND_MESSAGE, 0, hThisInstance, 0) 346 if err != nil { 347 return err 348 } 349 return nil 350 } 351 352 var ( 353 hDefaultIcon syscall.Handle 354 hDefaultCursor syscall.Handle 355 hThisInstance syscall.Handle 356 ) 357 358 func initCommon() (err error) { 359 hDefaultIcon, err = _LoadIcon(0, _IDI_APPLICATION) 360 if err != nil { 361 return err 362 } 363 hDefaultCursor, err = _LoadCursor(0, _IDC_ARROW) 364 if err != nil { 365 return err 366 } 367 // TODO(andlabs) hThisInstance 368 return nil 369 } 370 371 var mainCallback func() 372 373 func Main(f func()) (retErr error) { 374 // It does not matter which OS thread we are on. 375 // All that matters is that we confine all UI operations 376 // to the thread that created the respective window. 377 runtime.LockOSThread() 378 379 if err := initCommon(); err != nil { 380 return err 381 } 382 383 if err := initScreenWindow(); err != nil { 384 return err 385 } 386 defer func() { 387 // TODO(andlabs): log an error if this fails? 388 _DestroyWindow(screenHWND) 389 // TODO(andlabs): unregister window class 390 }() 391 392 if err := initWindowClass(); err != nil { 393 return err 394 } 395 396 // Prime the pump. 397 mainCallback = f 398 _PostMessage(screenHWND, msgMainCallback, 0, 0) 399 400 // Main message pump. 401 var m _MSG 402 for { 403 done, err := _GetMessage(&m, 0, 0, 0) 404 if err != nil { 405 return fmt.Errorf("win32 GetMessage failed: %v", err) 406 } 407 if done == 0 { // WM_QUIT 408 break 409 } 410 _TranslateMessage(&m) 411 _DispatchMessage(&m) 412 } 413 414 return nil 415 }