mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 17:52:29 +08:00

* get dimensions working for linux * Cleaning up some GTK code I was getting the following errors due to some bad casts. Gdk-CRITICAL **: 18:58:51.943: gdk_monitor_get_geometry: assertion 'GDK_IS_MONITOR (monitor)' failed Gdk-CRITICAL **: 18:58:51.943: gdk_display_get_monitor_at_window: assertion 'GDK_IS_DISPLAY (display)' failed This commit fixes these errors * Adding Screen namespace along with linux implementation * moving ScreenGetAll into a more appropriate place * Fixing typescript definition mistake, documentation, ordering of functions, and formatting * add ScreenGetAll to more templates * moving screen into its own javascript file * fixing bug where screen objects are not returned from the runtime function * rebuilding frontend wrapper package * adding windows implementation of ScreenGetAll * adding screen get all implementation for darwin * reverting a change that is unrelated to the work on expose-dimensions * removing duplicate comparison * changing GetNthScreen in screen API on macos To use frame instead of visibleframe to keep into account the space the the dock takes up We want to include that space in the calculation in order to keep the sizes of screens consistent across platforms * Correcting screen jsdoc It used to say it returned a single screen object. Now it says that it returns an array of screen objects * Fixing typo in function name * reverting pointless spacing change * reverting pointless spacing change Co-authored-by: Lea Anthony <lea.anthony@gmail.com> Co-authored-by: shmuel.kamensky <shmuel.kamensky@shutterfly.com>
762 lines
19 KiB
Go
762 lines
19 KiB
Go
//go:build windows
|
|
// +build windows
|
|
|
|
package windows
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/bep/debounce"
|
|
"github.com/wailsapp/wails/v2/internal/binding"
|
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
|
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
|
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge"
|
|
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/win32"
|
|
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc"
|
|
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
|
"github.com/wailsapp/wails/v2/internal/logger"
|
|
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
|
"github.com/wailsapp/wails/v2/pkg/options"
|
|
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"text/template"
|
|
"time"
|
|
)
|
|
|
|
const startURL = "http://wails.localhost/"
|
|
|
|
type Screen = frontend.Screen
|
|
|
|
type Frontend struct {
|
|
|
|
// Context
|
|
ctx context.Context
|
|
|
|
frontendOptions *options.App
|
|
logger *logger.Logger
|
|
chromium *edge.Chromium
|
|
debug bool
|
|
|
|
// Assets
|
|
assets *assetserver.AssetServer
|
|
startURL *url.URL
|
|
|
|
// main window handle
|
|
mainWindow *Window
|
|
bindings *binding.Bindings
|
|
dispatcher frontend.Dispatcher
|
|
|
|
hasStarted bool
|
|
|
|
// Windows build number
|
|
versionInfo *operatingsystem.WindowsVersionInfo
|
|
resizeDebouncer func(f func())
|
|
}
|
|
|
|
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
|
|
|
|
// Get Windows build number
|
|
versionInfo, _ := operatingsystem.GetWindowsVersionInfo()
|
|
|
|
result := &Frontend{
|
|
frontendOptions: appoptions,
|
|
logger: myLogger,
|
|
bindings: appBindings,
|
|
dispatcher: dispatcher,
|
|
ctx: ctx,
|
|
versionInfo: versionInfo,
|
|
}
|
|
|
|
if appoptions.Windows != nil {
|
|
if appoptions.Windows.ResizeDebounceMS > 0 {
|
|
result.resizeDebouncer = debounce.New(time.Duration(appoptions.Windows.ResizeDebounceMS) * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
// We currently can't use wails://wails/ as other platforms do, therefore we map the assets sever onto the following url.
|
|
result.startURL, _ = url.Parse(startURL)
|
|
|
|
if _starturl, _ := ctx.Value("starturl").(*url.URL); _starturl != nil {
|
|
result.startURL = _starturl
|
|
return result
|
|
}
|
|
|
|
bindingsJSON, err := appBindings.ToJSON()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
result.assets = assets
|
|
|
|
return result
|
|
}
|
|
|
|
func (f *Frontend) WindowReload() {
|
|
f.ExecJS("runtime.WindowReload();")
|
|
}
|
|
|
|
func (f *Frontend) WindowSetSystemDefaultTheme() {
|
|
f.mainWindow.SetTheme(windows.SystemDefault)
|
|
}
|
|
|
|
func (f *Frontend) WindowSetLightTheme() {
|
|
f.mainWindow.SetTheme(windows.Light)
|
|
}
|
|
|
|
func (f *Frontend) WindowSetDarkTheme() {
|
|
f.mainWindow.SetTheme(windows.Dark)
|
|
}
|
|
|
|
func (f *Frontend) Run(ctx context.Context) error {
|
|
|
|
f.ctx = context.WithValue(ctx, "frontend", f)
|
|
|
|
mainWindow := NewWindow(nil, f.frontendOptions, f.versionInfo)
|
|
f.mainWindow = mainWindow
|
|
|
|
var _debug = ctx.Value("debug")
|
|
if _debug != nil {
|
|
f.debug = _debug.(bool)
|
|
}
|
|
|
|
f.WindowCenter()
|
|
f.setupChromium()
|
|
|
|
f.mainWindow.notifyParentWindowPositionChanged = f.chromium.NotifyParentWindowPositionChanged
|
|
|
|
mainWindow.OnSize().Bind(func(arg *winc.Event) {
|
|
if f.frontendOptions.Frameless {
|
|
// If the window is frameless and we are minimizing, then we need to suppress the Resize on the
|
|
// WebView2. If we don't do this, restoring does not work as expected and first restores with some wrong
|
|
// size during the restore animation and only fully renders when the animation is done. This highly
|
|
// depends on the content in the WebView, see https://github.com/wailsapp/wails/issues/1319
|
|
event, _ := arg.Data.(*winc.SizeEventData)
|
|
if event != nil && event.Type == w32.SIZE_MINIMIZED {
|
|
return
|
|
}
|
|
}
|
|
|
|
if f.resizeDebouncer != nil {
|
|
f.resizeDebouncer(func() {
|
|
f.mainWindow.Invoke(func() {
|
|
f.chromium.Resize()
|
|
})
|
|
})
|
|
} else {
|
|
f.chromium.Resize()
|
|
}
|
|
})
|
|
|
|
mainWindow.OnClose().Bind(func(arg *winc.Event) {
|
|
if f.frontendOptions.HideWindowOnClose {
|
|
f.WindowHide()
|
|
} else {
|
|
f.Quit()
|
|
}
|
|
})
|
|
|
|
go func() {
|
|
if f.frontendOptions.OnStartup != nil {
|
|
f.frontendOptions.OnStartup(f.ctx)
|
|
}
|
|
}()
|
|
mainWindow.Run()
|
|
mainWindow.Close()
|
|
return nil
|
|
}
|
|
|
|
func (f *Frontend) WindowCenter() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.Center()
|
|
}
|
|
|
|
func (f *Frontend) WindowSetAlwaysOnTop(b bool) {
|
|
runtime.LockOSThread()
|
|
f.mainWindow.SetAlwaysOnTop(b)
|
|
}
|
|
|
|
func (f *Frontend) WindowSetPosition(x, y int) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.SetPos(x, y)
|
|
}
|
|
func (f *Frontend) WindowGetPosition() (int, int) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
return f.mainWindow.Pos()
|
|
}
|
|
|
|
func (f *Frontend) WindowSetSize(width, height int) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.SetSize(width, height)
|
|
}
|
|
|
|
func (f *Frontend) WindowGetSize() (int, int) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
return f.mainWindow.Size()
|
|
}
|
|
|
|
func (f *Frontend) WindowSetTitle(title string) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.SetText(title)
|
|
}
|
|
|
|
func (f *Frontend) WindowFullscreen() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
|
|
f.ExecJS("window.wails.flags.enableResize = false;")
|
|
}
|
|
f.mainWindow.Fullscreen()
|
|
}
|
|
|
|
func (f *Frontend) WindowReloadApp() {
|
|
f.ExecJS(fmt.Sprintf("window.location.href = '%s';", f.startURL))
|
|
}
|
|
|
|
func (f *Frontend) WindowUnfullscreen() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
|
|
f.ExecJS("window.wails.flags.enableResize = true;")
|
|
}
|
|
f.mainWindow.UnFullscreen()
|
|
}
|
|
|
|
func (f *Frontend) WindowShow() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.ShowWindow()
|
|
}
|
|
|
|
func (f *Frontend) WindowHide() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.Hide()
|
|
}
|
|
|
|
func (f *Frontend) WindowMaximise() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
if f.hasStarted {
|
|
if !f.frontendOptions.DisableResize {
|
|
f.mainWindow.Maximise()
|
|
}
|
|
} else {
|
|
f.frontendOptions.WindowStartState = options.Maximised
|
|
}
|
|
}
|
|
|
|
func (f *Frontend) WindowToggleMaximise() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
if !f.hasStarted {
|
|
return
|
|
}
|
|
if f.mainWindow.IsMaximised() {
|
|
f.WindowUnmaximise()
|
|
} else {
|
|
f.WindowMaximise()
|
|
}
|
|
}
|
|
|
|
func (f *Frontend) WindowUnmaximise() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.Restore()
|
|
}
|
|
|
|
func (f *Frontend) WindowMinimise() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
if f.hasStarted {
|
|
f.mainWindow.Minimise()
|
|
} else {
|
|
f.frontendOptions.WindowStartState = options.Minimised
|
|
}
|
|
}
|
|
|
|
func (f *Frontend) WindowUnminimise() {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.Restore()
|
|
}
|
|
|
|
func (f *Frontend) WindowSetMinSize(width int, height int) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.SetMinSize(width, height)
|
|
}
|
|
func (f *Frontend) WindowSetMaxSize(width int, height int) {
|
|
runtime.LockOSThread()
|
|
defer runtime.UnlockOSThread()
|
|
f.mainWindow.SetMaxSize(width, height)
|
|
}
|
|
|
|
func (f *Frontend) WindowSetBackgroundColour(col *options.RGBA) {
|
|
if col == nil {
|
|
return
|
|
}
|
|
|
|
f.mainWindow.Invoke(func() {
|
|
controller := f.chromium.GetController()
|
|
controller2 := controller.GetICoreWebView2Controller2()
|
|
|
|
backgroundCol := edge.COREWEBVIEW2_COLOR{
|
|
A: col.A,
|
|
R: col.R,
|
|
G: col.G,
|
|
B: col.B,
|
|
}
|
|
|
|
// Webview2 only has 0 and 255 as valid values.
|
|
if backgroundCol.A > 0 && backgroundCol.A < 255 {
|
|
backgroundCol.A = 255
|
|
}
|
|
|
|
if f.frontendOptions.Windows != nil && f.frontendOptions.Windows.WebviewIsTransparent {
|
|
backgroundCol.A = 0
|
|
}
|
|
|
|
err := controller2.PutDefaultBackgroundColor(backgroundCol)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
func (f *Frontend) ScreenGetAll() ([]Screen, error) {
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
screens := []Screen{}
|
|
err := error(nil)
|
|
f.mainWindow.Invoke(func() {
|
|
screens, err = GetAllScreens(f.mainWindow.Handle())
|
|
wg.Done()
|
|
|
|
})
|
|
wg.Wait()
|
|
return screens, err
|
|
}
|
|
|
|
func (f *Frontend) Quit() {
|
|
if f.frontendOptions.OnBeforeClose != nil && f.frontendOptions.OnBeforeClose(f.ctx) {
|
|
return
|
|
}
|
|
// Exit must be called on the Main-Thread. It calls PostQuitMessage which sends the WM_QUIT message to the thread's
|
|
// message queue and our message queue runs on the Main-Thread.
|
|
f.mainWindow.Invoke(winc.Exit)
|
|
}
|
|
|
|
func (f *Frontend) setupChromium() {
|
|
chromium := edge.NewChromium()
|
|
f.chromium = chromium
|
|
if opts := f.frontendOptions.Windows; opts != nil {
|
|
chromium.DataPath = opts.WebviewUserDataPath
|
|
chromium.BrowserPath = opts.WebviewBrowserPath
|
|
}
|
|
chromium.MessageCallback = f.processMessage
|
|
chromium.WebResourceRequestedCallback = f.processRequest
|
|
chromium.NavigationCompletedCallback = f.navigationCompleted
|
|
chromium.AcceleratorKeyCallback = func(vkey uint) bool {
|
|
w32.PostMessage(f.mainWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0)
|
|
return false
|
|
}
|
|
chromium.Embed(f.mainWindow.Handle())
|
|
chromium.Resize()
|
|
settings, err := chromium.GetSettings()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = settings.PutAreDefaultContextMenusEnabled(f.debug)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = settings.PutAreDevToolsEnabled(f.debug)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = settings.PutIsZoomControlEnabled(false)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = settings.PutIsStatusBarEnabled(false)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = settings.PutAreBrowserAcceleratorKeysEnabled(false)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = settings.PutIsSwipeNavigationEnabled(false)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Setup focus event handler
|
|
onFocus := f.mainWindow.OnSetFocus()
|
|
onFocus.Bind(f.onFocus)
|
|
|
|
// Set background colour
|
|
f.WindowSetBackgroundColour(f.frontendOptions.BackgroundColour)
|
|
|
|
chromium.SetGlobalPermission(edge.CoreWebView2PermissionStateAllow)
|
|
chromium.AddWebResourceRequestedFilter("*", edge.COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL)
|
|
chromium.Navigate(f.startURL.String())
|
|
}
|
|
|
|
type EventNotify struct {
|
|
Name string `json:"name"`
|
|
Data []interface{} `json:"data"`
|
|
}
|
|
|
|
func (f *Frontend) Notify(name string, data ...interface{}) {
|
|
notification := EventNotify{
|
|
Name: name,
|
|
Data: data,
|
|
}
|
|
payload, err := json.Marshal(notification)
|
|
if err != nil {
|
|
f.logger.Error(err.Error())
|
|
return
|
|
}
|
|
f.ExecJS(`window.wails.EventsNotify('` + template.JSEscapeString(string(payload)) + `');`)
|
|
}
|
|
|
|
func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, args *edge.ICoreWebView2WebResourceRequestedEventArgs) {
|
|
// Setting the UserAgent on the CoreWebView2Settings clears the whole default UserAgent of the Edge browser, but
|
|
// we want to just append our ApplicationIdentifier. So we adjust the UserAgent for every request.
|
|
if reqHeaders, err := req.GetHeaders(); err == nil {
|
|
useragent, _ := reqHeaders.GetHeader(assetserver.HeaderUserAgent)
|
|
useragent = strings.Join([]string{useragent, assetserver.WailsUserAgentValue}, " ")
|
|
reqHeaders.SetHeader(assetserver.HeaderUserAgent, useragent)
|
|
reqHeaders.Release()
|
|
}
|
|
|
|
if f.assets == nil {
|
|
// We are using the devServer let the WebView2 handle the request with its default handler
|
|
return
|
|
}
|
|
|
|
//Get the request
|
|
uri, _ := req.GetUri()
|
|
reqUri, err := url.ParseRequestURI(uri)
|
|
if err != nil {
|
|
f.logger.Error("Unable to parse equest uri %s: %s", uri, err)
|
|
return
|
|
}
|
|
|
|
if reqUri.Scheme != f.startURL.Scheme {
|
|
// Let the WebView2 handle the request with its default handler
|
|
return
|
|
} else if reqUri.Host != f.startURL.Host {
|
|
// Let the WebView2 handle the request with its default handler
|
|
return
|
|
}
|
|
|
|
logInfo := strings.Replace(uri, f.startURL.String(), "", 1)
|
|
|
|
rw := httptest.NewRecorder()
|
|
f.assets.ProcessHTTPRequest(logInfo, rw, coreWebview2RequestToHttpRequest(req))
|
|
|
|
headers := []string{}
|
|
for k, v := range rw.Header() {
|
|
headers = append(headers, fmt.Sprintf("%s: %s", k, strings.Join(v, ",")))
|
|
}
|
|
|
|
env := f.chromium.Environment()
|
|
response, err := env.CreateWebResourceResponse(rw.Body.Bytes(), rw.Code, http.StatusText(rw.Code), strings.Join(headers, "\n"))
|
|
if err != nil {
|
|
f.logger.Error("CreateWebResourceResponse Error: %s", err)
|
|
return
|
|
}
|
|
defer response.Release()
|
|
|
|
// Send response back
|
|
err = args.PutResponse(response)
|
|
if err != nil {
|
|
f.logger.Error("PutResponse Error: %s", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
var edgeMap = map[string]uintptr{
|
|
"n-resize": w32.HTTOP,
|
|
"ne-resize": w32.HTTOPRIGHT,
|
|
"e-resize": w32.HTRIGHT,
|
|
"se-resize": w32.HTBOTTOMRIGHT,
|
|
"s-resize": w32.HTBOTTOM,
|
|
"sw-resize": w32.HTBOTTOMLEFT,
|
|
"w-resize": w32.HTLEFT,
|
|
"nw-resize": w32.HTTOPLEFT,
|
|
}
|
|
|
|
func (f *Frontend) processMessage(message string) {
|
|
if message == "drag" {
|
|
if !f.mainWindow.IsFullScreen() {
|
|
err := f.startDrag()
|
|
if err != nil {
|
|
f.logger.Error(err.Error())
|
|
}
|
|
}
|
|
return
|
|
}
|
|
if strings.HasPrefix(message, "resize:") {
|
|
if !f.mainWindow.IsFullScreen() {
|
|
sl := strings.Split(message, ":")
|
|
if len(sl) != 2 {
|
|
f.logger.Info("Unknown message returned from dispatcher: %+v", message)
|
|
return
|
|
}
|
|
edge := edgeMap[sl[1]]
|
|
err := f.startResize(edge)
|
|
if err != nil {
|
|
f.logger.Error(err.Error())
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
result, err := f.dispatcher.ProcessMessage(message, f)
|
|
if err != nil {
|
|
f.logger.Error(err.Error())
|
|
f.Callback(result)
|
|
return
|
|
}
|
|
if result == "" {
|
|
return
|
|
}
|
|
|
|
switch result[0] {
|
|
case 'c':
|
|
// Callback from a method call
|
|
f.Callback(result[1:])
|
|
default:
|
|
f.logger.Info("Unknown message returned from dispatcher: %+v", result)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (f *Frontend) Callback(message string) {
|
|
f.mainWindow.Invoke(func() {
|
|
f.chromium.Eval(`window.wails.Callback(` + strconv.Quote(message) + `);`)
|
|
})
|
|
}
|
|
|
|
func (f *Frontend) startDrag() error {
|
|
if !w32.ReleaseCapture() {
|
|
return fmt.Errorf("unable to release mouse capture")
|
|
}
|
|
// Use PostMessage because we don't want to block the caller until dragging has been finished.
|
|
w32.PostMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0)
|
|
return nil
|
|
}
|
|
|
|
func (f *Frontend) startResize(border uintptr) error {
|
|
if !w32.ReleaseCapture() {
|
|
return fmt.Errorf("unable to release mouse capture")
|
|
}
|
|
// Use PostMessage because we don't want to block the caller until resizing has been finished.
|
|
w32.PostMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, border, 0)
|
|
return nil
|
|
}
|
|
|
|
func (f *Frontend) ExecJS(js string) {
|
|
f.mainWindow.Invoke(func() {
|
|
f.chromium.Eval(js)
|
|
})
|
|
}
|
|
|
|
func (f *Frontend) navigationCompleted(sender *edge.ICoreWebView2, args *edge.ICoreWebView2NavigationCompletedEventArgs) {
|
|
if f.frontendOptions.OnDomReady != nil {
|
|
go f.frontendOptions.OnDomReady(f.ctx)
|
|
}
|
|
|
|
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
|
|
f.ExecJS("window.wails.flags.enableResize = true;")
|
|
}
|
|
|
|
if f.hasStarted {
|
|
return
|
|
}
|
|
f.hasStarted = true
|
|
|
|
// Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026
|
|
err := f.chromium.Hide()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = f.chromium.Show()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if f.frontendOptions.StartHidden {
|
|
return
|
|
}
|
|
|
|
switch f.frontendOptions.WindowStartState {
|
|
case options.Maximised:
|
|
if !f.frontendOptions.DisableResize {
|
|
win32.ShowWindowMaximised(f.mainWindow.Handle())
|
|
} else {
|
|
win32.ShowWindow(f.mainWindow.Handle())
|
|
}
|
|
case options.Minimised:
|
|
win32.ShowWindowMinimised(f.mainWindow.Handle())
|
|
case options.Fullscreen:
|
|
f.mainWindow.Fullscreen()
|
|
win32.ShowWindow(f.mainWindow.Handle())
|
|
default:
|
|
if f.frontendOptions.Fullscreen {
|
|
f.mainWindow.Fullscreen()
|
|
}
|
|
win32.ShowWindow(f.mainWindow.Handle())
|
|
}
|
|
|
|
f.mainWindow.hasBeenShown = true
|
|
|
|
}
|
|
|
|
func (f *Frontend) ShowWindow() {
|
|
f.mainWindow.Invoke(func() {
|
|
if !f.mainWindow.hasBeenShown {
|
|
f.mainWindow.hasBeenShown = true
|
|
switch f.frontendOptions.WindowStartState {
|
|
case options.Maximised:
|
|
if !f.frontendOptions.DisableResize {
|
|
win32.ShowWindowMaximised(f.mainWindow.Handle())
|
|
} else {
|
|
win32.ShowWindow(f.mainWindow.Handle())
|
|
}
|
|
case options.Minimised:
|
|
win32.RestoreWindow(f.mainWindow.Handle())
|
|
case options.Fullscreen:
|
|
f.mainWindow.Fullscreen()
|
|
win32.ShowWindow(f.mainWindow.Handle())
|
|
default:
|
|
if f.frontendOptions.Fullscreen {
|
|
f.mainWindow.Fullscreen()
|
|
}
|
|
win32.ShowWindow(f.mainWindow.Handle())
|
|
}
|
|
} else {
|
|
if win32.IsWindowMinimised(f.mainWindow.Handle()) {
|
|
win32.RestoreWindow(f.mainWindow.Handle())
|
|
} else {
|
|
win32.ShowWindow(f.mainWindow.Handle())
|
|
}
|
|
}
|
|
w32.SetForegroundWindow(f.mainWindow.Handle())
|
|
w32.SetFocus(f.mainWindow.Handle())
|
|
})
|
|
|
|
}
|
|
|
|
func (f *Frontend) onFocus(arg *winc.Event) {
|
|
f.chromium.Focus()
|
|
}
|
|
|
|
func coreWebview2RequestToHttpRequest(coreReq *edge.ICoreWebView2WebResourceRequest) func() (*http.Request, error) {
|
|
return func() (r *http.Request, err error) {
|
|
header := http.Header{}
|
|
headers, err := coreReq.GetHeaders()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GetHeaders Error: %s", err)
|
|
}
|
|
defer headers.Release()
|
|
|
|
headersIt, err := headers.GetIterator()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GetIterator Error: %s", err)
|
|
}
|
|
defer headersIt.Release()
|
|
|
|
for {
|
|
has, err := headersIt.HasCurrentHeader()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("HasCurrentHeader Error: %s", err)
|
|
}
|
|
if !has {
|
|
break
|
|
}
|
|
|
|
name, value, err := headersIt.GetCurrentHeader()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GetCurrentHeader Error: %s", err)
|
|
}
|
|
|
|
header.Set(name, value)
|
|
if _, err := headersIt.MoveNext(); err != nil {
|
|
return nil, fmt.Errorf("MoveNext Error: %s", err)
|
|
}
|
|
}
|
|
|
|
method, err := coreReq.GetMethod()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GetMethod Error: %s", err)
|
|
}
|
|
|
|
uri, err := coreReq.GetUri()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("GetUri Error: %s", err)
|
|
}
|
|
|
|
var body io.ReadCloser
|
|
if content, err := coreReq.GetContent(); err != nil {
|
|
return nil, fmt.Errorf("GetContent Error: %s", err)
|
|
} else if content != nil {
|
|
body = &iStreamReleaseCloser{stream: content}
|
|
}
|
|
|
|
req, err := http.NewRequest(method, uri, body)
|
|
if err != nil {
|
|
if body != nil {
|
|
body.Close()
|
|
}
|
|
return nil, err
|
|
}
|
|
req.Header = header
|
|
return req, nil
|
|
}
|
|
}
|
|
|
|
type iStreamReleaseCloser struct {
|
|
stream *edge.IStream
|
|
closed bool
|
|
}
|
|
|
|
func (i *iStreamReleaseCloser) Read(p []byte) (int, error) {
|
|
if i.closed {
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
return i.stream.Read(p)
|
|
}
|
|
|
|
func (i *iStreamReleaseCloser) Close() error {
|
|
if i.closed {
|
|
return nil
|
|
}
|
|
i.closed = true
|
|
return i.stream.Release()
|
|
}
|