shiny/driver/internal/win32: introduce and use ResizeClientRect

This CL makes screen.NewWindowOptions.Width and
screen.NewWindowOptions.Height parameters of screen.NewWindow
set window's client area of the specified window, instead of
setting dimensions of the bounding rectangle.

Fixes golang/go#19704.

Change-Id: Ic8a819e8127b89cc2734ed26dff6be06d2446ab1
Reviewed-on: https://go-review.googlesource.com/39111
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/shiny/driver/internal/win32/syscall_windows.go b/shiny/driver/internal/win32/syscall_windows.go
index 53857c4..36e0dfe 100644
--- a/shiny/driver/internal/win32/syscall_windows.go
+++ b/shiny/driver/internal/win32/syscall_windows.go
@@ -168,12 +168,14 @@
 //sys	_DestroyWindow(hwnd syscall.Handle) (err error) = user32.DestroyWindow
 //sys	_DispatchMessage(msg *_MSG) (ret int32) = user32.DispatchMessageW
 //sys	_GetClientRect(hwnd syscall.Handle, rect *_RECT) (err error) = user32.GetClientRect
+//sys	_GetWindowRect(hwnd syscall.Handle, rect *_RECT) (err error) = user32.GetWindowRect
 //sys   _GetKeyboardLayout(threadID uint32) (locale syscall.Handle) = user32.GetKeyboardLayout
 //sys   _GetKeyboardState(lpKeyState *byte) (err error) = user32.GetKeyboardState
 //sys	_GetKeyState(virtkey int32) (keystatus int16) = user32.GetKeyState
 //sys	_GetMessage(msg *_MSG, hwnd syscall.Handle, msgfiltermin uint32, msgfiltermax uint32) (ret int32, err error) [failretval==-1] = user32.GetMessageW
 //sys	_LoadCursor(hInstance syscall.Handle, cursorName uintptr) (cursor syscall.Handle, err error) = user32.LoadCursorW
 //sys	_LoadIcon(hInstance syscall.Handle, iconName uintptr) (icon syscall.Handle, err error) = user32.LoadIconW
+//sys	_MoveWindow(hwnd syscall.Handle, x int32, y int32, w int32, h int32, repaint bool) (err error) = user32.MoveWindow
 //sys	_PostMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult bool) = user32.PostMessageW
 //sys   _PostQuitMessage(exitCode int32) = user32.PostQuitMessage
 //sys	_RegisterClass(wc *_WNDCLASS) (atom uint16, err error) = user32.RegisterClassW
diff --git a/shiny/driver/internal/win32/win32.go b/shiny/driver/internal/win32/win32.go
index 402b575..5836073 100644
--- a/shiny/driver/internal/win32/win32.go
+++ b/shiny/driver/internal/win32/win32.go
@@ -70,20 +70,11 @@
 	if err != nil {
 		return 0, err
 	}
-	w, h := _CW_USEDEFAULT, _CW_USEDEFAULT
-	if opts != nil {
-		if opts.Width > 0 {
-			w = opts.Width
-		}
-		if opts.Height > 0 {
-			h = opts.Height
-		}
-	}
 	hwnd, err := _CreateWindowEx(0,
 		wcname, title,
 		_WS_OVERLAPPEDWINDOW,
 		_CW_USEDEFAULT, _CW_USEDEFAULT,
-		int32(w), int32(h),
+		_CW_USEDEFAULT, _CW_USEDEFAULT,
 		0, 0, hThisInstance, 0)
 	if err != nil {
 		return 0, err
@@ -94,6 +85,25 @@
 	return hwnd, nil
 }
 
+// ResizeClientRect makes hwnd client rectangle opts.Width by opts.Height in size.
+func ResizeClientRect(hwnd syscall.Handle, opts *screen.NewWindowOptions) error {
+	if opts == nil || opts.Width <= 0 || opts.Height <= 0 {
+		return nil
+	}
+	var cr, wr _RECT
+	err := _GetClientRect(hwnd, &cr)
+	if err != nil {
+		return err
+	}
+	err = _GetWindowRect(hwnd, &wr)
+	if err != nil {
+		return err
+	}
+	w := (wr.Right - wr.Left) - (cr.Right - int32(opts.Width))
+	h := (wr.Bottom - wr.Top) - (cr.Bottom - int32(opts.Height))
+	return _MoveWindow(hwnd, wr.Left, wr.Top, w, h, false)
+}
+
 // Show shows a newly created window.
 // It sends the appropriate lifecycle events, makes the window appear
 // on the screen, and sends an initial size event.
diff --git a/shiny/driver/internal/win32/zsyscall_windows.go b/shiny/driver/internal/win32/zsyscall_windows.go
index 07430ac..1759c01 100644
--- a/shiny/driver/internal/win32/zsyscall_windows.go
+++ b/shiny/driver/internal/win32/zsyscall_windows.go
@@ -47,12 +47,14 @@
 	procDestroyWindow     = moduser32.NewProc("DestroyWindow")
 	procDispatchMessageW  = moduser32.NewProc("DispatchMessageW")
 	procGetClientRect     = moduser32.NewProc("GetClientRect")
+	procGetWindowRect     = moduser32.NewProc("GetWindowRect")
 	procGetKeyboardLayout = moduser32.NewProc("GetKeyboardLayout")
 	procGetKeyboardState  = moduser32.NewProc("GetKeyboardState")
 	procGetKeyState       = moduser32.NewProc("GetKeyState")
 	procGetMessageW       = moduser32.NewProc("GetMessageW")
 	procLoadCursorW       = moduser32.NewProc("LoadCursorW")
 	procLoadIconW         = moduser32.NewProc("LoadIconW")
+	procMoveWindow        = moduser32.NewProc("MoveWindow")
 	procPostMessageW      = moduser32.NewProc("PostMessageW")
 	procPostQuitMessage   = moduser32.NewProc("PostQuitMessage")
 	procRegisterClassW    = moduser32.NewProc("RegisterClassW")
@@ -142,6 +144,18 @@
 	return
 }
 
+func _GetWindowRect(hwnd syscall.Handle, rect *_RECT) (err error) {
+	r1, _, e1 := syscall.Syscall(procGetWindowRect.Addr(), 2, uintptr(hwnd), uintptr(unsafe.Pointer(rect)), 0)
+	if r1 == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
 func _GetKeyboardLayout(threadID uint32) (locale syscall.Handle) {
 	r0, _, _ := syscall.Syscall(procGetKeyboardLayout.Addr(), 1, uintptr(threadID), 0, 0)
 	locale = syscall.Handle(r0)
@@ -205,6 +219,24 @@
 	return
 }
 
+func _MoveWindow(hwnd syscall.Handle, x int32, y int32, w int32, h int32, repaint bool) (err error) {
+	var _p0 uint32
+	if repaint {
+		_p0 = 1
+	} else {
+		_p0 = 0
+	}
+	r1, _, e1 := syscall.Syscall6(procMoveWindow.Addr(), 6, uintptr(hwnd), uintptr(x), uintptr(y), uintptr(w), uintptr(h), uintptr(_p0))
+	if r1 == 0 {
+		if e1 != 0 {
+			err = errnoErr(e1)
+		} else {
+			err = syscall.EINVAL
+		}
+	}
+	return
+}
+
 func _PostMessage(hwnd syscall.Handle, uMsg uint32, wParam uintptr, lParam uintptr) (lResult bool) {
 	r0, _, _ := syscall.Syscall6(procPostMessageW.Addr(), 4, uintptr(hwnd), uintptr(uMsg), uintptr(wParam), uintptr(lParam), 0, 0)
 	lResult = r0 != 0
diff --git a/shiny/driver/windriver/screen.go b/shiny/driver/windriver/screen.go
index 98f35f9..3e84345 100644
--- a/shiny/driver/windriver/screen.go
+++ b/shiny/driver/windriver/screen.go
@@ -74,6 +74,11 @@
 	s.windows[w.hwnd] = w
 	s.mu.Unlock()
 
+	err = win32.ResizeClientRect(w.hwnd, opts)
+	if err != nil {
+		return nil, err
+	}
+
 	win32.Show(w.hwnd)
 	return w, nil
 }