summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--redo/common_windows.go48
-rw-r--r--redo/funcnames_windows.go21
-rw-r--r--redo/init_windows.go18
-rw-r--r--redo/uitask_windows.go98
-rw-r--r--redo/window_windows.go166
-rw-r--r--redo/zwinconstgen.go22
6 files changed, 373 insertions, 0 deletions
diff --git a/redo/common_windows.go b/redo/common_windows.go
new file mode 100644
index 0000000..d2cae60
--- /dev/null
+++ b/redo/common_windows.go
@@ -0,0 +1,48 @@
+// 12 july 2014
+
+package ui
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+)
+
+// TODO get rid of this when we actually use s_POINT somewhere
+var dummyToFoolwinconstgen s_POINT
+
+func getWindowText(hwnd uintptr) string {
+ // WM_GETTEXTLENGTH and WM_GETTEXT return the count /without/ the terminating null character
+ // but WM_GETTEXT expects the buffer size handed to it to /include/ the terminating null character
+ n := f_SendMessageW(hwnd, c_WM_GETTEXTLENGTH, 0, 0)
+ buf := make([]uint16, int(n + 1))
+ if f_SendMessageW(hwnd, c_WM_GETTEXT,
+ t_WPARAM(n + 1), t_LPARAM(uintptr(unsafe.Pointer(&buf[0])))) != n {
+ panic(fmt.Errorf("WM_GETTEXT did not copy exactly %d characters out", n))
+ }
+ return syscall.UTF16ToString(buf)
+}
+
+func setWindowText(hwnd uintptr, text string, errors []t_LRESULT) {
+ res := f_SendMessageW(hwnd, c_WM_SETTEXT,
+ 0, t_LPARAM(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text)))))
+ for _, err := range errors {
+ if res == err {
+ panic(fmt.Errorf("WM_SETTEXT failed; error code %d", res))
+ }
+ }
+}
+
+func updateWindow(hwnd uintptr, caller string) {
+ res, err := f_UpdateWindow(hwnd)
+ if res == 0 {
+ panic(fmt.Errorf("error calling UpdateWindow() from %s: %v", caller, err))
+ }
+}
+
+func storelpParam(hwnd uintptr, lParam t_LPARAM) {
+ var cs *s_CREATESTRUCTW
+
+ cs = (*s_CREATESTRUCTW)(unsafe.Pointer(uintptr(lParam)))
+ f_SetWindowLongPtrW(hwnd, c_GWLP_USERDATA, cs.lpCreateParams)
+}
diff --git a/redo/funcnames_windows.go b/redo/funcnames_windows.go
index 02b85ca..ce334f2 100644
--- a/redo/funcnames_windows.go
+++ b/redo/funcnames_windows.go
@@ -4,3 +4,24 @@ package ui
// wfunc kernel32 GetModuleHandleW *uint16 uintptr
// wfunc kernel32 GetStartupInfoW *s_STARTUPINFOW void
+// wfunc user32 LoadIconW uintptr uintptr uintptr
+// wfunc user32 LoadCursorW uintptr uintptr uintptr
+// wfunc user32 GetMessageW *s_MSG uintptr t_UINT t_UINT t_BOOL
+
+// these two don't technically return void but let's pretend they do since their return values are irrelevant/not indicative of anything useful
+// wfunc user32 TranslateMessage *s_MSG void
+// wfunc user32 DispatchMessageW *s_MSG void
+
+// wfunc user32 PostMessageW uintptr t_UINT t_WPARAM t_LPARAM uintptr
+// wfunc user32 RegisterClassW *s_WNDCLASSW uintptr
+
+// TODO narrow down argument types
+// wfunc user32 CreateWindowExW uintptr *uint16 *uint16 uintptr uintptr uintptr uintptr uintptr uintptr uintptr uintptr unsafe.Pointer uintptr
+
+// wfunc user32 DefWindowProcW uintptr t_UINT t_WPARAM t_LPARAM t_LRESULT,noerr
+
+// this one doesn't technically return void but let's pretend it does since its return value is irrelevant/not indicative of anything useful
+// wfunc user32 ShowWindow uintptr uintptr void
+
+// wfunc user32 SendMessageW uintptr t_UINT t_WPARAM t_LPARAM t_LRESULT,noerr
+// wfunc user32 UpdateWindow uintptr uintptr
diff --git a/redo/init_windows.go b/redo/init_windows.go
index 4375cee..1ce0e65 100644
--- a/redo/init_windows.go
+++ b/redo/init_windows.go
@@ -32,9 +32,27 @@ func getWinMainParams() (err error) {
return nil
}
+// TODO move to common_windows.go
+var hNULL uintptr = 0
+
+func loadIconsCursors() (err error) {
+ hDefaultIcon, err = f_LoadIconW(hNULL, c_IDI_APPLICATION)
+ if hDefaultIcon == hNULL {
+ return fmt.Errorf("error loading default icon: %v", err)
+ }
+ hArrowCursor, err = f_LoadCursorW(hNULL, c_IDC_ARROW)
+ if hArrowCursor == hNULL {
+ return fmt.Errorf("error loading arrow (default) cursor: %v", err)
+ }
+ return nil
+}
+
func initWindows() error {
if err := getWinMainParams(); err != nil {
return fmt.Errorf("error getting WinMain() parameters: %v", err)
}
+ if err := loadIconsCursors(); err != nil {
+ return fmt.Errorf("error loading standard/default icons and cursors: %v", err)
+ }
return nil
}
diff --git a/redo/uitask_windows.go b/redo/uitask_windows.go
new file mode 100644
index 0000000..0c43f36
--- /dev/null
+++ b/redo/uitask_windows.go
@@ -0,0 +1,98 @@
+// 12 july 2014
+
+package ui
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+)
+
+// global messages unique to everything
+const (
+ msgRequest = c_WM_APP + 1 + iota // + 1 just to be safe
+)
+
+var msgwin uintptr
+
+func uiinit() error {
+ if err := initWindows(); err != nil {
+ return fmt.Errorf("error initializing package ui on Windows: %v", err)
+ }
+ if err := makemsgwin(); err != nil {
+ return fmt.Errorf("error creating message-only window: %v", err)
+ }
+ if err := makeWindowWindowClass(); err != nil {
+ return fmt.Errorf("error creating Window window class: %v", err)
+ }
+ return nil
+}
+
+func uimsgloop() {
+ var msg s_MSG
+
+ for {
+ res, err := f_GetMessageW(&msg, hNULL, 0, 0)
+ if res < 0 {
+ panic(fmt.Errorf("error calling GetMessage(): %v", err))
+ }
+ if res == 0 { // WM_QUIT
+ break
+ }
+ // TODO IsDialogMessage()
+ f_TranslateMessage(&msg)
+ f_DispatchMessageW(&msg)
+ }
+}
+
+func issue(req *Request) {
+ res, err := f_PostMessageW(
+ hNULL, // TODO
+ msgRequest,
+ 0,
+ t_LPARAM(uintptr(unsafe.Pointer(req))))
+ if res == 0 {
+ panic(fmt.Errorf("error issuing request: %v", err))
+ }
+}
+
+const msgwinclass = "gouimsgwin"
+
+func makemsgwin() error {
+ var wc s_WNDCLASSW
+
+ wc.lpfnWndProc = syscall.NewCallback(msgwinproc)
+ wc.hInstance = hInstance
+ wc.hIcon = hDefaultIcon
+ wc.hCursor = hArrowCursor
+ wc.hbrBackground = c_COLOR_BTNFACE + 1
+ wc.lpszClassName = syscall.StringToUTF16Ptr(msgwinclass)
+ res, err := f_RegisterClassW(&wc)
+ if res == 0 {
+ return fmt.Errorf("error registering message-only window class: %v", err)
+ }
+ msgwin, err = f_CreateWindowExW(
+ 0,
+ wc.lpszClassName,
+ syscall.StringToUTF16Ptr("package ui message-only window"),
+ 0,
+ c_CW_USEDEFAULT, c_CW_USEDEFAULT,
+ c_CW_USEDEFAULT, c_CW_USEDEFAULT,
+ c_HWND_MESSAGE, hNULL, hInstance, nil)
+ if msgwin == hNULL {
+ return fmt.Errorf("error creating message-only window: %v", err)
+ }
+ return nil
+}
+
+func msgwinproc(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t_LRESULT {
+ switch uMsg {
+ case msgRequest:
+ req := (*Request)(unsafe.Pointer(uintptr(lParam)))
+ perform(req)
+ return 0
+ default:
+ return f_DefWindowProcW(hwnd, uMsg, wParam, lParam)
+ }
+ panic(fmt.Errorf("message-only window procedure does not return a value for message %d (bug in msgwinproc())", uMsg))
+}
diff --git a/redo/window_windows.go b/redo/window_windows.go
new file mode 100644
index 0000000..aba3232
--- /dev/null
+++ b/redo/window_windows.go
@@ -0,0 +1,166 @@
+// 12 july 2014
+
+package ui
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+)
+
+type window struct {
+ hwnd uintptr
+ shownbefore bool
+
+ closing *event
+}
+
+const windowclassname = "gouiwindow"
+var windowclassptr = syscall.StringToUTF16Ptr(windowclassname)
+
+func makeWindowWindowClass() error {
+ var wc s_WNDCLASSW
+
+ wc.lpfnWndProc = syscall.NewCallback(windowWndProc)
+ wc.hInstance = hInstance
+ wc.hIcon = hDefaultIcon
+ wc.hCursor = hArrowCursor
+ wc.hbrBackground = c_COLOR_BTNFACE + 1
+ wc.lpszClassName = windowclassptr
+ res, err := f_RegisterClassW(&wc)
+ if res == 0 {
+ return fmt.Errorf("error registering Window window class: %v", err)
+ }
+ return nil
+}
+
+func newWindow(title string, width int, height int) *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ w := &window{
+ // hwnd set in WM_CREATE handler
+ closing: newEvent(),
+ }
+ hwnd, err := f_CreateWindowExW(
+ 0,
+ windowclassptr,
+ syscall.StringToUTF16Ptr(title),
+ c_WS_OVERLAPPED,
+ c_CW_USEDEFAULT, c_CW_USEDEFAULT,
+ uintptr(width), uintptr(height),
+ hNULL, hNULL, hInstance, unsafe.Pointer(w))
+ if hwnd == hNULL {
+ panic(fmt.Errorf("Window creation failed: %v", err))
+ } else if hwnd != w.hwnd {
+ panic(fmt.Errorf("inconsistency: hwnd returned by CreateWindowEx() (%p) and hwnd stored in window (%p) differ", hwnd, w.hwnd))
+ }
+ c <- w
+ },
+ resp: c,
+ }
+}
+
+func (w *window) SetControl(control Control) *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ // TODO unparent
+ // TODO reparent
+ c <- struct{}{}
+ },
+ resp: c,
+ }
+}
+
+func (w *window) Title() *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ c <- getWindowText(w.hwnd)
+ },
+ resp: c,
+ }
+}
+
+func (w *window) SetTitle(title string) *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ setWindowText(w.hwnd, title, []t_LRESULT{c_FALSE})
+ c <- struct{}{}
+ },
+ resp: c,
+ }
+}
+
+func (w *window) Show() *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ if !w.shownbefore {
+ // TODO get rid of need for cast
+ f_ShowWindow(w.hwnd, uintptr(nCmdShow))
+ updateWindow(w.hwnd, "Window.Show()")
+ w.shownbefore = true
+ } else {
+ f_ShowWindow(w.hwnd, c_SW_SHOW)
+ }
+ c <- struct{}{}
+ },
+ resp: c,
+ }
+}
+
+func (w *window) Hide() *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ f_ShowWindow(w.hwnd, c_SW_HIDE)
+ c <- struct{}{}
+ },
+ resp: c,
+ }
+}
+
+func (w *window) Close() *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ // TODO
+ c <- struct{}{}
+ },
+ resp: c,
+ }
+}
+
+func (w *window) OnClosing(e func(Doer) bool) *Request {
+ c := make(chan interface{})
+ return &Request{
+ op: func() {
+ w.closing.setbool(e)
+ c <- struct{}{}
+ },
+ resp: c,
+ }
+}
+
+func windowWndProc(hwnd uintptr, msg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t_LRESULT {
+ w := (*window)(unsafe.Pointer(f_GetWindowLongPtrW(hwnd, c_GWLP_USERDATA)))
+ if w == nil {
+ // the lpParam is available during WM_NCCREATE and WM_CREATE
+ if msg == c_WM_NCCREATE {
+ storelpParam(hwnd, lParam)
+ }
+ // act as if we're not ready yet, even during WM_NCCREATE (nothing important to the switch statement below happens here anyway)
+ return f_DefWindowProcW(hwnd, msg, wParam, lParam)
+ }
+ switch msg {
+ default:
+ return f_DefWindowProcW(hwnd, msg, wParam, lParam)
+ }
+ panic(fmt.Errorf("Window message %d does not return a value (bug in windowWndProc())", msg))
+}
+
+// TODO
+func newButton(string)*Request{return nil} \ No newline at end of file
diff --git a/redo/zwinconstgen.go b/redo/zwinconstgen.go
index 07c5959..28ff843 100644
--- a/redo/zwinconstgen.go
+++ b/redo/zwinconstgen.go
@@ -172,6 +172,22 @@ var gwlpNames = map[string]string{
"amd64": "etWindowLongPtrW",
}
+// in reality these use LONG_PTR for the actual values; LONG_PTR is a signed value, but for our use case it doesn't really matter
+func genGetSetWindowLongPtr(targetarch string) {
+ name := gwlpNames[targetarch]
+
+ funcs = append(funcs, fmt.Sprintf("var fv_GetWindowLongPtrW = user32.NewProc(%q)", "G" + name))
+ funcs = append(funcs, "func f_GetWindowLongPtrW(hwnd uintptr, which uintptr) uintptr {")
+ funcs = append(funcs, "\tres, _, _ := fv_GetWindowLongPtrW.Call(hwnd, which)")
+ funcs = append(funcs, "\treturn res")
+ funcs = append(funcs, "}")
+
+ funcs = append(funcs, fmt.Sprintf("var fv_SetWindowLongPtrW = user32.NewProc(%q)", "S" + name))
+ funcs = append(funcs, "func f_SetWindowLongPtrW(hwnd uintptr, which uintptr, value uintptr) {")
+ funcs = append(funcs, "\tfv_SetWindowLongPtrW.Call(hwnd, which, value)")
+ funcs = append(funcs, "}")
+}
+
const outTemplate = `package main
import (
"fmt"
@@ -202,6 +218,7 @@ var handleOverrides = []string{
"HICON",
"HCURSOR",
"HBRUSH",
+ "HMENU",
// These are all pointers to functions; handle them identically to handles.
"WNDPROC",
}
@@ -259,6 +276,8 @@ func main() {
fmt.Fprintf(buf, "type t_WPARAM %s\n", winName(reflect.TypeOf(C.WPARAM(0))))
fmt.Fprintf(buf, "type t_LPARAM %s\n", winName(reflect.TypeOf(C.LPARAM(0))))
fmt.Fprintf(buf, "type t_LRESULT %s\n", winName(reflect.TypeOf(C.LRESULT(0))))
+ // and one for GetMessageW()
+ fmt.Fprintf(buf, "type t_BOOL %s\n", winName(reflect.TypeOf(C.BOOL(0))))
// functions
{{range .Funcs}} fmt.Fprintf(buf, "%s\n", {{printf "%q" .}})
@@ -332,6 +351,9 @@ func main() {
sort.Strings(structs)
sort.Strings(sorteddlls)
+ // and finally
+ genGetSetWindowLongPtr(targetarch)
+
// thanks to james4k in irc.freenode.net/#go-nuts
tmpdir, err := ioutil.TempDir("", "windowsconstgen")
if err != nil {