win32.go (13292B)
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 // import "golang.org/x/exp/shiny/driver/internal/win32" 11 12 import ( 13 "fmt" 14 "runtime" 15 "sync" 16 "syscall" 17 "unsafe" 18 19 "golang.org/x/exp/shiny/screen" 20 "golang.org/x/mobile/event/key" 21 "golang.org/x/mobile/event/lifecycle" 22 "golang.org/x/mobile/event/mouse" 23 "golang.org/x/mobile/event/paint" 24 "golang.org/x/mobile/event/size" 25 "golang.org/x/mobile/geom" 26 ) 27 28 // screenHWND is the handle to the "Screen window". 29 // The Screen window encapsulates all screen.Screen operations 30 // in an actual Windows window so they all run on the main thread. 31 // Since any messages sent to a window will be executed on the 32 // main thread, we can safely use the messages below. 33 var screenHWND syscall.Handle 34 35 const ( 36 msgCreateWindow = _WM_USER + iota 37 msgMainCallback 38 msgShow 39 msgQuit 40 msgLast 41 ) 42 43 // userWM is used to generate private (WM_USER and above) window message IDs 44 // for use by screenWindowWndProc and windowWndProc. 45 type userWM struct { 46 sync.Mutex 47 id uint32 48 } 49 50 func (m *userWM) next() uint32 { 51 m.Lock() 52 if m.id == 0 { 53 m.id = msgLast 54 } 55 r := m.id 56 m.id++ 57 m.Unlock() 58 return r 59 } 60 61 var currentUserWM userWM 62 63 func newWindow(opts *screen.NewWindowOptions) (syscall.Handle, error) { 64 // TODO(brainman): convert windowClass to *uint16 once (in initWindowClass) 65 wcname, err := syscall.UTF16PtrFromString(windowClass) 66 if err != nil { 67 return 0, err 68 } 69 title, err := syscall.UTF16PtrFromString(opts.GetTitle()) 70 if err != nil { 71 return 0, err 72 } 73 hwnd, err := _CreateWindowEx(0, 74 wcname, title, 75 _WS_OVERLAPPEDWINDOW, 76 _CW_USEDEFAULT, _CW_USEDEFAULT, 77 _CW_USEDEFAULT, _CW_USEDEFAULT, 78 0, 0, hThisInstance, 0) 79 if err != nil { 80 return 0, err 81 } 82 // TODO(andlabs): use proper nCmdShow 83 // TODO(andlabs): call UpdateWindow() 84 85 return hwnd, nil 86 } 87 88 // ResizeClientRect makes hwnd client rectangle opts.Width by opts.Height in size. 89 func ResizeClientRect(hwnd syscall.Handle, opts *screen.NewWindowOptions) error { 90 if opts == nil || opts.Width <= 0 || opts.Height <= 0 { 91 return nil 92 } 93 var cr, wr _RECT 94 err := _GetClientRect(hwnd, &cr) 95 if err != nil { 96 return err 97 } 98 err = _GetWindowRect(hwnd, &wr) 99 if err != nil { 100 return err 101 } 102 w := (wr.Right - wr.Left) - (cr.Right - int32(opts.Width)) 103 h := (wr.Bottom - wr.Top) - (cr.Bottom - int32(opts.Height)) 104 return _MoveWindow(hwnd, wr.Left, wr.Top, w, h, false) 105 } 106 107 // Show shows a newly created window. 108 // It sends the appropriate lifecycle events, makes the window appear 109 // on the screen, and sends an initial size event. 110 // 111 // This is a separate step from NewWindow to give the driver a chance 112 // to setup its internal state for a window before events start being 113 // delivered. 114 func Show(hwnd syscall.Handle) { 115 SendMessage(hwnd, msgShow, 0, 0) 116 } 117 118 func Release(hwnd syscall.Handle) { 119 SendMessage(hwnd, _WM_CLOSE, 0, 0) 120 } 121 122 func sendFocus(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 123 switch uMsg { 124 case _WM_SETFOCUS: 125 LifecycleEvent(hwnd, lifecycle.StageFocused) 126 case _WM_KILLFOCUS: 127 LifecycleEvent(hwnd, lifecycle.StageVisible) 128 default: 129 panic(fmt.Sprintf("unexpected focus message: %d", uMsg)) 130 } 131 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 132 } 133 134 func sendShow(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 135 LifecycleEvent(hwnd, lifecycle.StageVisible) 136 _ShowWindow(hwnd, _SW_SHOWDEFAULT) 137 sendSize(hwnd) 138 return 0 139 } 140 141 func sendSizeEvent(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 142 wp := (*_WINDOWPOS)(unsafe.Pointer(lParam)) 143 if wp.Flags&_SWP_NOSIZE != 0 { 144 return 0 145 } 146 sendSize(hwnd) 147 return 0 148 } 149 150 func sendSize(hwnd syscall.Handle) { 151 var r _RECT 152 if err := _GetClientRect(hwnd, &r); err != nil { 153 panic(err) // TODO(andlabs) 154 } 155 156 width := int(r.Right - r.Left) 157 height := int(r.Bottom - r.Top) 158 159 // TODO(andlabs): don't assume that PixelsPerPt == 1 160 SizeEvent(hwnd, size.Event{ 161 WidthPx: width, 162 HeightPx: height, 163 WidthPt: geom.Pt(width), 164 HeightPt: geom.Pt(height), 165 PixelsPerPt: 1, 166 }) 167 } 168 169 func sendClose(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 170 // TODO(ktye): DefWindowProc calls DestroyWindow by default. 171 // To intercept destruction of the window, return 0 and call 172 // DestroyWindow when appropriate. 173 LifecycleEvent(hwnd, lifecycle.StageDead) 174 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 175 } 176 177 func sendMouseEvent(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 178 e := mouse.Event{ 179 X: float32(_GET_X_LPARAM(lParam)), 180 Y: float32(_GET_Y_LPARAM(lParam)), 181 Modifiers: keyModifiers(), 182 } 183 184 switch uMsg { 185 case _WM_MOUSEMOVE: 186 e.Direction = mouse.DirNone 187 case _WM_LBUTTONDOWN, _WM_MBUTTONDOWN, _WM_RBUTTONDOWN: 188 e.Direction = mouse.DirPress 189 case _WM_LBUTTONUP, _WM_MBUTTONUP, _WM_RBUTTONUP: 190 e.Direction = mouse.DirRelease 191 case _WM_MOUSEWHEEL: 192 // TODO: On a trackpad, a scroll can be a drawn-out affair with a 193 // distinct beginning and end. Should the intermediate events be 194 // DirNone? 195 e.Direction = mouse.DirStep 196 197 // Convert from screen to window coordinates. 198 p := _POINT{ 199 int32(e.X), 200 int32(e.Y), 201 } 202 _ScreenToClient(hwnd, &p) 203 e.X = float32(p.X) 204 e.Y = float32(p.Y) 205 default: 206 panic("sendMouseEvent() called on non-mouse message") 207 } 208 209 switch uMsg { 210 case _WM_MOUSEMOVE: 211 // No-op. 212 case _WM_LBUTTONDOWN, _WM_LBUTTONUP: 213 e.Button = mouse.ButtonLeft 214 case _WM_MBUTTONDOWN, _WM_MBUTTONUP: 215 e.Button = mouse.ButtonMiddle 216 case _WM_RBUTTONDOWN, _WM_RBUTTONUP: 217 e.Button = mouse.ButtonRight 218 case _WM_MOUSEWHEEL: 219 // TODO: handle horizontal scrolling 220 delta := _GET_WHEEL_DELTA_WPARAM(wParam) / _WHEEL_DELTA 221 switch { 222 case delta > 0: 223 e.Button = mouse.ButtonWheelUp 224 case delta < 0: 225 e.Button = mouse.ButtonWheelDown 226 delta = -delta 227 default: 228 return 229 } 230 for delta > 0 { 231 MouseEvent(hwnd, e) 232 delta-- 233 } 234 return 235 } 236 237 MouseEvent(hwnd, e) 238 239 return 0 240 } 241 242 // Precondition: this is called in immediate response to the message that triggered the event (so not after w.Send). 243 func keyModifiers() (m key.Modifiers) { 244 down := func(x int32) bool { 245 // GetKeyState gets the key state at the time of the message, so this is what we want. 246 return _GetKeyState(x)&0x80 != 0 247 } 248 249 if down(_VK_CONTROL) { 250 m |= key.ModControl 251 } 252 if down(_VK_MENU) { 253 m |= key.ModAlt 254 } 255 if down(_VK_SHIFT) { 256 m |= key.ModShift 257 } 258 if down(_VK_LWIN) || down(_VK_RWIN) { 259 m |= key.ModMeta 260 } 261 return m 262 } 263 264 var ( 265 MouseEvent func(hwnd syscall.Handle, e mouse.Event) 266 PaintEvent func(hwnd syscall.Handle, e paint.Event) 267 SizeEvent func(hwnd syscall.Handle, e size.Event) 268 KeyEvent func(hwnd syscall.Handle, e key.Event) 269 LifecycleEvent func(hwnd syscall.Handle, e lifecycle.Stage) 270 271 // TODO: use the golang.org/x/exp/shiny/driver/internal/lifecycler package 272 // instead of or together with the LifecycleEvent callback? 273 ) 274 275 func sendPaint(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr) { 276 PaintEvent(hwnd, paint.Event{}) 277 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 278 } 279 280 var screenMsgs = map[uint32]func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr){} 281 282 func AddScreenMsg(fn func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr)) uint32 { 283 uMsg := currentUserWM.next() 284 screenMsgs[uMsg] = func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) uintptr { 285 fn(hwnd, uMsg, wParam, lParam) 286 return 0 287 } 288 return uMsg 289 } 290 291 func screenWindowWndProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 292 switch uMsg { 293 case msgCreateWindow: 294 p := (*newWindowParams)(unsafe.Pointer(lParam)) 295 p.w, p.err = newWindow(p.opts) 296 case msgMainCallback: 297 go func() { 298 mainCallback() 299 SendScreenMessage(msgQuit, 0, 0) 300 }() 301 case msgQuit: 302 _PostQuitMessage(0) 303 } 304 fn := screenMsgs[uMsg] 305 if fn != nil { 306 return fn(hwnd, uMsg, wParam, lParam) 307 } 308 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 309 } 310 311 //go:uintptrescapes 312 313 func SendScreenMessage(uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 314 return SendMessage(screenHWND, uMsg, wParam, lParam) 315 } 316 317 var windowMsgs = map[uint32]func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) (lResult uintptr){ 318 _WM_SETFOCUS: sendFocus, 319 _WM_KILLFOCUS: sendFocus, 320 _WM_PAINT: sendPaint, 321 msgShow: sendShow, 322 _WM_WINDOWPOSCHANGED: sendSizeEvent, 323 _WM_CLOSE: sendClose, 324 325 _WM_LBUTTONDOWN: sendMouseEvent, 326 _WM_LBUTTONUP: sendMouseEvent, 327 _WM_MBUTTONDOWN: sendMouseEvent, 328 _WM_MBUTTONUP: sendMouseEvent, 329 _WM_RBUTTONDOWN: sendMouseEvent, 330 _WM_RBUTTONUP: sendMouseEvent, 331 _WM_MOUSEMOVE: sendMouseEvent, 332 _WM_MOUSEWHEEL: sendMouseEvent, 333 334 _WM_KEYDOWN: sendKeyEvent, 335 _WM_KEYUP: sendKeyEvent, 336 // TODO case _WM_SYSKEYDOWN, _WM_SYSKEYUP: 337 } 338 339 func AddWindowMsg(fn func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr)) uint32 { 340 uMsg := currentUserWM.next() 341 windowMsgs[uMsg] = func(hwnd syscall.Handle, uMsg uint32, wParam, lParam uintptr) uintptr { 342 fn(hwnd, uMsg, wParam, lParam) 343 return 0 344 } 345 return uMsg 346 } 347 348 func windowWndProc(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 349 fn := windowMsgs[uMsg] 350 if fn != nil { 351 return fn(hwnd, uMsg, wParam, lParam) 352 } 353 return _DefWindowProc(hwnd, uMsg, wParam, lParam) 354 } 355 356 type newWindowParams struct { 357 opts *screen.NewWindowOptions 358 w syscall.Handle 359 err error 360 } 361 362 func NewWindow(opts *screen.NewWindowOptions) (syscall.Handle, error) { 363 var p newWindowParams 364 p.opts = opts 365 SendScreenMessage(msgCreateWindow, 0, uintptr(unsafe.Pointer(&p))) 366 return p.w, p.err 367 } 368 369 const windowClass = "shiny_Window" 370 const screenWindowClass = "shiny_ScreenWindow" 371 372 func initWindowClass() (err error) { 373 wcname, err := syscall.UTF16PtrFromString(windowClass) 374 if err != nil { 375 return err 376 } 377 _, err = _RegisterClass(&_WNDCLASS{ 378 LpszClassName: wcname, 379 LpfnWndProc: syscall.NewCallback(windowWndProc), 380 HIcon: hDefaultIcon, 381 HCursor: hDefaultCursor, 382 HInstance: hThisInstance, 383 // TODO(andlabs): change this to something else? NULL? the hollow brush? 384 HbrBackground: syscall.Handle(_COLOR_BTNFACE + 1), 385 }) 386 return err 387 } 388 389 func closeWindowClass() (err error) { 390 wcname, err := syscall.UTF16PtrFromString(windowClass) 391 if err != nil { 392 return err 393 } 394 _UnregisterClass(wcname, hThisInstance) 395 396 return nil 397 } 398 399 func initScreenWindow() (err error) { 400 swc, err := syscall.UTF16PtrFromString(screenWindowClass) 401 if err != nil { 402 return err 403 } 404 emptyString, err := syscall.UTF16PtrFromString("") 405 if err != nil { 406 return err 407 } 408 wc := _WNDCLASS{ 409 LpszClassName: swc, 410 LpfnWndProc: syscall.NewCallback(screenWindowWndProc), 411 HIcon: hDefaultIcon, 412 HCursor: hDefaultCursor, 413 HInstance: hThisInstance, 414 HbrBackground: syscall.Handle(_COLOR_BTNFACE + 1), 415 } 416 _, err = _RegisterClass(&wc) 417 if err != nil { 418 return err 419 } 420 screenHWND, err = _CreateWindowEx(0, 421 swc, emptyString, 422 _WS_OVERLAPPEDWINDOW, 423 _CW_USEDEFAULT, _CW_USEDEFAULT, 424 _CW_USEDEFAULT, _CW_USEDEFAULT, 425 _HWND_MESSAGE, 0, hThisInstance, 0) 426 if err != nil { 427 return err 428 } 429 return nil 430 } 431 432 func closeScreenWindow() (err error) { 433 // first destroy window 434 _DestroyWindow(screenHWND) 435 436 // then unregister class 437 swc, err := syscall.UTF16PtrFromString(screenWindowClass) 438 if err != nil { 439 return err 440 } 441 _UnregisterClass(swc, hThisInstance) 442 443 return nil 444 } 445 446 var ( 447 hDefaultIcon syscall.Handle 448 hDefaultCursor syscall.Handle 449 hThisInstance syscall.Handle 450 ) 451 452 func initCommon() (err error) { 453 hDefaultIcon, err = _LoadIcon(0, _IDI_APPLICATION) 454 if err != nil { 455 return err 456 } 457 hDefaultCursor, err = _LoadCursor(0, _IDC_ARROW) 458 if err != nil { 459 return err 460 } 461 // TODO(andlabs) hThisInstance 462 return nil 463 } 464 465 //go:uintptrescapes 466 467 func SendMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult uintptr) { 468 return sendMessage(hwnd, uMsg, wParam, lParam) 469 } 470 471 var mainCallback func() 472 473 func Main(f func()) (retErr error) { 474 // It does not matter which OS thread we are on. 475 // All that matters is that we confine all UI operations 476 // to the thread that created the respective window. 477 runtime.LockOSThread() 478 479 if err := initCommon(); err != nil { 480 return err 481 } 482 483 if err := initScreenWindow(); err != nil { 484 return err 485 } 486 defer func() { 487 // TODO(andlabs): log an error if this fails? 488 closeScreenWindow() 489 }() 490 491 if err := initWindowClass(); err != nil { 492 return err 493 } 494 defer func() { 495 // TODO(andlabs): log an error if this fails? 496 closeWindowClass() 497 }() 498 499 // Prime the pump. 500 mainCallback = f 501 _PostMessage(screenHWND, msgMainCallback, 0, 0) 502 503 // Main message pump. 504 var m _MSG 505 for { 506 done, err := _GetMessage(&m, 0, 0, 0) 507 if err != nil { 508 return fmt.Errorf("win32 GetMessage failed: %v", err) 509 } 510 if done == 0 { // WM_QUIT 511 break 512 } 513 _TranslateMessage(&m) 514 _DispatchMessage(&m) 515 } 516 517 return nil 518 }