shiny/driver/windriver: new package providing a Windows driver

This provides the initial implementation of a Windows driver for shiny.
It will only open a window and wait for you to close it; Buffer and
Texture will come next, when I figure out how they will work.

I tried to lay down the design of the package in doc.go. If you are still
confused, I'll be glad to rewrite or expand it.

Currently this uses cgo.

Patch set 2 changes the C formatting to match the Go sourcce tree's.
It also includes a quick change to driver_fallback.go.

Patch set 3 rewrites doc.go, hopefully to be clearer.

Patch set 4 implements changes suggested in code review and
removes a block of comments that was accidentally left in
when doc.go was written.

Patch set 5 formats this commit message.

Change-Id: I2b060455243f445dd0f4c62f6f0c346768491547
Reviewed-on: https://go-review.googlesource.com/13617
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/shiny/driver/driver_fallback.go b/shiny/driver/driver_fallback.go
index bbe99f5..ac37746 100644
--- a/shiny/driver/driver_fallback.go
+++ b/shiny/driver/driver_fallback.go
@@ -4,6 +4,7 @@
 
 // +build !darwin
 // +build !linux android
+// +build !windows
 
 package driver
 
diff --git a/shiny/driver/driver_windows.go b/shiny/driver/driver_windows.go
new file mode 100644
index 0000000..4ae016c
--- /dev/null
+++ b/shiny/driver/driver_windows.go
@@ -0,0 +1,14 @@
+// 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.
+
+package driver
+
+import (
+	"golang.org/x/exp/shiny/driver/windriver"
+	"golang.org/x/exp/shiny/screen"
+)
+
+func main(f func(screen.Screen)) {
+	windriver.Main(f)
+}
diff --git a/shiny/driver/windriver/doc.go b/shiny/driver/windriver/doc.go
new file mode 100644
index 0000000..36ef294
--- /dev/null
+++ b/shiny/driver/windriver/doc.go
@@ -0,0 +1,104 @@
+// 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.
+
+// Package windriver provides the Windows driver for accessing a screen.
+package windriver
+
+/*
+Implementation Details
+
+On Windows, UI can run on any thread, but any windows created
+on a thread must only be manipulated from that thread. You can send
+"window messages" to any window; when you send a window
+message to a window owned by another thread, Windows will
+temporarily switch to that thread to dispatch the message. As such,
+windows serve as the communication endpoints between threads
+on Windows. In addition, each thread that hosts UI must handle
+incoming window messages from the OS through a "message pump".
+These messages include paint events and input events.
+
+windriver designates the thread that calls Main as the UI thread.
+It locks this thread, creates a special window to handle screen.Screen
+calls, runs the function passed to Main on another goroutine, and
+runs a message pump.
+
+The window that handles screen.Screen functions is currently called
+the "utility window". A better name can be chosen later. This window
+handles creating screen.Windows/Buffers/Textures. As such, all shiny
+Windows are owned by a single thread.
+
+Each function in windriver, be it on screen.Screen or screen.Window,
+is translated into a window message and sent to a window, namely
+the utility window and the screen.Window window, respectively.
+This is how windriver remains thread-safe.
+
+(TODO(andlabs): actually move per-window messages to the window itself)
+
+Presently, the actual Windows API work is implemented in C. This is
+to encapsulate Windows's data structures, ensure properly handling
+signed -> unsigned conversions in constants, handle pointer casts
+cleanly, and properly handle the "last error", which I will describe
+later.
+
+Here is a demonstration of all of the above. When you call
+screen.NewWindow(opts), the Go code calls the C function
+createWindow, which is implemented as something similar to
+
+	HRESULT createWindow(newWindowOpts *opts, HWND *phwnd) {
+		return (HRESULT) SendMessageW(utilityWindow,
+			msgCreateWindow,
+			(WPARAM) opts,
+			(LPARAM) phwnd);
+	}
+
+HRESULT is another type for errors in Windows; I will again describe
+this later. This function tells the utility window to make a new window,
+using the given options, storing the window's OS handle in phwnd, and
+returning any error directly to us through SendMessageW.
+
+This code is running on another goroutine, which will definitely be
+run on another OS thread. As such, Windows will switch to the UI
+thread to dispatch this new window message. The code for the
+implementation of the utility window (called a "window procedure")
+contains something like this:
+
+		case msgCreateWindow:
+			return utilCreateWindow((newWindowOpts *) wParam,
+				(HWND *) lParam);
+
+and the utilCreateWindow function does the actual work:
+
+	LRESULT utilCreateWindow(newWindowOpts *opts, HWND *phwnd) {
+		*phwnd = CreateWindowExW(...);
+		if (*phwnd == NULL) {
+			return lastErrorAsLRESULT();
+		}
+		return lS_OK;
+	}
+
+When this returns, Windows switches back to the previous thread,
+which can now use the window handle and error value.
+
+Older Windows API functions return a Boolean flag to indicate if they
+succeeded or failed, storing the actual reason for failure in what is
+called the "last error". This is NOT contractual; functions are free to
+fail without setting the last error, or free to clear the last error on
+success.
+
+To simplify error reporting, we instead convert all last errors to the
+newer HRESULT error code system. The rules are simple: if the
+function succeeded, we return the standard success code, S_OK.
+If the function failed, we get the last error. If it's zero (no error),
+we return the special value E_FAIL. Otherwise, we convert the last
+error to an HRESULT (this is a well-defined operation that we can
+reverse later when we're ready to report the error to the user).
+This is all done by the C lastErrorToHRESULT function. Error
+reporting on the Go side is handled by th winerror function.
+
+Because window messages return LRESULTs, not HRESULTs,
+the lastErrorToLRESULT and lS_OK macros are provided, which
+automatically insert the necessary casts. An LRESULT (which is
+pointer-sized) will always be either the same size as or larger than
+an HRESULT (which is strictly 32 bits wide).
+*/
diff --git a/shiny/driver/windriver/errors.go b/shiny/driver/windriver/errors.go
new file mode 100644
index 0000000..ab3c67f
--- /dev/null
+++ b/shiny/driver/windriver/errors.go
@@ -0,0 +1,20 @@
+// 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.
+
+package windriver
+
+// #include "windriver.h"
+import "C"
+
+import (
+	"fmt"
+)
+
+func winerror(msg string, hr C.HRESULT) error {
+	// TODO(andlabs): get long description
+	if hr == C.E_FAIL {
+		return fmt.Errorf("windriver: %s: unknown error", msg)
+	}
+	return fmt.Errorf("windriver: %s: last error %d", msg, hr&0xFFFF)
+}
diff --git a/shiny/driver/windriver/screen.go b/shiny/driver/windriver/screen.go
new file mode 100644
index 0000000..6581c92
--- /dev/null
+++ b/shiny/driver/windriver/screen.go
@@ -0,0 +1,30 @@
+// 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.
+
+package windriver
+
+import (
+	"fmt"
+	"image"
+
+	"golang.org/x/exp/shiny/screen"
+)
+
+type screenimpl struct{}
+
+func newScreenImpl() screen.Screen {
+	return &screenimpl{}
+}
+
+func (*screenimpl) NewBuffer(size image.Point) (screen.Buffer, error) {
+	return nil, fmt.Errorf("TODO")
+}
+
+func (*screenimpl) NewTexture(size image.Point) (screen.Texture, error) {
+	return nil, fmt.Errorf("TODO")
+}
+
+func (*screenimpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) {
+	return newWindow(opts)
+}
diff --git a/shiny/driver/windriver/utilwindow.c b/shiny/driver/windriver/utilwindow.c
new file mode 100644
index 0000000..37a4fa3
--- /dev/null
+++ b/shiny/driver/windriver/utilwindow.c
@@ -0,0 +1,54 @@
+// 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.
+
+#include "windriver.h"
+
+HWND utilityWindow = NULL;
+
+static LRESULT CALLBACK utilityWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+	HWND *phwnd;
+
+	switch (uMsg) {
+	case msgCreateWindow:
+		phwnd = (HWND *) lParam;
+		return utilCreateWindow(phwnd);
+	case msgDestroyWindow:
+		return utilDestroyWindow((HWND) wParam);
+	}
+	return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
+
+HRESULT initUtilityWindow(void) {
+	WNDCLASSW wc;
+
+	ZeroMemory(&wc, sizeof (WNDCLASSW));
+	wc.lpszClassName = L"shiny_utilityWindow";
+	wc.lpfnWndProc = utilityWindowWndProc;
+	wc.hInstance = thishInstance;
+	wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
+	if (wc.hIcon == NULL) {
+		return lastErrorToHRESULT();
+	}
+	wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
+	if (wc.hCursor == NULL) {
+		return lastErrorToHRESULT();
+	}
+	// TODO(andlabs): change this to something else? NULL? the hollow brush?
+	wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
+	if (RegisterClassW(&wc) == 0) {
+		return lastErrorToHRESULT();
+	}
+
+	utilityWindow = CreateWindowExW(0,
+		L"shiny_utilityWindow", L"Shiny Utility Window",
+		WS_OVERLAPPEDWINDOW,
+		CW_USEDEFAULT, CW_USEDEFAULT,
+		CW_USEDEFAULT, CW_USEDEFAULT,
+		HWND_MESSAGE, NULL, thishInstance, NULL);
+	if (utilityWindow == NULL) {
+		return lastErrorToHRESULT();
+	}
+
+	return S_OK;
+}
diff --git a/shiny/driver/windriver/window.c b/shiny/driver/windriver/window.c
new file mode 100644
index 0000000..6e54d95
--- /dev/null
+++ b/shiny/driver/windriver/window.c
@@ -0,0 +1,69 @@
+// 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.
+
+#include "windriver.h"
+
+#define windowClass L"shiny_Window"
+
+static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+	// TODO(andlabs): this is only for testing that the package works; delete when done
+	if (uMsg == WM_CLOSE) {
+		PostQuitMessage(0);
+	}
+	return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
+
+HRESULT initWindowClass(void) {
+	WNDCLASSW wc;
+
+	ZeroMemory(&wc, sizeof (WNDCLASSW));
+	wc.lpszClassName = windowClass;
+	wc.lpfnWndProc = windowWndProc;
+	wc.hInstance = thishInstance;
+	wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
+	if (wc.hIcon == NULL) {
+		return lastErrorToHRESULT();
+	}
+	wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
+	if (wc.hCursor == NULL) {
+		return lastErrorToHRESULT();
+	}
+	// TODO(andlabs): change this to something else? NULL? the hollow brush?
+	wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
+	if (RegisterClassW(&wc) == 0) {
+		return lastErrorToHRESULT();
+	}
+	return S_OK;
+}
+
+HRESULT createWindow(HWND *phwnd) {
+	return (HRESULT) SendMessageW(utilityWindow, msgCreateWindow, 0, (LPARAM) phwnd);
+}
+
+LRESULT utilCreateWindow(HWND *phwnd) {
+	*phwnd = CreateWindowExW(0,
+		windowClass, L"Shiny Window",
+		WS_OVERLAPPEDWINDOW,
+		CW_USEDEFAULT, CW_USEDEFAULT,
+		CW_USEDEFAULT, CW_USEDEFAULT,
+		NULL, NULL, thishInstance, NULL);
+	if (*phwnd == NULL) {
+		return lastErrorToLRESULT();
+	}
+	// TODO(andlabs): use proper nCmdShow
+	ShowWindow(*phwnd, SW_SHOWDEFAULT);
+	// TODO(andlabs): UpdateWindow()?
+	return lS_OK;
+}
+
+HRESULT destroyWindow(HWND hwnd) {
+	return (HRESULT) SendMessageW(utilityWindow, msgDestroyWindow, (WPARAM) hwnd, 0);
+}
+
+LRESULT utilDestroyWindow(HWND hwnd) {
+	if (DestroyWindow(hwnd) == 0) {
+		return lastErrorToLRESULT();
+	}
+	return lS_OK;
+}
diff --git a/shiny/driver/windriver/window.go b/shiny/driver/windriver/window.go
new file mode 100644
index 0000000..e2eaa54
--- /dev/null
+++ b/shiny/driver/windriver/window.go
@@ -0,0 +1,66 @@
+// 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.
+
+package windriver
+
+// #include "windriver.h"
+import "C"
+
+import (
+	"image"
+	"image/color"
+	"image/draw"
+
+	"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/paint"
+)
+
+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)
+	}
+	return &window{
+		hwnd: hwnd,
+		pump: pump.Make(),
+	}, nil
+}
+
+func (w *window) Release() {
+	if w.hwnd == nil { // already released?
+		return
+	}
+	// 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
+}
diff --git a/shiny/driver/windriver/windriver.c b/shiny/driver/windriver/windriver.c
new file mode 100644
index 0000000..be6183d
--- /dev/null
+++ b/shiny/driver/windriver/windriver.c
@@ -0,0 +1,25 @@
+// 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.
+
+#include "windriver.h"
+
+void mainMessagePump(void) {
+	MSG msg;
+
+	// This GetMessage cannot fail: http://blogs.msdn.com/b/oldnewthing/archive/2013/03/22/10404367.aspx
+	// TODO(andlabs): besides, what should we do if a future Windows change makes it fail for some other reason? we can't return an error because it's too late to stop the main function
+	while (GetMessage(&msg, NULL, 0, 0)) {
+		TranslateMessage(&msg);
+		DispatchMessage(&msg);
+	}
+}
+
+HRESULT lastErrorToHRESULT(void) {
+	DWORD le;
+
+	le = GetLastError();
+	if (le == 0)
+		return E_FAIL;
+	return HRESULT_FROM_WIN32(le);
+}
diff --git a/shiny/driver/windriver/windriver.go b/shiny/driver/windriver/windriver.go
new file mode 100644
index 0000000..5964882
--- /dev/null
+++ b/shiny/driver/windriver/windriver.go
@@ -0,0 +1,76 @@
+// 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.
+
+package windriver
+
+// #include "windriver.h"
+import "C"
+
+import (
+	"image"
+	"runtime"
+
+	"golang.org/x/exp/shiny/screen"
+)
+
+// TODO(andlabs): Should the Windows API code be split into a
+// separate package internal/winbackend so gldriver can use it too?
+
+// Main is called by the program's main function to run the graphical
+// application.
+//
+// It calls f on the Screen, possibly in a separate goroutine, as some OS-
+// specific libraries require being on 'the main thread'. It returns when f
+// returns.
+func Main(f func(screen.Screen)) {
+	if err := main(f); err != nil {
+		f(errScreen{err})
+	}
+}
+
+func main(f func(screen.Screen)) (retErr error) {
+	// It does not matter which OS thread we are on.
+	// All that matters is that we confine all UI operations
+	// to the thread that created the respective window.
+	runtime.LockOSThread()
+
+	hr := C.initUtilityWindow()
+	if hr != C.S_OK {
+		return winerror("failed to create utility window", hr)
+	}
+	defer func() {
+		// TODO(andlabs): log an error if this fails?
+		C.DestroyWindow(C.utilityWindow)
+		// TODO(andlabs): unregister window class
+	}()
+
+	hr = C.initWindowClass()
+	if hr != C.S_OK {
+		return winerror("failed to create Window window class", hr)
+	}
+	// TODO(andlabs): uninit
+
+	s := newScreenImpl()
+	go f(s)
+
+	C.mainMessagePump()
+	return nil
+}
+
+// errScreen is a screen.Screen.
+type errScreen struct {
+	err error
+}
+
+func (e errScreen) NewBuffer(size image.Point) (screen.Buffer, error) {
+	return nil, e.err
+}
+
+func (e errScreen) NewTexture(size image.Point) (screen.Texture, error) {
+	return nil, e.err
+}
+
+func (e errScreen) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) {
+	return nil, e.err
+}
diff --git a/shiny/driver/windriver/windriver.h b/shiny/driver/windriver/windriver.h
new file mode 100644
index 0000000..89acace
--- /dev/null
+++ b/shiny/driver/windriver/windriver.h
@@ -0,0 +1,56 @@
+// 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
+
+#define UNICODE
+#define _UNICODE
+#define STRICT
+#define STRICT_TYPED_ITEMIDS
+#define CINTERFACE
+#define COBJMACROS
+// see https://github.com/golang/go/issues/9916#issuecomment-74812211
+#define INITGUID
+// get Windows version right; right now Windows XP
+#define WINVER 0x0501
+#define _WIN32_WINNT 0x0501
+#define _WIN32_WINDOWS 0x0501		/* according to Microsoft's winperf.h */
+#define _WIN32_IE 0x0600			/* according to Microsoft's sdkddkver.h */
+#define NTDDI_VERSION 0x05010000	/* according to Microsoft's sdkddkver.h */
+#include <windows.h>
+
+// see http://blogs.msdn.com/b/oldnewthing/archive/2004/10/25/247180.aspx
+// this will work on MinGW too
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+#define thishInstance ((HINSTANCE) (&__ImageBase))
+
+// messages sent to the utility window to do the various functions of the package on the UI thread
+// we start at WM_USER + 0x40 to make room for the DM_* messages
+enum {
+	// wParam - 0
+	// lParam - pointer to store HWND in
+	// return - error LRESULT
+	msgCreateWindow = WM_USER + 0x40,
+	// wParam - hwnd
+	// lParam - 0
+	// return - error LRESULT
+	msgDestroyWindow,
+};
+
+// windriver.c
+extern void mainMessagePump(void);
+extern HRESULT lastErrorToHRESULT(void);
+#define lS_OK ((LRESULT) S_OK)
+#define lastErrorToLRESULT() ((LRESULT) lastErrorToHRESULT())
+
+// utilwindow.c
+extern HWND utilityWindow;
+extern HRESULT initUtilityWindow(void);
+
+// window.c
+extern HRESULT initWindowClass(void);
+extern HRESULT createWindow(HWND *);
+extern LRESULT utilCreateWindow(HWND *);
+extern HRESULT destroyWindow(HWND);
+extern LRESULT utilDestroyWindow(HWND);