2014-02-08 22:51:11 -06:00
// 8 february 2014
2014-03-12 20:55:45 -05:00
2014-02-19 10:41:10 -06:00
package ui
2014-02-08 22:51:11 -06:00
import (
2014-02-11 16:38:38 -06:00
"fmt"
2014-02-08 22:51:11 -06:00
"syscall"
"unsafe"
2014-02-12 19:51:07 -06:00
"sync"
)
const (
stdWndClassFormat = "gouiwnd%X"
2014-02-08 22:51:11 -06:00
)
2014-02-11 18:52:39 -06:00
var (
2014-02-12 19:51:07 -06:00
curWndClassNum uintptr
curWndClassNumLock sync . Mutex
2014-02-11 16:38:38 -06:00
)
var (
2014-05-30 13:13:47 -05:00
_defWindowProc = user32 . NewProc ( "DefWindowProcW" )
2014-02-11 16:38:38 -06:00
)
2014-05-30 13:13:47 -05:00
func defWindowProc ( hwnd _HWND , uMsg uint32 , wParam _WPARAM , lParam _LPARAM ) _LRESULT {
r1 , _ , _ := _defWindowProc . Call (
uintptr ( hwnd ) ,
uintptr ( uMsg ) ,
uintptr ( wParam ) ,
uintptr ( lParam ) )
return _LRESULT ( r1 )
}
2014-05-30 16:59:29 -05:00
// don't worry about error returns from GetWindowLongPtr()/SetWindowLongPtr()
// see comments of http://blogs.msdn.com/b/oldnewthing/archive/2014/02/03/10496248.aspx
func getWindowLongPtr ( hwnd _HWND , what uintptr ) uintptr {
r1 , _ , _ := _getWindowLongPtr . Call (
uintptr ( hwnd ) ,
what )
return r1
}
func setWindowLongPtr ( hwnd _HWND , what uintptr , value uintptr ) {
_setWindowLongPtr . Call (
uintptr ( hwnd ) ,
what ,
value )
}
// we can store a pointer in extra space provided by Windows
// we'll store sysData there
// see http://blogs.msdn.com/b/oldnewthing/archive/2005/03/03/384285.aspx
func getSysData ( hwnd _HWND ) * sysData {
return ( * sysData ) ( unsafe . Pointer ( getWindowLongPtr ( hwnd , negConst ( _GWLP_USERDATA ) ) ) )
}
func storeSysData ( hwnd _HWND , uMsg uint32 , wParam _WPARAM , lParam _LPARAM ) _LRESULT {
// we can get the lpParam from CreateWindowEx() in WM_NCCREATE and WM_CREATE
// we can freely skip any messages that come prior
// see http://blogs.msdn.com/b/oldnewthing/archive/2005/04/22/410773.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2014/02/03/10496248.aspx (note the date on the latter one!)
if uMsg == _WM_NCCREATE {
// the lpParam to CreateWindowEx() is the first uintptr of the CREATESTRUCT
// so rather than create that whole structure, we'll just grab the uintptr at the address pointed to by lParam
cs := ( * uintptr ) ( unsafe . Pointer ( lParam ) )
saddr := * cs
setWindowLongPtr ( hwnd , negConst ( _GWLP_USERDATA ) , saddr )
// don't set s; we return here
}
// TODO is this correct for WM_NCCREATE? I think the above link does it but I'm not entirely sure...
return defWindowProc ( hwnd , uMsg , wParam , lParam )
}
func stdWndProc ( unused * sysData ) func ( hwnd _HWND , uMsg uint32 , wParam _WPARAM , lParam _LPARAM ) _LRESULT {
2014-02-12 19:51:07 -06:00
return func ( hwnd _HWND , uMsg uint32 , wParam _WPARAM , lParam _LPARAM ) _LRESULT {
2014-05-30 16:59:29 -05:00
s := getSysData ( hwnd )
if s == nil { // not yet saved
return storeSysData ( hwnd , uMsg , wParam , lParam )
}
2014-02-12 19:51:07 -06:00
switch uMsg {
case _WM_COMMAND :
id := _HMENU ( wParam . LOWORD ( ) )
s . childrenLock . Lock ( )
2014-02-12 20:33:24 -06:00
ss := s . children [ id ]
s . childrenLock . Unlock ( )
2014-02-12 19:51:07 -06:00
switch ss . ctype {
case c_button :
if wParam . HIWORD ( ) == _BN_CLICKED {
2014-02-18 08:57:19 -06:00
ss . signal ( )
2014-02-12 19:51:07 -06:00
}
2014-02-12 10:32:17 -06:00
}
2014-02-12 19:51:07 -06:00
return 0
case _WM_GETMINMAXINFO :
mm := lParam . MINMAXINFO ( )
// ... minimum size
_ = mm
return 0
case _WM_SIZE :
2014-02-13 04:28:26 -06:00
if s . resize != nil {
var r _RECT
r1 , _ , err := _getClientRect . Call (
uintptr ( hwnd ) ,
uintptr ( unsafe . Pointer ( & r ) ) )
if r1 == 0 {
panic ( "GetClientRect failed: " + err . Error ( ) )
}
2014-03-03 16:44:03 -06:00
// top-left corner is (0,0) so no need for winheight
2014-05-11 10:11:00 -05:00
s . doResize ( int ( r . left ) , int ( r . top ) , int ( r . right ) , int ( r . bottom ) , 0 )
2014-03-24 10:36:15 -05:00
// TODO use the Defer movement functions here?
2014-05-17 16:10:23 -05:00
// TODO redraw window and all children here?
2014-02-13 04:28:26 -06:00
}
2014-02-12 19:51:07 -06:00
return 0
case _WM_CLOSE :
2014-02-18 08:57:19 -06:00
s . signal ( )
2014-02-12 19:51:07 -06:00
return 0
default :
2014-05-30 13:13:47 -05:00
return defWindowProc ( hwnd , uMsg , wParam , lParam )
2014-02-12 10:32:17 -06:00
}
2014-02-12 19:51:07 -06:00
panic ( fmt . Sprintf ( "stdWndProc message %d did not return: internal bug in ui library" , uMsg ) )
2014-02-11 16:38:38 -06:00
}
2014-02-08 22:51:11 -06:00
}
2014-02-11 16:38:38 -06:00
type _WNDCLASS struct {
2014-02-08 22:51:11 -06:00
style uint32
2014-02-09 13:59:37 -06:00
lpfnWndProc uintptr
2014-02-21 01:27:59 -06:00
cbClsExtra int32 // originally int
cbWndExtra int32 // originally int
2014-02-11 16:38:38 -06:00
hInstance _HANDLE
hIcon _HANDLE
hCursor _HANDLE
hbrBackground _HBRUSH
2014-02-08 22:51:11 -06:00
lpszMenuName * uint16
2014-02-11 18:52:39 -06:00
lpszClassName uintptr
2014-02-08 22:51:11 -06:00
}
2014-02-12 19:51:07 -06:00
var (
icon , cursor _HANDLE
)
var (
_registerClass = user32 . NewProc ( "RegisterClassW" )
)
func registerStdWndClass ( s * sysData ) ( newClassName string , err error ) {
curWndClassNumLock . Lock ( )
newClassName = fmt . Sprintf ( stdWndClassFormat , curWndClassNum )
curWndClassNum ++
curWndClassNumLock . Unlock ( )
wc := & _WNDCLASS {
lpszClassName : uintptr ( unsafe . Pointer ( syscall . StringToUTF16Ptr ( newClassName ) ) ) ,
lpfnWndProc : syscall . NewCallback ( stdWndProc ( s ) ) ,
hInstance : hInstance ,
hIcon : icon ,
hCursor : cursor ,
hbrBackground : _HBRUSH ( _COLOR_BTNFACE + 1 ) ,
}
2014-02-12 20:11:21 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _registerClass ,
p : [ ] uintptr { uintptr ( unsafe . Pointer ( wc ) ) } ,
ret : ret ,
}
r := <- ret
if r . ret == 0 { // failure
return "" , r . err
2014-02-12 19:51:07 -06:00
}
return newClassName , nil
}
2014-05-25 14:09:01 -05:00
// no need to use/recreate MAKEINTRESOURCE() here as the Windows constant generator already took care of that because Microsoft's headers do already
2014-02-12 19:51:07 -06:00
func initWndClassInfo ( ) ( err error ) {
2014-02-11 18:18:03 -06:00
r1 , _ , err := user32 . NewProc ( "LoadIconW" ) . Call (
2014-02-11 16:38:38 -06:00
uintptr ( _NULL ) ,
2014-05-25 14:09:01 -05:00
uintptr ( _IDI_APPLICATION ) )
2014-02-11 18:27:58 -06:00
if r1 == 0 { // failure
2014-02-11 16:38:38 -06:00
return fmt . Errorf ( "error getting window icon: %v" , err )
2014-02-08 22:51:11 -06:00
}
2014-02-12 19:51:07 -06:00
icon = _HANDLE ( r1 )
2014-02-11 18:18:03 -06:00
r1 , _ , err = user32 . NewProc ( "LoadCursorW" ) . Call (
2014-02-11 16:38:38 -06:00
uintptr ( _NULL ) ,
2014-05-25 14:09:01 -05:00
uintptr ( _IDC_ARROW ) )
2014-02-11 18:27:58 -06:00
if r1 == 0 { // failure
2014-02-11 16:38:38 -06:00
return fmt . Errorf ( "error getting window cursor: %v" , err )
2014-02-08 22:51:11 -06:00
}
2014-02-12 19:51:07 -06:00
cursor = _HANDLE ( r1 )
2014-02-08 22:51:11 -06:00
2014-02-11 16:38:38 -06:00
return nil
2014-02-08 22:51:11 -06:00
}