app/internal/window: [Windows] properly handle WM_QUIT

First, don't filter on HWND in GetMessage and PeekMessage, lest
thread-specific messages may get lost. See

https://devblogs.microsoft.com/oldnewthing/20050209-00/?p=36493

Second, replace the dead status with the detection of WM_QUIT; it's
what it's there for.

May update gio#168

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-10-27 17:57:06 +00:00
parent f9642447ec
commit 3740f89171
+14 -9
View File
@@ -44,7 +44,6 @@ type window struct {
width int width int
height int height int
stage system.Stage stage system.Stage
dead bool
pointerBtns pointer.Buttons pointerBtns pointer.Buttons
mu sync.Mutex mu sync.Mutex
@@ -86,7 +85,10 @@ func Main() {
func NewWindow(window Callbacks, opts *Options) error { func NewWindow(window Callbacks, opts *Options) error {
cerr := make(chan error) cerr := make(chan error)
go func() { go func() {
// Call win32 API from a single OS thread. // GetMessage and PeekMessage can filter on a window HWND, but
// then thread-specific messages such as WM_QUIT are ignored.
// Instead lock the thread so window messages arrive through
// unfiltered GetMessage calls.
runtime.LockOSThread() runtime.LockOSThread()
w, err := createNativeWindow(opts) w, err := createNativeWindow(opts)
if err != nil { if err != nil {
@@ -263,7 +265,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
case windows.WM_MOUSEWHEEL: case windows.WM_MOUSEWHEEL:
w.scrollEvent(wParam, lParam) w.scrollEvent(wParam, lParam)
case windows.WM_DESTROY: case windows.WM_DESTROY:
w.dead = true windows.PostQuitMessage(0)
case windows.WM_PAINT: case windows.WM_PAINT:
w.draw(true) w.draw(true)
case windows.WM_SIZE: case windows.WM_SIZE:
@@ -363,18 +365,21 @@ func (w *window) scrollEvent(wParam, lParam uintptr) {
// Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/ // Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/
func (w *window) loop() error { func (w *window) loop() error {
msg := new(windows.Msg) msg := new(windows.Msg)
for !w.dead { loop:
for {
w.mu.Lock() w.mu.Lock()
anim := w.animating anim := w.animating
w.mu.Unlock() w.mu.Unlock()
if anim && !windows.PeekMessage(msg, w.hwnd, 0, 0, windows.PM_NOREMOVE) { if anim && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) {
w.draw(false) w.draw(false)
continue continue
} }
windows.GetMessage(msg, w.hwnd, 0, 0) switch ret := windows.GetMessage(msg, 0, 0, 0); ret {
if msg.Message == windows.WM_QUIT { case -1:
windows.PostQuitMessage(msg.WParam) return errors.New("GetMessage failed")
break case 0:
// WM_QUIT received.
break loop
} }
windows.TranslateMessage(msg) windows.TranslateMessage(msg)
windows.DispatchMessage(msg) windows.DispatchMessage(msg)