Fixed the Windows stalling when scrolling an Area problem: messages were getting lost because (I assume) scrolling is done in a modal event loop. Alas, we have to change to using a dummy window to handle events. Also altered the respective TODO to document bugs now exposed by this fix.
This commit is contained in:
parent
2efda7bc39
commit
df087d0ea5
2
todo.md
2
todo.md
|
@ -86,7 +86,7 @@ super ultra important things:
|
|||
- the window background of a GTK+ window seems to be... off - I think it has to do with the GtkLayout
|
||||
- see update 18 March 2014 in README
|
||||
- resizing seems to be completely and totally broken in the Wayland backend
|
||||
- scrolling Areas in wine by clicking in the page scroll area often causes the main thread to lock up; uitask does not
|
||||
- redrawing Areas on Windows seems to be flaky
|
||||
- make sure the first and last rows and columns of an Area are being drawn on Windows
|
||||
- clicking on Areas in GTK+ don't bring keyboard focus to them?
|
||||
- make sure GTK+ keyboard events on numpad off don't switch between controls
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
"runtime"
|
||||
|
@ -10,8 +11,15 @@ import (
|
|||
|
||||
/*
|
||||
problem: messages have to be dispatched on the same thread as system calls, and we can't mux GetMessage() with select, and PeekMessage() every iteration is wasteful (and leads to lag for me (only) with the concurrent garbage collector sweep)
|
||||
solution: use PostThreadMessage() to send uimsgs out to the message loop, which runs on its own goroutine
|
||||
I had come up with this first but wanted to try other things before doing it (and wasn't really sure if user-defined messages were safe, not quite understanding the system); nsf came up with it independently and explained that this was really the only right way to do it, so thanks to him
|
||||
possible: solution: use PostThreadMessage() to send uimsgs out to the message loop, which runs on its own goroutine
|
||||
(I had come up with this first but wanted to try other things before doing it (and wasn't really sure if user-defined messages were safe, not quite understanding the system); nsf came up with it independently and explained that this was really the only right way to do it, so thanks to him)
|
||||
|
||||
problem: if the thread isn't in its main message pump, the thread message is simply lost
|
||||
this happened when scrolling Area
|
||||
|
||||
the only recourse, and the one both Microsoft (http://support.microsoft.com/kb/183116) and Raymond Chen (http://blogs.msdn.com/b/oldnewthing/archive/2008/12/23/9248851.aspx) suggest (and Treeki/Ninjifox confirmed), is to create an invisible window to dispatch messages instead.
|
||||
|
||||
yay.
|
||||
*/
|
||||
|
||||
var uitask chan *uimsg
|
||||
|
@ -35,8 +43,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
_getCurrentThreadID = kernel32.NewProc("GetCurrentThreadId")
|
||||
_postThreadMessage = user32.NewProc("PostThreadMessageW")
|
||||
_postMessage = user32.NewProc("PostMessageW")
|
||||
)
|
||||
|
||||
func ui(main func()) error {
|
||||
|
@ -45,15 +52,19 @@ func ui(main func()) error {
|
|||
uitask = make(chan *uimsg)
|
||||
err := doWindowsInit()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error doing general Windows initialization: %v", err)
|
||||
}
|
||||
|
||||
threadID, _, _ := _getCurrentThreadID.Call()
|
||||
hwnd, err := makeMessageHandler()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making invisible window for handling events: %v", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for m := range uitask {
|
||||
r1, _, err := _postThreadMessage.Call(
|
||||
threadID,
|
||||
// TODO use _sendMessage instead?
|
||||
r1, _, err := _postMessage.Call(
|
||||
uintptr(hwnd),
|
||||
msgRequested,
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(m)))
|
||||
|
@ -65,8 +76,8 @@ func ui(main func()) error {
|
|||
|
||||
go func() {
|
||||
main()
|
||||
r1, _, err := _postThreadMessage.Call(
|
||||
threadID,
|
||||
r1, _, err := _postMessage.Call(
|
||||
uintptr(hwnd),
|
||||
msgQuit,
|
||||
uintptr(0),
|
||||
uintptr(0))
|
||||
|
@ -113,21 +124,71 @@ func msgloop() {
|
|||
if r1 == 0 { // WM_QUIT message
|
||||
return
|
||||
}
|
||||
if msg.Message == msgRequested {
|
||||
m := (*uimsg)(unsafe.Pointer(msg.LParam))
|
||||
r1, _, err := m.call.Call(m.p...)
|
||||
m.ret <- uiret{
|
||||
ret: r1,
|
||||
err: err,
|
||||
}
|
||||
continue
|
||||
}
|
||||
if msg.Message == msgQuit {
|
||||
// does not return a value according to MSDN
|
||||
_postQuitMessage.Call(0)
|
||||
continue
|
||||
}
|
||||
_translateMessage.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
_dispatchMessage.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move to init?
|
||||
|
||||
const (
|
||||
msghandlerclass = "gomsghandler"
|
||||
)
|
||||
|
||||
func makeMessageHandler() (hwnd _HWND, err error) {
|
||||
wc := &_WNDCLASS{
|
||||
lpszClassName: uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(msghandlerclass))),
|
||||
lpfnWndProc: syscall.NewCallback(messageHandlerWndProc),
|
||||
hInstance: hInstance,
|
||||
hIcon: icon,
|
||||
hCursor: cursor,
|
||||
hbrBackground: _HBRUSH(_COLOR_BTNFACE + 1),
|
||||
}
|
||||
|
||||
r1, _, err := _registerClass.Call(uintptr(unsafe.Pointer(wc)))
|
||||
if r1 == 0 { // failure
|
||||
return _HWND(_NULL), fmt.Errorf("error registering the class of the invisible window for handling events: %v", err)
|
||||
}
|
||||
|
||||
r1, _, err = _createWindowEx.Call(
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(msghandlerclass))),
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(""))), // TODO can this be NULL?
|
||||
uintptr(0),
|
||||
uintptr(_CW_USEDEFAULT),
|
||||
uintptr(_CW_USEDEFAULT),
|
||||
uintptr(_CW_USEDEFAULT),
|
||||
uintptr(_CW_USEDEFAULT),
|
||||
uintptr(_NULL),
|
||||
uintptr(_NULL),
|
||||
uintptr(hInstance),
|
||||
uintptr(_NULL))
|
||||
if r1 == 0 { // failure
|
||||
return _HWND(_NULL), fmt.Errorf("error actually creating invisible window for handling events: %v", err)
|
||||
}
|
||||
|
||||
return _HWND(r1), nil
|
||||
}
|
||||
|
||||
func messageHandlerWndProc(hwnd _HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) _LRESULT {
|
||||
switch uMsg {
|
||||
case msgRequested:
|
||||
m := (*uimsg)(unsafe.Pointer(lParam))
|
||||
r1, _, err := m.call.Call(m.p...)
|
||||
m.ret <- uiret{
|
||||
ret: r1,
|
||||
err: err,
|
||||
}
|
||||
return 0
|
||||
case msgQuit:
|
||||
// does not return a value according to MSDN
|
||||
_postQuitMessage.Call(0)
|
||||
return 0
|
||||
}
|
||||
r1, _, _ := defWindowProc.Call(
|
||||
uintptr(hwnd),
|
||||
uintptr(uMsg),
|
||||
uintptr(wParam),
|
||||
uintptr(lParam))
|
||||
return _LRESULT(r1)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue