Finished writing initial Windows implementation. Now to find out why it doesn't work...
This commit is contained in:
parent
c55386f929
commit
b944b6d4d8
|
@ -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)
|
||||||
|
}
|
|
@ -4,3 +4,24 @@ package ui
|
||||||
|
|
||||||
// wfunc kernel32 GetModuleHandleW *uint16 uintptr
|
// wfunc kernel32 GetModuleHandleW *uint16 uintptr
|
||||||
// wfunc kernel32 GetStartupInfoW *s_STARTUPINFOW void
|
// 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
|
||||||
|
|
|
@ -32,9 +32,27 @@ func getWinMainParams() (err error) {
|
||||||
return nil
|
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 {
|
func initWindows() error {
|
||||||
if err := getWinMainParams(); err != nil {
|
if err := getWinMainParams(); err != nil {
|
||||||
return fmt.Errorf("error getting WinMain() parameters: %v", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
|
@ -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}
|
|
@ -172,6 +172,22 @@ var gwlpNames = map[string]string{
|
||||||
"amd64": "etWindowLongPtrW",
|
"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
|
const outTemplate = `package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -202,6 +218,7 @@ var handleOverrides = []string{
|
||||||
"HICON",
|
"HICON",
|
||||||
"HCURSOR",
|
"HCURSOR",
|
||||||
"HBRUSH",
|
"HBRUSH",
|
||||||
|
"HMENU",
|
||||||
// These are all pointers to functions; handle them identically to handles.
|
// These are all pointers to functions; handle them identically to handles.
|
||||||
"WNDPROC",
|
"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_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_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_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
|
// functions
|
||||||
{{range .Funcs}} fmt.Fprintf(buf, "%s\n", {{printf "%q" .}})
|
{{range .Funcs}} fmt.Fprintf(buf, "%s\n", {{printf "%q" .}})
|
||||||
|
@ -332,6 +351,9 @@ func main() {
|
||||||
sort.Strings(structs)
|
sort.Strings(structs)
|
||||||
sort.Strings(sorteddlls)
|
sort.Strings(sorteddlls)
|
||||||
|
|
||||||
|
// and finally
|
||||||
|
genGetSetWindowLongPtr(targetarch)
|
||||||
|
|
||||||
// thanks to james4k in irc.freenode.net/#go-nuts
|
// thanks to james4k in irc.freenode.net/#go-nuts
|
||||||
tmpdir, err := ioutil.TempDir("", "windowsconstgen")
|
tmpdir, err := ioutil.TempDir("", "windowsconstgen")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue