diff --git a/redo/controls_windows.c b/redo/controls_windows.c new file mode 100644 index 0000000..dadce75 --- /dev/null +++ b/redo/controls_windows.c @@ -0,0 +1,71 @@ +/* 17 july 2014 */ + +#include "winapi_windows.h" + +HWND newWidget(LPCWSTR class, DWORD style, DWORD extstyle) +{ + HWND hwnd; + + hwnd = CreateWindowExW( + extstyle, + class, L"", + style | WS_CHILD | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + /* + the following has the consequence of making the control message-only at first + this shouldn't cause any problems... hopefully not + but see the msgwndproc() for caveat info + also don't use low control IDs as they will conflict with dialog boxes (IDCANCEL, etc.) + */ + msgwin, (HMENU) 100, hInstance, NULL); + if (hwnd == NULL) { + xpanic("error creating control", GetLastError()); + return hwnd; +} + +void controlSetParent(HWND control, HWND parent) +{ + if (SetParent(control, parent) == NULL) + xpanic("error changing control parent", GetLastError()); +} + +/* +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 +*/ +LRESULT forwardCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HWND control = (HWND) lParam; + + /* don't generate an event if the control (if there is one) is unparented (a child of the message-only window) */ + if (control != NULL && IsChild(msgwin, control) == 0) + return SendMessageW(control, msgCOMMAND, wParam, lParam); + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +static LRESULT CALLBACK buttonSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR data) +{ + switch (uMsg) { + case msgCOMMAND: + if (HIWORD(wParam) == BN_CLICKED) { + buttonClicked(data); + return 0; + } + return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam); + case WM_NCDESTROY: + if ((*fv_RemoveWindowSubclass)(hwnd, buttonSubProc, id) == FALSE) + xpanic("error removing Button subclass (which was for its own event handler)", GetLastError()); + return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam); + default: + return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam); + } + xmissedmsg("Button", "buttonSubProc()", uMsg); + return 0; /* unreached */ +} + +void setButtonSubclass(HWND hwnd, void *data) +{ + if ((*fv_SetWindowSubclass)(hwnd, buttonSubProc, 0, (DWORD_PTR) data) == FALSE) + xpanic("error subclassing Button to give it its own event handler", GetLastError()); +} diff --git a/redo/controls_windows.go b/redo/controls_windows.go index 299ce99..8f82eac 100644 --- a/redo/controls_windows.go +++ b/redo/controls_windows.go @@ -8,47 +8,27 @@ import ( "unsafe" ) +// #include "winapi_windows.h" +import "C" + type widgetbase struct { - hwnd uintptr + hwnd C.HWND } -var emptystr = syscall.StringToUTF16Ptr("") - -func newWidget(class *uint16, style uintptr, extstyle uintptr) *widgetbase { - hwnd, err := f_CreateWindowExW( - extstyle, - class, emptystr, - style | c_WS_CHILD | c_WS_VISIBLE, - c_CW_USEDEFAULT, c_CW_USEDEFAULT, -// c_CW_USEDEFAULT, c_CW_USEDEFAULT, -100,100, - // the following has the consequence of making the control message-only at first - // this shouldn't cause any problems... hopefully not - // but see the msgwndproc() for caveat info - // also don't use low control IDs as they will conflict with dialog boxes (IDCANCEL, etc.) - msgwin, 100, hInstance, nil) - if hwnd == hNULL { - panic(fmt.Errorf("creating control of class %q failed: %v", class, err)) - } +func newWidget(class C.LPCWSTR, style uintptr, extstyle uintptr) *widgetbase { return &widgetbase{ - hwnd: hwnd, + hwnd: C.newWidget(class, style, extstyle), } } // these few methods are embedded by all the various Controls since they all will do the same thing func (w *widgetbase) unparent() { - res, err := f_SetParent(w.hwnd, msgwin) - if res == hNULL { // result type is HWND - panic(fmt.Errorf("error unparenting control: %v", err)) - } + C.controlSetParent(w.hwnd, C.msgwin) } func (w *widgetbase) parent(win *window) { - res, err := f_SetParent(w.hwnd, win.hwnd) - if res == hNULL { // result type is HWND - panic(fmt.Errorf("error parenting control: %v", err)) - } + C.controlSetParent(w.hwnd, win.hwnd) } // don't embed these as exported; let each Control decide if it should @@ -57,7 +37,7 @@ func (w *widgetbase) text() *Request { c := make(chan interface{}) return &Request{ op: func() { - c <- getWindowText(w.hwnd) + c <- C.GoString(C.getWindowText(w.hwnd)) }, resp: c, } @@ -67,47 +47,33 @@ func (w *widgetbase) settext(text string, results ...t_LRESULT) *Request { c := make(chan interface{}) return &Request{ op: func() { - setWindowText(w.hwnd, text, append([]t_LRESULT{c_FALSE}, results...)) + C.setWindowText(w.hwnd, toUTF16(text)) c <- struct{}{} }, resp: c, } } -// 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") +var buttonclass = toUTF16("BUTTON") func newButton(text string) *Request { c := make(chan interface{}) return &Request{ op: func() { w := newWidget(buttonclass, - c_BS_PUSHBUTTON | c_WS_TABSTOP, + C.BS_PUSHBUTTON | C.WS_TABSTOP, 0) - setWindowText(w.hwnd, text, []t_LRESULT{c_FALSE}) + C.setWindowText(w.hwnd, toUTF16(text)) b := &button{ widgetbase: w, clicked: newEvent(), } - res, err := f_SetWindowSubclass(w.hwnd, buttonsubprocptr, 0, t_DWORD_PTR(uintptr(unsafe.Pointer(b)))) - if res == c_FALSE { - panic(fmt.Errorf("error subclassing Button to give it its own event handler: %v", err)) - } + C.setButtonSubclass(w.hwnd, unsafe.Pointer(b)) c <- b }, resp: c, @@ -140,24 +106,9 @@ func init() { 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 - } - return f_DefSubclassProc(hwnd, uMsg, wParam, lParam) - case c_WM_NCDESTROY: - res, err := f_RemoveWindowSubclass(b.hwnd, buttonsubprocptr, id) - if res == c_FALSE { - panic(fmt.Errorf("error removing Button subclass (which was for its own event handler): %v", err)) - } - return f_DefSubclassProc(hwnd, uMsg, wParam, lParam) - default: - return f_DefSubclassProc(hwnd, uMsg, wParam, lParam) - } - panic(fmt.Errorf("Button message %d does not return a value (bug in buttonSubProc())", uMsg)) +//export buttonClicked +func buttonClicked(data unsafe.Pointer) { + b := (*button)(data) + b.clicked.fire() + println("button clicked") } diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 81bf271..ea3c745 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -32,3 +32,9 @@ extern DWORD initCommonControls(LPCWSTR, char **); extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); extern LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM); + +/* controls_windows.c */ +extern HWND newWidget(LPCWSTR, DWORD, DWORD); +extern void controlSetParent(HWND, HWND); +extern LRESULT forwardCommand(HWND, UINT, WPARAM, LPARAM); +extern void setButtonSubclass(HWND, void *);