From bf3ef18db9afddc36857490a03b3a512ab756c86 Mon Sep 17 00:00:00 2001 From: Iain Collins Date: Tue, 16 Nov 2021 13:22:25 +0000 Subject: [PATCH] Refactor app code into files --- src/app/main.go | 184 +++++++++------------------------------ src/app/process-group.go | 41 +++++++++ src/app/win32-windows.go | 77 ++++++++++++++++ 3 files changed, 159 insertions(+), 143 deletions(-) create mode 100644 src/app/process-group.go create mode 100644 src/app/win32-windows.go diff --git a/src/app/main.go b/src/app/main.go index 3bb9189b..07664f6c 100644 --- a/src/app/main.go +++ b/src/app/main.go @@ -8,7 +8,6 @@ import ( "github.com/phayes/freeport" "github.com/sqweek/dialog" "github.com/webview/webview" - "golang.org/x/sys/windows" "os" "os/exec" "syscall" @@ -44,14 +43,12 @@ type process struct { Handle uintptr } -type ProcessExitGroup windows.Handle - -var processGroup ProcessExitGroup +var processGroup ProcessGroup func main() { startTime := time.Now() - _processGroup, err := NewProcessExitGroup() + _processGroup, err := NewProcessGroup() if err != nil { panic(err) } @@ -134,6 +131,7 @@ func main() { exitApplication(0) } +// createWindow() lets the webview library create a managed window for us func createWindow(LAUNCHER_WINDOW_TITLE string, url string, width int32, height int32, hint webview.Hint) { // Passes the pointer to the window as an unsafe reference w := webview.New(DEBUGGER) @@ -163,6 +161,44 @@ func createWindow(LAUNCHER_WINDOW_TITLE string, url string, width int32, height w.Run() } +// createNativeWindow() explicitly creates a native window and passes the handle +// for it to the webview, this allows for greater customisation +func createNativeWindow(LAUNCHER_WINDOW_TITLE string, url string, width int32, height int32) { + // Instance of this executable + hInstance := win.GetModuleHandle(nil) + if hInstance == 0 { + fmt.Println("GetModuleHandle failed:", win.GetLastError()) + } + + // Register window class + atom := RegisterClass(hInstance) + if atom == 0 { + fmt.Println("RegisterClass failed:", win.GetLastError()) + } + + // Create our own window + // We do this manually and pass it to webview so that we can set the window + // location (i.e. centered), style, etc before it is displayed. + hwndPtr := CreateWin32Window(hInstance, LAUNCHER_WINDOW_TITLE, width, height) + if hwndPtr == 0 { + fmt.Println("CreateWin32Window failed:", win.GetLastError()) + } + + // Center window + screenWidth := int32(win.GetSystemMetrics(win.SM_CXSCREEN)) + screenHeight := int32(win.GetSystemMetrics(win.SM_CYSCREEN)) + windowX := int32((screenWidth / 2) - (width / 2)) + windowY := int32((screenHeight / 2) - (height / 2)) + win.MoveWindow(win.HWND(hwndPtr), windowX, windowY, width, height, false) + + // Pass the pointer to the window as an unsafe reference + webViewInstance = webview.NewWindow(DEBUGGER, unsafe.Pointer(&hwndPtr)) + defer webViewInstance.Destroy() + bindFunctionsToWebView(webViewInstance) + webViewInstance.Navigate(url) + webViewInstance.Run() +} + func bindFunctionsToWebView(w webview.WebView) { hwndPtr := w.Window() hwnd := win.HWND(hwndPtr) @@ -246,141 +282,3 @@ func checkProcessAlreadyExists(windowTitle string) bool { } return !bytes.Contains(result, []byte("No tasks are running")) } - -func createNativeWindow(LAUNCHER_WINDOW_TITLE string, url string, width int32, height int32) { - // Instance of this executable - hInstance := win.GetModuleHandle(nil) - if hInstance == 0 { - fmt.Println("GetModuleHandle failed:", win.GetLastError()) - } - - // Register window class - atom := registerWindowClass(hInstance) - if atom == 0 { - fmt.Println("registerWindowClass failed:", win.GetLastError()) - } - - // Create our own window - // We do this manually and pass it to webview so that we can set the window - // location (i.e. centered), style, etc before it is displayed. - hwndPtr := createWin32Window(hInstance, LAUNCHER_WINDOW_TITLE, width, height) - if hwndPtr == 0 { - fmt.Println("createWin32Window failed:", win.GetLastError()) - } - - // Center window - screenWidth := int32(win.GetSystemMetrics(win.SM_CXSCREEN)) - screenHeight := int32(win.GetSystemMetrics(win.SM_CYSCREEN)) - windowX := int32((screenWidth / 2) - (width / 2)) - windowY := int32((screenHeight / 2) - (height / 2)) - win.MoveWindow(win.HWND(hwndPtr), windowX, windowY, width, height, false) - - // Pass the pointer to the window as an unsafe reference - webViewInstance = webview.NewWindow(DEBUGGER, unsafe.Pointer(&hwndPtr)) - defer webViewInstance.Destroy() - bindFunctionsToWebView(webViewInstance) - webViewInstance.Navigate(url) - webViewInstance.Run() -} - -func wndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr { - // windowPtr := unsafe.Pointer(win.GetWindowLongPtr(hwnd, win.GWLP_USERDATA)) - // w, _ := GetWindowContext(hwnd).(webViewInstance); - switch msg { - case win.WM_SIZE: - // TODO Handle weview resizing on custom windows - // Would be great if could access w.m_browser.resize(hwnd) here - // w.m_browser.resize(hwnd); - break - case win.WM_DESTROY: - win.PostQuitMessage(0) - exitApplication(0) - default: - return win.DefWindowProc(hwnd, msg, wParam, lParam) - } - return 0 -} - -// func GetWindowContext(wnd win.HWND) interface{} { -// windowContextSync.RLock() -// defer windowContextSync.RUnlock() -// return windowContext[wnd] -// } - -// var ( -// windowContext = map[uintptr]interface{}{} -// windowContextSync sync.RWMutex -// ) - -func registerWindowClass(hInstance win.HINSTANCE) (atom win.ATOM) { - var wc win.WNDCLASSEX - wc.CbSize = uint32(unsafe.Sizeof(wc)) - wc.Style = win.CS_HREDRAW | win.CS_VREDRAW | win.CS_OWNDC - wc.LpfnWndProc = syscall.NewCallback(wndProc) - wc.CbClsExtra = 0 - wc.CbWndExtra = 0 - wc.HInstance = hInstance - wc.HbrBackground = win.GetSysColorBrush(win.COLOR_WINDOWFRAME) - wc.LpszMenuName = syscall.StringToUTF16Ptr("") - wc.LpszClassName = syscall.StringToUTF16Ptr(LPSZ_CLASS_NAME) - wc.HIconSm = win.HICON(win.LoadImage(hInstance, syscall.StringToUTF16Ptr(ICON), win.IMAGE_ICON, 32, 32, win.LR_LOADFROMFILE|win.LR_SHARED|win.LR_LOADTRANSPARENT)) - wc.HIcon = win.HICON(win.LoadImage(hInstance, syscall.StringToUTF16Ptr(ICON), win.IMAGE_ICON, 64, 64, win.LR_LOADFROMFILE|win.LR_SHARED|win.LR_LOADTRANSPARENT)) - wc.HCursor = win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_ARROW)) - return win.RegisterClassEx(&wc) -} - -func createWin32Window(hInstance win.HINSTANCE, LAUNCHER_WINDOW_TITLE string, width int32, height int32) (hwnd win.HWND) { - // Center window - // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ - screenWidth := int32(win.GetSystemMetrics(win.SM_CXSCREEN)) - screenHeight := int32(win.GetSystemMetrics(win.SM_CYSCREEN)) - windowX := int32((screenWidth / 2) - (width / 2)) - windowY := int32((screenHeight / 2) - (height / 2)) - - return win.CreateWindowEx( - win.WS_EX_APPWINDOW, - syscall.StringToUTF16Ptr(LPSZ_CLASS_NAME), - syscall.StringToUTF16Ptr(LAUNCHER_WINDOW_TITLE), - win.WS_OVERLAPPED|win.WS_SYSMENU|win.WS_MINIMIZEBOX, - //win.WS_OVERLAPPEDWINDOW, // A normal window - windowX, - windowY, - width, - height, - 0, - 0, - hInstance, - nil) -} - -func NewProcessExitGroup() (ProcessExitGroup, error) { - handle, err := windows.CreateJobObject(nil, nil) - if err != nil { - return 0, err - } - - info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{ - BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{ - LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, - }, - } - if _, err := windows.SetInformationJobObject( - handle, - windows.JobObjectExtendedLimitInformation, - uintptr(unsafe.Pointer(&info)), - uint32(unsafe.Sizeof(info))); err != nil { - return 0, err - } - - return ProcessExitGroup(handle), nil -} - -func (g ProcessExitGroup) Dispose() error { - return windows.CloseHandle(windows.Handle(g)) -} - -func (g ProcessExitGroup) AddProcess(p *os.Process) error { - return windows.AssignProcessToJobObject( - windows.Handle(g), - windows.Handle((*process)(unsafe.Pointer(p)).Handle)) -} diff --git a/src/app/process-group.go b/src/app/process-group.go new file mode 100644 index 00000000..9d0fd857 --- /dev/null +++ b/src/app/process-group.go @@ -0,0 +1,41 @@ +package main + +import ( + "os" + "golang.org/x/sys/windows" + "unsafe" +) + +type ProcessGroup windows.Handle + +func NewProcessGroup() (ProcessGroup, error) { + handle, err := windows.CreateJobObject(nil, nil) + if err != nil { + return 0, err + } + + info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{ + BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{ + LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, + }, + } + if _, err := windows.SetInformationJobObject( + handle, + windows.JobObjectExtendedLimitInformation, + uintptr(unsafe.Pointer(&info)), + uint32(unsafe.Sizeof(info))); err != nil { + return 0, err + } + + return ProcessGroup(handle), nil +} + +func (g ProcessGroup) Dispose() error { + return windows.CloseHandle(windows.Handle(g)) +} + +func (g ProcessGroup) AddProcess(p *os.Process) error { + return windows.AssignProcessToJobObject( + windows.Handle(g), + windows.Handle((*process)(unsafe.Pointer(p)).Handle)) +} diff --git a/src/app/win32-windows.go b/src/app/win32-windows.go new file mode 100644 index 00000000..09184df0 --- /dev/null +++ b/src/app/win32-windows.go @@ -0,0 +1,77 @@ +package main + +import ( + "github.com/nvsoft/win" + "syscall" + "unsafe" +) + +func WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr { + // windowPtr := unsafe.Pointer(win.GetWindowLongPtr(hwnd, win.GWLP_USERDATA)) + // w, _ := GetWindowContext(hwnd).(webViewInstance); + switch msg { + case win.WM_SIZE: + // TODO Handle weview resizing on custom windows + // Would be great if could access w.m_browser.resize(hwnd) here + // w.m_browser.resize(hwnd); + break + case win.WM_DESTROY: + win.PostQuitMessage(0) + exitApplication(0) + default: + return win.DefWindowProc(hwnd, msg, wParam, lParam) + } + return 0 +} + +// func GetWindowContext(wnd win.HWND) interface{} { +// windowContextSync.RLock() +// defer windowContextSync.RUnlock() +// return windowContext[wnd] +// } + +// var ( +// windowContext = map[uintptr]interface{}{} +// windowContextSync sync.RWMutex +// ) + +func RegisterClass(hInstance win.HINSTANCE) (atom win.ATOM) { + var wc win.WNDCLASSEX + wc.CbSize = uint32(unsafe.Sizeof(wc)) + wc.Style = win.CS_HREDRAW | win.CS_VREDRAW | win.CS_OWNDC + wc.LpfnWndProc = syscall.NewCallback(WndProc) + wc.CbClsExtra = 0 + wc.CbWndExtra = 0 + wc.HInstance = hInstance + wc.HbrBackground = win.GetSysColorBrush(win.COLOR_WINDOWFRAME) + wc.LpszMenuName = syscall.StringToUTF16Ptr("") + wc.LpszClassName = syscall.StringToUTF16Ptr(LPSZ_CLASS_NAME) + wc.HIconSm = win.HICON(win.LoadImage(hInstance, syscall.StringToUTF16Ptr(ICON), win.IMAGE_ICON, 32, 32, win.LR_LOADFROMFILE|win.LR_SHARED|win.LR_LOADTRANSPARENT)) + wc.HIcon = win.HICON(win.LoadImage(hInstance, syscall.StringToUTF16Ptr(ICON), win.IMAGE_ICON, 64, 64, win.LR_LOADFROMFILE|win.LR_SHARED|win.LR_LOADTRANSPARENT)) + wc.HCursor = win.LoadCursor(0, win.MAKEINTRESOURCE(win.IDC_ARROW)) + return win.RegisterClassEx(&wc) +} + +func CreateWin32Window(hInstance win.HINSTANCE, LAUNCHER_WINDOW_TITLE string, width int32, height int32) (hwnd win.HWND) { + // Center window + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ + screenWidth := int32(win.GetSystemMetrics(win.SM_CXSCREEN)) + screenHeight := int32(win.GetSystemMetrics(win.SM_CYSCREEN)) + windowX := int32((screenWidth / 2) - (width / 2)) + windowY := int32((screenHeight / 2) - (height / 2)) + + return win.CreateWindowEx( + win.WS_EX_APPWINDOW, + syscall.StringToUTF16Ptr(LPSZ_CLASS_NAME), + syscall.StringToUTF16Ptr(LAUNCHER_WINDOW_TITLE), + win.WS_OVERLAPPED|win.WS_SYSMENU|win.WS_MINIMIZEBOX, + //win.WS_OVERLAPPEDWINDOW, // A normal window + windowX, + windowY, + width, + height, + 0, + 0, + hInstance, + nil) +}