From 19f7b2946a6198862f860b1f7c5798c0d66b8fc1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 Jul 2014 15:10:26 -0400 Subject: [PATCH] Laid down the framework for control events on Windows. The only problem left is that we need to use functions from comctl32.dll, so it's time to bring that blob of code back. --- redo/common_windows.go | 10 +++++++++ redo/controls_windows.go | 45 +++++++++++++++++++++++++++++++++++++-- redo/funcnames_windows.go | 1 + redo/uitask_windows.go | 3 +++ redo/window_windows.go | 3 +++ redo/zwinconstgen.go | 2 ++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/redo/common_windows.go b/redo/common_windows.go index d2cae60..30c0d75 100644 --- a/redo/common_windows.go +++ b/redo/common_windows.go @@ -46,3 +46,13 @@ func storelpParam(hwnd uintptr, lParam t_LPARAM) { cs = (*s_CREATESTRUCTW)(unsafe.Pointer(uintptr(lParam))) f_SetWindowLongPtrW(hwnd, c_GWLP_USERDATA, cs.lpCreateParams) } + +func (w t_WPARAM) HIWORD() uint16 { + u := uintptr(w) & 0xFFFF0000 + return uint16(u >> 16) +} + +func (w t_WPARAM) LOWORD() uint16 { + u := uintptr(w) & 0x0000FFFF + return uint16(u) +} diff --git a/redo/controls_windows.go b/redo/controls_windows.go index 3b73b48..dd4b8c9 100644 --- a/redo/controls_windows.go +++ b/redo/controls_windows.go @@ -5,6 +5,7 @@ package ui import ( "fmt" "syscall" + "unsafe" ) type widgetbase struct { @@ -73,8 +74,20 @@ func (w *widgetbase) settext(text string, results ...t_LRESULT) *Request { } } +// all controls that have events receive the events themselves through subclasses +// to do this, all windows (including the message-only window; see http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q104069) forward WM_COMMAND to each control with this function +func forwardCommand(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t_LRESULT { + control := uintptr(lParam) + // don't generate an event if the control (if there is one) is unparented (a child of the message-only window) + if control != hNULL && f_IsChild(msgwin, control) == 0 { + return f_SendMessageW(control, msgCOMMAND, wParam, lParam) + } + return f_DefWindowProcW(hwnd, uMsg, wParam, lParam) +} + type button struct { *widgetbase + clicked *event } var buttonclass = syscall.StringToUTF16Ptr("BUTTON") @@ -89,6 +102,7 @@ func newButton(text string) *Request { setWindowText(w.hwnd, text, []t_LRESULT{c_FALSE}) c <- &button{ widgetbase: w, + clicked: newEvent(), } }, resp: c, @@ -96,8 +110,14 @@ func newButton(text string) *Request { } func (b *button) OnClicked(e func(c Doer)) *Request { - // TODO - return nil + c := make(chan interface{}) + return &Request{ + op: func() { + b.clicked.set(e) + c <- struct{}{} + }, + resp: c, + } } func (b *button) Text() *Request { @@ -107,3 +127,24 @@ func (b *button) Text() *Request { func (b *button) SetText(text string) *Request { return b.settext(text) } + +var buttonsubprocptr = syscall.NewCallback(buttonSubProc) + +func buttonSubProc(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM, id t_UINT_PTR, data t_DWORD_PTR) t_LRESULT { + b := (*button)(unsafe.Pointer(uintptr(data))) + switch uMsg { + case msgCOMMAND: + if wParam.HIWORD() == c_BN_CLICKED { + b.clicked.fire() + println("button clicked") + return 0 + } + // TODO return + case c_WM_NCDESTROY: + // TODO remove + // TODO return + default: + // TODO return + } + panic(fmt.Errorf("Button message %d does not return a value (bug in buttonSubProc())", uMsg)) +} diff --git a/redo/funcnames_windows.go b/redo/funcnames_windows.go index 4a12430..005607f 100644 --- a/redo/funcnames_windows.go +++ b/redo/funcnames_windows.go @@ -38,3 +38,4 @@ package ui // wfunc user32 GetDC uintptr uintptr // wfunc gdi32 SelectObject uintptr uintptr uintptr // wfunc user32 ReleaseDC uintptr uintptr uintptr +// wfunc user32 IsChild uintptr uintptr uintptr,noerr diff --git a/redo/uitask_windows.go b/redo/uitask_windows.go index 6c5b974..8e8187a 100644 --- a/redo/uitask_windows.go +++ b/redo/uitask_windows.go @@ -11,6 +11,7 @@ import ( // global messages unique to everything const ( msgRequest = c_WM_APP + 1 + iota // + 1 just to be safe + msgCOMMAND // WM_COMMAND proxy; see forwardCommand() in controls_windows.go ) var msgwin uintptr @@ -91,6 +92,8 @@ func makemsgwin() error { func msgwinproc(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t_LRESULT { switch uMsg { + case c_WM_COMMAND: + return forwardCommand(hwnd, uMsg, wParam, lParam) case msgRequest: req := (*Request)(unsafe.Pointer(uintptr(lParam))) perform(req) diff --git a/redo/window_windows.go b/redo/window_windows.go index 9bf0b26..c58d028 100644 --- a/redo/window_windows.go +++ b/redo/window_windows.go @@ -160,6 +160,7 @@ func (w *window) OnClosing(e func(Doer) bool) *Request { } } +// TODO msg -> uMsg 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 { @@ -173,6 +174,8 @@ func windowWndProc(hwnd uintptr, msg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t return f_DefWindowProcW(hwnd, msg, wParam, lParam) } switch msg { + case c_WM_COMMAND: + return forwardCommand(hwnd, msg, wParam, lParam) case c_WM_SIZE: var r s_RECT diff --git a/redo/zwinconstgen.go b/redo/zwinconstgen.go index 28ff843..c0e2171 100644 --- a/redo/zwinconstgen.go +++ b/redo/zwinconstgen.go @@ -276,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)))) + fmt.Fprintf(buf, "type t_UINT_PTR %s\n", winName(reflect.TypeOf(C.UINT_PTR(0)))) + fmt.Fprintf(buf, "type t_DWORD_PTR %s\n", winName(reflect.TypeOf(C.DWORD_PTR(0)))) // and one for GetMessageW() fmt.Fprintf(buf, "type t_BOOL %s\n", winName(reflect.TypeOf(C.BOOL(0))))