app/internal/window: [Windows] support multiple windows

Updates gio#19

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-06-07 15:05:08 +02:00
parent 65078cdece
commit c2c31a4d00
+61 -22
View File
@@ -26,8 +26,6 @@ import (
"gioui.org/io/system" "gioui.org/io/system"
) )
var winMap = make(map[syscall.Handle]*window)
type window struct { type window struct {
hwnd syscall.Handle hwnd syscall.Handle
hdc syscall.Handle hdc syscall.Handle
@@ -44,16 +42,34 @@ type window struct {
const _WM_REDRAW = windows.WM_USER + 0 const _WM_REDRAW = windows.WM_USER + 0
var onceMu sync.Mutex
var mainDone = make(chan struct{}) var mainDone = make(chan struct{})
type gpuAPI struct {
priority int
initializer func(w *window) (Context, error)
}
// backends is the list of potential Context // backends is the list of potential Context
// implementations. // implementations.
var backends []gpuAPI var backends []gpuAPI
type gpuAPI struct { var winMap struct {
priority int mu sync.Mutex
initializer func(w *window) (Context, error) windows map[syscall.Handle]*window
}
var resources struct {
once sync.Once
// handle is the module handle from GetModuleHandle.
handle syscall.Handle
// class is the Gio window class from RegisterClassEx.
class uint16
// cursor is the arrow cursor resource
cursor syscall.Handle
}
func init() {
winMap.windows = make(map[syscall.Handle]*window)
} }
func Main() { func Main() {
@@ -61,11 +77,6 @@ func Main() {
} }
func NewWindow(window Callbacks, opts *Options) error { func NewWindow(window Callbacks, opts *Options) error {
onceMu.Lock()
defer onceMu.Unlock()
if len(winMap) > 0 {
return errors.New("multiple windows are not supported")
}
cerr := make(chan error) cerr := make(chan error)
go func() { go func() {
// Call win32 API from a single OS thread. // Call win32 API from a single OS thread.
@@ -77,8 +88,20 @@ func NewWindow(window Callbacks, opts *Options) error {
} }
defer w.destroy() defer w.destroy()
cerr <- nil cerr <- nil
winMap[w.hwnd] = w winMap.mu.Lock()
defer delete(winMap, w.hwnd) winMap.windows[w.hwnd] = w
winMap.mu.Unlock()
defer func() {
winMap.mu.Lock()
defer winMap.mu.Unlock()
delete(winMap.windows, w.hwnd)
if len(winMap.windows) == 0 {
select {
case mainDone <- struct{}{}:
default:
}
}
}()
w.w = window w.w = window
w.w.SetDriver(w) w.w.SetDriver(w)
defer w.w.Event(system.DestroyEvent{}) defer w.w.Event(system.DestroyEvent{})
@@ -88,22 +111,23 @@ func NewWindow(window Callbacks, opts *Options) error {
if err := w.loop(); err != nil { if err := w.loop(); err != nil {
panic(err) panic(err)
} }
close(mainDone)
}() }()
return <-cerr return <-cerr
} }
func createNativeWindow(opts *Options) (*window, error) { // initResources initializes the resources global.
func initResources() error {
windows.SetProcessDPIAware() windows.SetProcessDPIAware()
cfg := configForDC()
hInst, err := windows.GetModuleHandle() hInst, err := windows.GetModuleHandle()
if err != nil { if err != nil {
return nil, err return err
} }
resources.handle = hInst
curs, err := windows.LoadCursor(windows.IDC_ARROW) curs, err := windows.LoadCursor(windows.IDC_ARROW)
if err != nil { if err != nil {
return nil, err return err
} }
resources.cursor = curs
wcls := windows.WndClassEx{ wcls := windows.WndClassEx{
CbSize: uint32(unsafe.Sizeof(windows.WndClassEx{})), CbSize: uint32(unsafe.Sizeof(windows.WndClassEx{})),
Style: windows.CS_HREDRAW | windows.CS_VREDRAW | windows.CS_OWNDC, Style: windows.CS_HREDRAW | windows.CS_VREDRAW | windows.CS_OWNDC,
@@ -114,8 +138,21 @@ func createNativeWindow(opts *Options) (*window, error) {
} }
cls, err := windows.RegisterClassEx(&wcls) cls, err := windows.RegisterClassEx(&wcls)
if err != nil { if err != nil {
return nil, err return err
} }
resources.class = cls
return nil
}
func createNativeWindow(opts *Options) (*window, error) {
var resErr error
resources.once.Do(func() {
resErr = initResources()
})
if resErr != nil {
return nil, resErr
}
cfg := configForDC()
wr := windows.Rect{ wr := windows.Rect{
Right: int32(cfg.Px(opts.Width)), Right: int32(cfg.Px(opts.Width)),
Bottom: int32(cfg.Px(opts.Height)), Bottom: int32(cfg.Px(opts.Height)),
@@ -124,7 +161,7 @@ func createNativeWindow(opts *Options) (*window, error) {
dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE)
windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle) windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle)
hwnd, err := windows.CreateWindowEx(dwExStyle, hwnd, err := windows.CreateWindowEx(dwExStyle,
cls, resources.class,
opts.Title, opts.Title,
dwStyle|windows.WS_CLIPSIBLINGS|windows.WS_CLIPCHILDREN, dwStyle|windows.WS_CLIPSIBLINGS|windows.WS_CLIPCHILDREN,
windows.CW_USEDEFAULT, windows.CW_USEDEFAULT, windows.CW_USEDEFAULT, windows.CW_USEDEFAULT,
@@ -132,7 +169,7 @@ func createNativeWindow(opts *Options) (*window, error) {
wr.Bottom-wr.Top, wr.Bottom-wr.Top,
0, 0,
0, 0,
hInst, resources.handle,
0) 0)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -148,7 +185,9 @@ func createNativeWindow(opts *Options) (*window, error) {
} }
func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr { func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr {
w := winMap[hwnd] winMap.mu.Lock()
w := winMap.windows[hwnd]
winMap.mu.Unlock()
switch msg { switch msg {
case windows.WM_UNICHAR: case windows.WM_UNICHAR:
if wParam == windows.UNICODE_NOCHAR { if wParam == windows.UNICODE_NOCHAR {