mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
[v3, windows] Add MainThread dispatching and fixes the blocking window
This commit is contained in:
parent
829a829cb4
commit
4a60dfc373
5 changed files with 179 additions and 22 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
_ "embed"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
|
@ -38,6 +39,21 @@ func main() {
|
|||
println("clicked")
|
||||
})
|
||||
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
|
||||
Title: "Plain Bundle new Window from GoRoutine",
|
||||
Width: 500,
|
||||
Height: 500,
|
||||
Mac: application.MacWindow{
|
||||
Backdrop: application.MacBackdropTranslucent,
|
||||
TitleBar: application.MacTitleBarHiddenInsetUnified,
|
||||
InvisibleTitleBarHeight: 50,
|
||||
},
|
||||
})
|
||||
}()
|
||||
|
||||
err := app.Run()
|
||||
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ var (
|
|||
destroyCursor = user32.NewProc("DestroyCursor")
|
||||
getDlgCtrlID = user32.NewProc("GetDlgCtrlID")
|
||||
systemParametersInfo = user32.NewProc("SystemParametersInfoW")
|
||||
registerWindowMessage = user32.NewProc("RegisterWindowMessageW")
|
||||
|
||||
regCreateKeyEx = advapi32.NewProc("RegCreateKeyExW")
|
||||
regOpenKeyEx = advapi32.NewProc("RegOpenKeyExW")
|
||||
|
|
@ -364,6 +365,7 @@ var (
|
|||
mulDiv = kernel32.NewProc("MulDiv")
|
||||
getConsoleWindow = kernel32.NewProc("GetConsoleWindow")
|
||||
getCurrentThread = kernel32.NewProc("GetCurrentThread")
|
||||
getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
||||
getLogicalDrives = kernel32.NewProc("GetLogicalDrives")
|
||||
getDriveType = kernel32.NewProc("GetDriveTypeW")
|
||||
getUserDefaultLCID = kernel32.NewProc("GetUserDefaultLCID")
|
||||
|
|
@ -482,6 +484,14 @@ var (
|
|||
setProcessDpiAwareness = shcore.NewProc("SetProcessDpiAwareness")
|
||||
)
|
||||
|
||||
func RegisterWindowMessage(name string) uint32 {
|
||||
ret, _, _ := registerWindowMessage.Call(
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))),
|
||||
)
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// RegisterClassEx sets the Size of the WNDCLASSEX automatically.
|
||||
func RegisterClassEx(wndClassEx *WNDCLASSEX) ATOM {
|
||||
if wndClassEx != nil {
|
||||
|
|
@ -3674,6 +3684,11 @@ func GetCurrentThread() HANDLE {
|
|||
return HANDLE(ret)
|
||||
}
|
||||
|
||||
func GetCurrentThreadId() HANDLE {
|
||||
ret, _, _ := getCurrentThreadId.Call()
|
||||
return HANDLE(ret)
|
||||
}
|
||||
|
||||
func GetLogicalDrives() uint32 {
|
||||
ret, _, _ := getLogicalDrives.Call()
|
||||
return uint32(ret)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v3/internal/w32"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v3/internal/w32"
|
||||
)
|
||||
|
||||
var windowClassName = lo.Must(syscall.UTF16PtrFromString("WailsWebviewWindow"))
|
||||
|
|
@ -15,11 +16,9 @@ type windowsApp struct {
|
|||
parent *App
|
||||
|
||||
instance w32.HINSTANCE
|
||||
}
|
||||
|
||||
func (m *windowsApp) dispatchOnMainThread(id uint) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
mainThreadID w32.HANDLE
|
||||
mainThreadWindowHWND w32.HWND
|
||||
}
|
||||
|
||||
func (m *windowsApp) getPrimaryScreen() (*Screen, error) {
|
||||
|
|
@ -116,27 +115,20 @@ func (m *windowsApp) init() {
|
|||
|
||||
func (m *windowsApp) wndProc(hwnd w32.HWND, msg uint32, wParam, lParam uintptr) uintptr {
|
||||
switch msg {
|
||||
case w32.WM_SIZE, w32.WM_PAINT:
|
||||
case w32.WM_SIZE:
|
||||
return 0
|
||||
case w32.WM_CLOSE:
|
||||
w32.PostQuitMessage(0)
|
||||
return 0
|
||||
case wmInvokeCallback:
|
||||
if hwnd == m.mainThreadWindowHWND {
|
||||
m.invokeCallback(wParam, lParam)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return w32.DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
}
|
||||
|
||||
func (m *windowsApp) runMainLoop() int {
|
||||
msg := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{})))))
|
||||
defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(m)))
|
||||
|
||||
for w32.GetMessage(msg, 0, 0, 0) != 0 {
|
||||
w32.TranslateMessage(msg)
|
||||
w32.DispatchMessage(msg)
|
||||
}
|
||||
|
||||
return int(msg.WParam)
|
||||
}
|
||||
|
||||
func newPlatformApp(app *App) *windowsApp {
|
||||
result := &windowsApp{
|
||||
parent: app,
|
||||
|
|
@ -144,6 +136,7 @@ func newPlatformApp(app *App) *windowsApp {
|
|||
}
|
||||
|
||||
result.init()
|
||||
result.initMainLoop()
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
129
v3/pkg/application/mainthread_windows.go
Normal file
129
v3/pkg/application/mainthread_windows.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
//go:build windows
|
||||
|
||||
package application
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sort"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v3/internal/w32"
|
||||
)
|
||||
|
||||
var (
|
||||
wmInvokeCallback uint32
|
||||
)
|
||||
|
||||
func init() {
|
||||
wmInvokeCallback = w32.RegisterWindowMessage("WailsV0.InvokeCallback")
|
||||
}
|
||||
|
||||
// initMainLoop must be called with the same OSThread that is used to call runMainLoop() later.
|
||||
func (m *windowsApp) initMainLoop() {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if m.mainThreadWindowHWND != 0 {
|
||||
panic("initMainLoop was already called")
|
||||
}
|
||||
|
||||
// We need a hidden window so we can PostMessage to it, if we don't use PostMessage for dispatching to a HWND
|
||||
// messages might get lost if a modal inner loop is being run.
|
||||
// We had this once in V2: https://github.com/wailsapp/wails/issues/969
|
||||
// See: https://devblogs.microsoft.com/oldnewthing/20050426-18/?p=35783
|
||||
// See also: https://learn.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues#creating-a-message-loop
|
||||
// > Because the system directs messages to individual windows in an application, a thread must create at least one window before starting its message loop.
|
||||
m.mainThreadWindowHWND = w32.CreateWindowEx(
|
||||
0,
|
||||
windowClassName,
|
||||
lo.Must(syscall.UTF16PtrFromString("__wails_hidden_mainthread")),
|
||||
w32.WS_DISABLED,
|
||||
w32.CW_USEDEFAULT,
|
||||
w32.CW_USEDEFAULT,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
w32.GetModuleHandle(""),
|
||||
nil)
|
||||
|
||||
m.mainThreadID, _ = w32.GetWindowThreadProcessId(m.mainThreadWindowHWND)
|
||||
}
|
||||
|
||||
func (m *windowsApp) runMainLoop() int {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if m.invokeRequired() {
|
||||
panic("invokeRequired for runMainLoop, the mainloop must be running on the same OSThread as the mainThreadWindow has been created on")
|
||||
}
|
||||
|
||||
msg := (*w32.MSG)(unsafe.Pointer(w32.GlobalAlloc(0, uint32(unsafe.Sizeof(w32.MSG{})))))
|
||||
defer w32.GlobalFree(w32.HGLOBAL(unsafe.Pointer(m)))
|
||||
|
||||
for w32.GetMessage(msg, 0, 0, 0) != 0 {
|
||||
w32.TranslateMessage(msg)
|
||||
w32.DispatchMessage(msg)
|
||||
}
|
||||
|
||||
return int(msg.WParam)
|
||||
}
|
||||
|
||||
func (m *windowsApp) dispatchOnMainThread(id uint) {
|
||||
mainThreadHWND := m.mainThreadWindowHWND
|
||||
if mainThreadHWND == 0 {
|
||||
panic("initMainLoop was not called")
|
||||
}
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if m.invokeRequired() {
|
||||
w32.PostMessage(mainThreadHWND, wmInvokeCallback, uintptr(id), 0)
|
||||
} else {
|
||||
mainThreadFunctionStoreLock.Lock()
|
||||
fn := mainThreadFunctionStore[id]
|
||||
delete(mainThreadFunctionStore, id)
|
||||
mainThreadFunctionStoreLock.Unlock()
|
||||
|
||||
if fn == nil {
|
||||
Fatal("dispatchOnMainThread called with invalid id: %v", id)
|
||||
}
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *windowsApp) invokeRequired() bool {
|
||||
mainThreadID := m.mainThreadID
|
||||
if mainThreadID == 0 {
|
||||
panic("initMainLoop was not called")
|
||||
}
|
||||
|
||||
return mainThreadID != w32.GetCurrentThreadId()
|
||||
}
|
||||
|
||||
func (m *windowsApp) invokeCallback(wParam, lParam uintptr) {
|
||||
// TODO: Should we invoke just one or all queued? In v2 we always invoked all pendings...
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if m.invokeRequired() {
|
||||
panic("invokeCallback must always be called on the MainOSThread")
|
||||
}
|
||||
|
||||
mainThreadFunctionStoreLock.Lock()
|
||||
fnIDs := make([]uint, 0, len(mainThreadFunctionStore))
|
||||
for id := range mainThreadFunctionStore {
|
||||
fnIDs = append(fnIDs, id)
|
||||
}
|
||||
sort.Slice(fnIDs, func(i, j int) bool { return fnIDs[i] < fnIDs[j] })
|
||||
|
||||
fns := make([]func(), len(fnIDs))
|
||||
for i, id := range fnIDs {
|
||||
fns[i] = mainThreadFunctionStore[id]
|
||||
delete(mainThreadFunctionStore, id)
|
||||
}
|
||||
mainThreadFunctionStoreLock.Unlock()
|
||||
|
||||
for _, fn := range fns {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,11 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v3/internal/w32"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v3/internal/w32"
|
||||
)
|
||||
|
||||
var showDevTools = func(window unsafe.Pointer) {}
|
||||
|
|
@ -67,7 +68,10 @@ func (w *windowsWebviewWindow) setBackgroundColour(color *RGBA) {
|
|||
}
|
||||
|
||||
func (w *windowsWebviewWindow) run() {
|
||||
globalApplication.dispatchOnMainThread(w._run)
|
||||
}
|
||||
|
||||
func (w *windowsWebviewWindow) _run() {
|
||||
var exStyle uint
|
||||
options := w.parent.options
|
||||
windowsOptions := options.Windows
|
||||
|
|
@ -84,7 +88,7 @@ func (w *windowsWebviewWindow) run() {
|
|||
hwnd = w32.CreateWindowEx(
|
||||
exStyle,
|
||||
windowClassName,
|
||||
lo.Must(syscall.UTF16PtrFromString("My Window Title")),
|
||||
lo.Must(syscall.UTF16PtrFromString(options.Title)),
|
||||
w32.WS_OVERLAPPEDWINDOW|w32.WS_VISIBLE,
|
||||
w32.CW_USEDEFAULT,
|
||||
w32.CW_USEDEFAULT,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue