Compare commits

...

49 Commits

Author SHA1 Message Date
Travis McLane d14e4048aa [dialogs] remove default title
without this removal a user would have to call `SetTitle("")` in order to
erase the default if they desire a title-less dialog.
2023-05-10 10:57:25 -05:00
Lea Anthony 7fd627f169 [v3 windows] initial systray support 2023-05-10 19:35:40 +10:00
Lea Anthony b91468b6f2 [v3 mac] Update api. New template icon. 2023-05-09 21:55:17 +10:00
Lea Anthony 0b9cd4be5d [v3 windows] update status 2023-05-09 21:47:22 +10:00
Lea Anthony 4c587ee1b8 [v3 windows] New icons 2023-05-09 19:53:01 +10:00
Lea Anthony 19e1e8b8a6 [v3 windows] Move icons to own package, systray dark mode icon, window.Focus(), 2023-05-09 19:52:33 +10:00
Lea Anthony 67e9522c67 [v3 windows] Rename systray callback handlers 2023-05-08 20:16:25 +10:00
Lea Anthony a829b38a34 [v3 windows] Dialogs to use invokeSync 2023-05-08 20:11:51 +10:00
Lea Anthony c8dae94b5b [v3 windows] Systray callback handlers 2023-05-08 19:43:58 +10:00
stffabi 87267758ac [v3 windows] Fix wndproc default case with WMMessageToString 2023-05-07 22:17:43 +02:00
stffabi 5f72df81fb [v3 windows] Do not disable WndProc messaging for systray when updating icon 2023-05-07 21:40:40 +02:00
Lea Anthony 1ed270fe05 [v3 windows] Fix systray icon size 2023-05-07 20:19:37 +10:00
Lea Anthony 19a654a2b1 [v3 windows] Initial systray support 2023-05-07 19:13:07 +10:00
Lea Anthony f9bbc11711 [v3] Use invokeSync for systray methods 2023-05-07 10:34:08 +10:00
Lea Anthony 42b1807c36 [v3 windows] Support irregular shaped windows. Centered option. 2023-05-06 20:22:28 +10:00
Lea Anthony 647982de1a [v3 windows] Support irregular shaped windows 2023-05-06 15:05:00 +10:00
Lea Anthony f5557c612a Merge remote-tracking branch 'origin/v3-alpha' into v3-alpha 2023-05-05 06:41:04 +10:00
stffabi f4749db8b3 [v3] Add some missing methods for darwin and windows 2023-05-02 21:56:40 +02:00
Travis McLane 86a1de6788 [w32] move windows specific code to impl file 2023-05-02 10:52:21 -05:00
Travis McLane 792c5e2d95 [w32] add missing build constraint 2023-05-02 10:52:09 -05:00
Lea Anthony 6758580be9 [v3 windows] Add frameless resize 2023-05-02 23:18:22 +10:00
stffabi 6f246eed4a [v3 windows] Add HiDPI awareness 2023-05-02 09:45:21 +02:00
stffabi fc3725d3f4 [v3 windows] Add frameless support 2023-05-02 09:29:47 +02:00
Lea Anthony 00c6f0dfdb [v3 windows] Implement getScreen 2023-05-01 21:11:40 +10:00
Lea Anthony cf7b4e2458 [v3] Ensure impl calls from WebvieWindow are on the main thread. Support size. 2023-05-01 20:28:46 +10:00
Lea Anthony f682e44367 [v3] Fix examples 2023-05-01 18:24:24 +10:00
Lea Anthony 9d1f86c410 [v3] Update examples to use correct options. 2023-05-01 18:21:22 +10:00
Lea Anthony dac281ac32 [v3] Change WebviewWindow options to be a value, not a pointer. Support Un/Fullscreen. Remove main thread switching. Use parent options instead of local variables. 2023-05-01 18:20:28 +10:00
Lea Anthony 29a58086a3 [v3 Windows] Support application hide/show. Add WebviewWindow.IsVisible(). 2023-05-01 11:34:06 +10:00
Lea Anthony cb8eb755a7 [v3 Windows] Support setMin/MaxSize, setPosition 2023-05-01 10:52:46 +10:00
Lea Anthony 1e8fc29ee4 [v3 Windows] Add Support for SetTitle, Center, Un/Minimise/Maximise, IsMin/Maximised, IsNormal, Show/Hide 2023-04-30 20:55:51 +10:00
Lea Anthony 9a05b49e3d [v3 Breaking Change] Add NativeWindowHandle method to WebviewWindow. 2023-04-30 10:17:03 +10:00
Lea Anthony 79f8d92084 [v3 windows] Moved w32 from internal to pkg so it may be used by applications 2023-04-30 10:02:00 +10:00
Lea Anthony 6e56542586 [v3 windows] Add WndProcInterceptor for custom message processing 2023-04-30 09:49:50 +10:00
Lea Anthony c53443b62b [v3 windows] Rename options_windows.go -> options_win.go 2023-04-30 09:10:00 +10:00
Lea Anthony 1128662c89 [v3 windows] Add APM Events 2023-04-29 20:33:33 +10:00
Lea Anthony 676787417f [v3] Update application.On and window.On to return functions that unregister the listener. WebviewWindow.onApplicationEvent is a helper which will manage the unregistering for you on window destroy. 2023-04-29 19:39:05 +10:00
Lea Anthony 57422dccf3 [windows] Split out wndProc. Generate windows events, support per-window themes 2023-04-29 12:14:12 +10:00
Lea Anthony 7f3f51e36b [windows] Support AlwaysOnTop, EnableResize at runtime. Added Solid/Transparent/Translucent options. 2023-04-28 21:11:49 +10:00
stffabi ef184ec8bf [v3, windows] Add MainThread dispatching and fixes the blocking window 2023-04-26 21:06:54 +02:00
Lea Anthony 9bfe3094dd [windows] WIP 2023-04-26 21:07:04 +10:00
stffabi 3547b4d010 [v2, darwin] Add some missing default shortcuts (#2586)
* [v2, darwin] Add "Hide, Hide Others, Show All“ to appmenu

This also includes shortcuts support for those commands.
Arrange the menu items in the well known MacOS order.

* [v2, darwin] Add Window menu with well known shortcuts Minimize, Full-Screen and Zoom.
2023-04-26 21:07:04 +10:00
stffabi 1222e3aa1b [v2, dev] Use custom schemes for in-app dev mode (#2610)
This fixes some long-standing inconsistencies between
dev mode builds and production builds but is a breaking
change. Dev mode uses custom scheme for Vite versions >= 3.0.0
and for older it still behaves in the old way.
2023-04-26 21:07:03 +10:00
stffabi cff3ee5079 [assetServer] Improve release/close handling of webview requests (#2612) 2023-04-26 21:07:03 +10:00
Travis McLane c91aa462aa [darwin] add getPrimaryScreen/getScreens to impl (#2618) 2023-04-24 09:34:44 +10:00
Lea Anthony 62b3775e2f Fix module path for non-modified repo 2023-04-19 08:22:58 +10:00
Lea Anthony 54bf8c1142 [windows] Initial commit 2023-04-18 21:27:09 +10:00
Lea Anthony c4f613e4c5 [windows] Fix paths for wails init 2023-04-18 20:41:59 +10:00
Lea Anthony a66d9ab0b1 Intial STATUS.md commit 2023-04-18 18:53:39 +10:00
152 changed files with 13154 additions and 529 deletions
+34 -10
View File
@@ -22,6 +22,7 @@ import (
"github.com/wailsapp/wails/v2/cmd/wails/flags"
"github.com/wailsapp/wails/v2/cmd/wails/internal/gomod"
"github.com/wailsapp/wails/v2/cmd/wails/internal/logutils"
"golang.org/x/mod/semver"
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
@@ -36,6 +37,10 @@ import (
"github.com/wailsapp/wails/v2/pkg/commands/build"
)
const (
viteMinVersion = "v3.0.0"
)
func sliceToMap(input []string) map[string]struct{} {
result := map[string]struct{}{}
for _, value := range input {
@@ -88,10 +93,11 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error {
buildOptions.IgnoreApplication = false
}
legacyUseDevServerInsteadofCustomScheme := false
// frontend:dev:watcher command.
frontendDevAutoDiscovery := projectConfig.IsFrontendDevServerURLAutoDiscovery()
if command := projectConfig.DevWatcherCommand; command != "" {
closer, devServerURL, err := runFrontendDevWatcherCommand(projectConfig.GetFrontendDir(), command, frontendDevAutoDiscovery)
closer, devServerURL, devServerViteVersion, err := runFrontendDevWatcherCommand(projectConfig.GetFrontendDir(), command, frontendDevAutoDiscovery)
if err != nil {
return err
}
@@ -100,6 +106,12 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error {
f.FrontendDevServerURL = devServerURL
}
defer closer()
if devServerViteVersion != "" && semver.Compare(devServerViteVersion, viteMinVersion) < 0 {
logutils.LogRed("Please upgrade your Vite Server to at least '%s' future Wails versions will require at least Vite '%s'", viteMinVersion, viteMinVersion)
time.Sleep(3 * time.Second)
legacyUseDevServerInsteadofCustomScheme = true
}
} else if frontendDevAutoDiscovery {
return fmt.Errorf("unable to auto discover frontend:dev:serverUrl without a frontend:dev:watcher command, please either set frontend:dev:watcher or remove the auto discovery from frontend:dev:serverUrl")
}
@@ -107,7 +119,7 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error {
// Do initial build but only for the application.
logger.Println("Building application for development...")
buildOptions.IgnoreFrontend = true
debugBinaryProcess, appBinary, err := restartApp(buildOptions, nil, f, exitCodeChannel)
debugBinaryProcess, appBinary, err := restartApp(buildOptions, nil, f, exitCodeChannel, legacyUseDevServerInsteadofCustomScheme)
buildOptions.IgnoreFrontend = ignoreFrontend || f.FrontendDevServerURL != ""
if err != nil {
return err
@@ -153,7 +165,7 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error {
}()
// Watch for changes and trigger restartApp()
debugBinaryProcess = doWatcherLoop(buildOptions, debugBinaryProcess, f, watcher, exitCodeChannel, quitChannel, f.DevServerURL())
debugBinaryProcess = doWatcherLoop(buildOptions, debugBinaryProcess, f, watcher, exitCodeChannel, quitChannel, f.DevServerURL(), legacyUseDevServerInsteadofCustomScheme)
// Kill the current program if running and remove dev binary
if err := killProcessAndCleanupBinary(debugBinaryProcess, appBinary); err != nil {
@@ -202,7 +214,7 @@ func runCommand(dir string, exitOnError bool, command string, args ...string) er
}
// runFrontendDevWatcherCommand will run the `frontend:dev:watcher` command if it was given, ex- `npm run dev`
func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, discoverViteServerURL bool) (func(), string, error) {
func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, discoverViteServerURL bool) (func(), string, string, error) {
ctx, cancel := context.WithCancel(context.Background())
scanner := NewStdoutScanner()
cmdSlice := strings.Split(devCommand, " ")
@@ -214,7 +226,7 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d
if err := cmd.Start(); err != nil {
cancel()
return nil, "", fmt.Errorf("unable to start frontend DevWatcher: %w", err)
return nil, "", "", fmt.Errorf("unable to start frontend DevWatcher: %w", err)
}
var viteServerURL string
@@ -224,10 +236,19 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d
viteServerURL = serverURL
case <-time.After(time.Second * 10):
cancel()
return nil, "", errors.New("failed to find Vite server URL")
return nil, "", "", errors.New("failed to find Vite server URL")
}
}
viteVersion := ""
select {
case version := <-scanner.ViteServerVersionC:
viteVersion = version
case <-time.After(time.Second * 5):
// That's fine, then most probably it was not vite that was running
}
logutils.LogGreen("Running frontend DevWatcher command: '%s'", devCommand)
var wg sync.WaitGroup
wg.Add(1)
@@ -255,11 +276,11 @@ func runFrontendDevWatcherCommand(frontendDirectory string, devCommand string, d
}
cancel()
wg.Wait()
}, viteServerURL, nil
}, viteServerURL, viteVersion, nil
}
// restartApp does the actual rebuilding of the application when files change
func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, exitCodeChannel chan int) (*process.Process, string, error) {
func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, exitCodeChannel chan int, legacyUseDevServerInsteadofCustomScheme bool) (*process.Process, string, error) {
appBinary, err := build.Build(buildOptions)
println()
@@ -297,6 +318,9 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process
os.Setenv("assetdir", f.AssetDir)
os.Setenv("devserver", f.DevServer)
os.Setenv("frontenddevserverurl", f.FrontendDevServerURL)
if legacyUseDevServerInsteadofCustomScheme {
os.Setenv("legacyusedevsererinsteadofcustomscheme", "true")
}
// Start up new binary with correct args
newProcess := process.NewProcess(appBinary, args...)
@@ -316,7 +340,7 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process
}
// doWatcherLoop is the main watch loop that runs while dev is active
func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, watcher *fsnotify.Watcher, exitCodeChannel chan int, quitChannel chan os.Signal, devServerURL *url.URL) *process.Process {
func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Process, f *flags.Dev, watcher *fsnotify.Watcher, exitCodeChannel chan int, quitChannel chan os.Signal, devServerURL *url.URL, legacyUseDevServerInsteadofCustomScheme bool) *process.Process {
// Main Loop
var extensionsThatTriggerARebuild = sliceToMap(strings.Split(f.Extensions, ","))
var dirsThatTriggerAReload []string
@@ -422,7 +446,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
rebuild = false
logutils.LogGreen("[Rebuild triggered] files updated")
// Try and build the app
newBinaryProcess, _, err := restartApp(buildOptions, debugBinaryProcess, f, exitCodeChannel)
newBinaryProcess, _, err := restartApp(buildOptions, debugBinaryProcess, f, exitCodeChannel, legacyUseDevServerInsteadofCustomScheme)
if err != nil {
logutils.LogRed("Error during build: %s", err.Error())
continue
+37 -2
View File
@@ -2,30 +2,47 @@ package dev
import (
"bufio"
"fmt"
"net/url"
"os"
"strings"
"github.com/acarl005/stripansi"
"github.com/wailsapp/wails/v2/cmd/wails/internal/logutils"
"golang.org/x/mod/semver"
)
// stdoutScanner acts as a stdout target that will scan the incoming
// data to find out the vite server url
type stdoutScanner struct {
ViteServerURLChan chan string
ViteServerURLChan chan string
ViteServerVersionC chan string
versionDetected bool
}
// NewStdoutScanner creates a new stdoutScanner
func NewStdoutScanner() *stdoutScanner {
return &stdoutScanner{
ViteServerURLChan: make(chan string, 2),
ViteServerURLChan: make(chan string, 2),
ViteServerVersionC: make(chan string, 2),
}
}
// Write bytes to the scanner. Will copy the bytes to stdout
func (s *stdoutScanner) Write(data []byte) (n int, err error) {
input := stripansi.Strip(string(data))
if !s.versionDetected {
v, err := detectViteVersion(input)
if v != "" || err != nil {
if err != nil {
logutils.LogRed("ViteStdoutScanner: %s", err)
v = "v0.0.0"
}
s.ViteServerVersionC <- v
s.versionDetected = true
}
}
match := strings.Index(input, "Local:")
if match != -1 {
sc := bufio.NewScanner(strings.NewReader(input))
@@ -47,3 +64,21 @@ func (s *stdoutScanner) Write(data []byte) (n int, err error) {
}
return os.Stdout.Write(data)
}
func detectViteVersion(line string) (string, error) {
s := strings.Split(strings.TrimSpace(line), " ")
if strings.ToLower(s[0]) != "vite" {
return "", nil
}
if len(line) < 2 {
return "", fmt.Errorf("unable to parse vite version")
}
v := s[1]
if !semver.IsValid(v) {
return "", fmt.Errorf("%s is not a valid vite version string", v)
}
return v, nil
}
@@ -8,6 +8,6 @@
"preview": "vite preview"
},
"devDependencies": {
"vite": "^2.9.9"
"vite": "^3.0.7"
}
}
+44 -5
View File
@@ -8,9 +8,11 @@ import (
"flag"
"fmt"
iofs "io/fs"
"net"
"net/url"
"os"
"path/filepath"
"time"
"github.com/wailsapp/wails/v2/pkg/assetserver"
@@ -104,17 +106,35 @@ func CreateApp(appoptions *options.App) (*App, error) {
}
if frontendDevServerURL != "" {
if devServer == "" {
return nil, fmt.Errorf("Unable to use FrontendDevServerUrl without a DevServer address")
if os.Getenv("legacyusedevsererinsteadofcustomscheme") != "" {
startURL, err := url.Parse("http://" + devServer)
if err != nil {
return nil, err
}
ctx = context.WithValue(ctx, "starturl", startURL)
}
startURL, err := url.Parse("http://" + devServer)
ctx = context.WithValue(ctx, "frontenddevserverurl", frontendDevServerURL)
externalURL, err := url.Parse(frontendDevServerURL)
if err != nil {
return nil, err
}
ctx = context.WithValue(ctx, "starturl", startURL)
ctx = context.WithValue(ctx, "frontenddevserverurl", frontendDevServerURL)
if externalURL.Host == "" {
return nil, fmt.Errorf("Invalid frontend:dev:serverUrl missing protocol scheme?")
}
waitCb := func() { myLogger.Debug("Waiting for frontend DevServer '%s' to be ready", externalURL) }
if !checkPortIsOpen(externalURL.Host, time.Minute, waitCb) {
myLogger.Error("Timeout waiting for frontend DevServer")
}
handler := assetserver.NewExternalAssetsHandler(myLogger, assetConfig, externalURL)
assetConfig.Assets = nil
assetConfig.Handler = handler
assetConfig.Middleware = nil
myLogger.Info("Serving assets from frontend DevServer URL: %s", frontendDevServerURL)
} else {
@@ -246,3 +266,22 @@ func tryInferAssetDirFromFS(assets iofs.FS) (string, error) {
return path, nil
}
func checkPortIsOpen(host string, timeout time.Duration, waitCB func()) (ret bool) {
if timeout == 0 {
timeout = time.Minute
}
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
conn, _ := net.DialTimeout("tcp", host, 2*time.Second)
if conn != nil {
conn.Close()
return true
}
waitCB()
time.Sleep(1 * time.Second)
}
return false
}
@@ -12,5 +12,6 @@ typedef int Role;
static const Role AppMenu = 1;
static const Role EditMenu = 2;
static const Role WindowMenu = 3;
#endif /* Role_h */
@@ -68,12 +68,20 @@
appName = [[NSProcessInfo processInfo] processName];
}
WailsMenu *appMenu = [[[WailsMenu new] initWithNSTitle:appName] autorelease];
if (ctx.aboutTitle != nil) {
[appMenu addItem:[self newMenuItemWithContext :ctx :[@"About " stringByAppendingString:appName] :@selector(About) :nil :0]];
[appMenu addItem:[NSMenuItem separatorItem]];
}
[appMenu addItem:[self newMenuItem:[@"Hide " stringByAppendingString:appName] :@selector(hide:) :@"h" :NSEventModifierFlagCommand]];
[appMenu addItem:[self newMenuItem:@"Hide Others" :@selector(hideOtherApplications:) :@"h" :(NSEventModifierFlagOption | NSEventModifierFlagCommand)]];
[appMenu addItem:[self newMenuItem:@"Show All" :@selector(unhideAllApplications:) :@""]];
[appMenu addItem:[NSMenuItem separatorItem]];
id quitTitle = [@"Quit " stringByAppendingString:appName];
NSMenuItem* quitMenuItem = [self newMenuItem:quitTitle :@selector(Quit) :@"q" :NSEventModifierFlagCommand];
quitMenuItem.target = ctx;
if (ctx.aboutTitle != nil) {
[appMenu addItem:[self newMenuItemWithContext :ctx :[@"About " stringByAppendingString:appName] :@selector(About) :nil :0]];
}
[appMenu addItem:quitMenuItem];
[self appendSubmenu:appMenu];
break;
@@ -100,6 +108,17 @@
[editMenu appendSubmenu:speechMenu];
[self appendSubmenu:editMenu];
break;
}
case WindowMenu:
{
WailsMenu *windowMenu = [[[WailsMenu new] initWithNSTitle:@"Window"] autorelease];
[windowMenu addItem:[self newMenuItem:@"Minimize" :@selector(performMiniaturize:) :@"m" :NSEventModifierFlagCommand]];
[windowMenu addItem:[self newMenuItem:@"Zoom" :@selector(performZoom:) :@""]];
[windowMenu addItem:[NSMenuItem separatorItem]];
[windowMenu addItem:[self newMenuItem:@"Full Screen" :@selector(enterFullScreenMode:) :@"f" :(NSEventModifierFlagControl | NSEventModifierFlagCommand)]];
[self appendSubmenu:windowMenu];
break;
}
}
@@ -113,7 +113,6 @@ func (f *Frontend) startMessageProcessor() {
func (f *Frontend) startRequestProcessor() {
for request := range requestBuffer {
f.assets.ServeWebViewRequest(request)
request.Release()
}
}
func (f *Frontend) startCallbackProcessor() {
@@ -466,7 +466,6 @@ var requestBuffer = make(chan webview.Request, 100)
func (f *Frontend) startRequestProcessor() {
for request := range requestBuffer {
f.assets.ServeWebViewRequest(request)
request.Release()
}
}
+5 -37
View File
@@ -10,13 +10,11 @@ import (
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"sync"
"time"
"github.com/wailsapp/wails/v2/pkg/assetserver"
@@ -67,7 +65,6 @@ func (d *DevWebServer) Run(ctx context.Context) error {
myLogger = _logger.(*logger.Logger)
}
var assetHandler http.Handler
var wsHandler http.Handler
_fronendDevServerURL, _ := ctx.Value("frontenddevserverurl").(string)
@@ -77,33 +74,23 @@ func (d *DevWebServer) Run(ctx context.Context) error {
return c.String(http.StatusOK, assetdir)
})
var err error
assetHandler, err = assetserver.NewAssetHandler(assetServerConfig, myLogger)
if err != nil {
log.Fatal(err)
}
} else {
externalURL, err := url.Parse(_fronendDevServerURL)
if err != nil {
return err
}
if externalURL.Host == "" {
return fmt.Errorf("Invalid frontend:dev:serverUrl missing protocol scheme?")
}
waitCb := func() { d.LogDebug("Waiting for frontend DevServer '%s' to be ready", externalURL) }
if !checkPortIsOpen(externalURL.Host, time.Minute, waitCb) {
d.logger.Error("Timeout waiting for frontend DevServer")
}
assetHandler = newExternalDevServerAssetHandler(d.logger, externalURL, assetServerConfig)
// WebSockets aren't currently supported in prod mode, so a WebSocket connection is the result of the
// FrontendDevServer e.g. Vite to support auto reloads.
// Therefore we direct WebSockets directly to the FrontendDevServer instead of returning a NotImplementedStatus.
wsHandler = httputil.NewSingleHostReverseProxy(externalURL)
}
assetHandler, err := assetserver.NewAssetHandler(assetServerConfig, myLogger)
if err != nil {
log.Fatal(err)
}
// Setup internal dev server
bindingsJSON, err := d.appBindings.ToJSON()
if err != nil {
@@ -307,22 +294,3 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
result.server.HidePort = true
return result
}
func checkPortIsOpen(host string, timeout time.Duration, waitCB func()) (ret bool) {
if timeout == 0 {
timeout = time.Minute
}
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
conn, _ := net.DialTimeout("tcp", host, 2*time.Second)
if conn != nil {
conn.Close()
return true
}
waitCB()
time.Sleep(1 * time.Second)
}
return false
}
@@ -1,7 +1,7 @@
//go:build dev
// +build dev
package devserver
package assetserver
import (
"errors"
@@ -10,21 +10,12 @@ import (
"net/http/httputil"
"net/url"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
func newExternalDevServerAssetHandler(logger *logger.Logger, url *url.URL, options assetserver.Options) http.Handler {
handler := newExternalAssetsHandler(logger, url, options.Handler)
func NewExternalAssetsHandler(logger Logger, options assetserver.Options, url *url.URL) http.Handler {
baseHandler := options.Handler
if middleware := options.Middleware; middleware != nil {
handler = middleware(handler)
}
return handler
}
func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http.Handler) http.Handler {
errSkipProxy := fmt.Errorf("skip proxying")
proxy := httputil.NewSingleHostReverseProxy(url)
@@ -37,7 +28,7 @@ func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http.
}
proxy.ModifyResponse = func(res *http.Response) error {
if handler == nil {
if baseHandler == nil {
return nil
}
@@ -53,11 +44,11 @@ func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http.
}
proxy.ErrorHandler = func(rw http.ResponseWriter, r *http.Request, err error) {
if handler != nil && errors.Is(err, errSkipProxy) {
if baseHandler != nil && errors.Is(err, errSkipProxy) {
if logger != nil {
logger.Debug("[ExternalAssetHandler] Loading '%s' failed, using AssetHandler", r.URL)
logger.Debug("[ExternalAssetHandler] Loading '%s' failed, using original AssetHandler", r.URL)
}
handler.ServeHTTP(rw, r)
baseHandler.ServeHTTP(rw, r)
} else {
if logger != nil {
logger.Error("[ExternalAssetHandler] Proxy error: %v", err)
@@ -66,18 +57,24 @@ func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http.
}
}
return http.HandlerFunc(
var result http.Handler = http.HandlerFunc(
func(rw http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodGet {
proxy.ServeHTTP(rw, req)
return
}
if handler != nil {
handler.ServeHTTP(rw, req)
if baseHandler != nil {
baseHandler.ServeHTTP(rw, req)
return
}
rw.WriteHeader(http.StatusMethodNotAllowed)
})
if middleware := options.Middleware; middleware != nil {
result = middleware(result)
}
return result
}
+2 -10
View File
@@ -56,13 +56,7 @@ func (r legacyRequest) Response() webview.ResponseWriter {
return &legacyRequestNoOpCloserResponseWriter{r.rw}
}
func (r legacyRequest) AddRef() error {
return nil
}
func (r legacyRequest) Release() error {
return nil
}
func (r legacyRequest) Close() error { return nil }
func (r *legacyRequest) request() (*http.Request, error) {
if r.req != nil {
@@ -81,6 +75,4 @@ type legacyRequestNoOpCloserResponseWriter struct {
http.ResponseWriter
}
func (*legacyRequestNoOpCloserResponseWriter) Finish() error {
return nil
}
func (*legacyRequestNoOpCloserResponseWriter) Finish() {}
+5 -7
View File
@@ -22,6 +22,7 @@ type assetServerWebView struct {
// ServeWebViewRequest processes the HTTP Request asynchronously by faking a golang HTTP Server.
// The request will be finished with a StatusNotImplemented code if no handler has written to the response.
// The AssetServer takes ownership of the request and the caller mustn't close it or access it in any other way.
func (d *AssetServer) ServeWebViewRequest(req webview.Request) {
d.dispatchInit.Do(func() {
workers := d.dispatchWorkers
@@ -33,8 +34,11 @@ func (d *AssetServer) ServeWebViewRequest(req webview.Request) {
for i := 0; i < workers; i++ {
go func() {
for req := range workerC {
uri, _ := req.URL()
d.processWebViewRequest(req)
req.Release()
if err := req.Close(); err != nil {
d.logError("Unable to call close for request for uri '%s'", uri)
}
}
}()
}
@@ -45,12 +49,6 @@ func (d *AssetServer) ServeWebViewRequest(req webview.Request) {
d.dispatchReqC = dispatchC
})
if err := req.AddRef(); err != nil {
uri, _ := req.URL()
d.logError("Unable to call AddRef for request '%s'", uri)
return
}
d.dispatchReqC <- req
}
+1 -2
View File
@@ -13,6 +13,5 @@ type Request interface {
Response() ResponseWriter
AddRef() error
Release() error
Close() error
}
+11 -13
View File
@@ -118,11 +118,9 @@ import (
)
// NewRequest creates as new WebViewRequest based on a pointer to an `id<WKURLSchemeTask>`
//
// Please make sure to call Release() when finished using the request.
func NewRequest(wkURLSchemeTask unsafe.Pointer) Request {
C.URLSchemeTaskRetain(wkURLSchemeTask)
return &request{task: wkURLSchemeTask}
return newRequestFinalizer(&request{task: wkURLSchemeTask})
}
var _ Request = &request{}
@@ -135,16 +133,6 @@ type request struct {
rw *responseWriter
}
func (r *request) AddRef() error {
C.URLSchemeTaskRetain(r.task)
return nil
}
func (r *request) Release() error {
C.URLSchemeTaskRelease(r.task)
return nil
}
func (r *request) URL() (string, error) {
return C.GoString(C.URLSchemeTaskRequestURL(r.task)), nil
}
@@ -205,6 +193,16 @@ func (r *request) Response() ResponseWriter {
return r.rw
}
func (r *request) Close() error {
var err error
if r.body != nil {
err = r.body.Close()
}
r.Response().Finish()
C.URLSchemeTaskRelease(r.task)
return err
}
var _ io.ReadCloser = &requestBodyStreamReader{}
type requestBodyStreamReader struct {
@@ -0,0 +1,40 @@
package webview
import (
"runtime"
"sync/atomic"
)
var _ Request = &requestFinalizer{}
type requestFinalizer struct {
Request
closed int32
}
// newRequestFinalizer returns a request with a runtime finalizer to make sure it will be closed from the finalizer
// if it has not been already closed.
// It also makes sure Close() of the wrapping request is only called once.
func newRequestFinalizer(r Request) Request {
rf := &requestFinalizer{Request: r}
// Make sure to async release since it might block the finalizer goroutine for a longer period
runtime.SetFinalizer(rf, func(obj *requestFinalizer) { rf.close(true) })
return rf
}
func (r *requestFinalizer) Close() error {
return r.close(false)
}
func (r *requestFinalizer) close(asyncRelease bool) error {
if atomic.CompareAndSwapInt32(&r.closed, 0, 1) {
runtime.SetFinalizer(r, nil)
if asyncRelease {
go r.Request.Close()
return nil
} else {
return r.Request.Close()
}
}
return nil
}
+13 -14
View File
@@ -18,13 +18,12 @@ import (
)
// NewRequest creates as new WebViewRequest based on a pointer to an `WebKitURISchemeRequest`
//
// Please make sure to call Release() when finished using the request.
func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request {
webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest)
C.g_object_ref(C.gpointer(webkitReq))
req := &request{req: webkitReq}
req.AddRef()
return req
return newRequestFinalizer(req)
}
var _ Request = &request{}
@@ -37,16 +36,6 @@ type request struct {
rw *responseWriter
}
func (r *request) AddRef() error {
C.g_object_ref(C.gpointer(r.req))
return nil
}
func (r *request) Release() error {
C.g_object_unref(C.gpointer(r.req))
return nil
}
func (r *request) URL() (string, error) {
return C.GoString(C.webkit_uri_scheme_request_get_uri(r.req)), nil
}
@@ -82,3 +71,13 @@ func (r *request) Response() ResponseWriter {
r.rw = &responseWriter{req: r.req}
return r.rw
}
func (r *request) Close() error {
var err error
if r.body != nil {
err = r.body.Close()
}
r.Response().Finish()
C.g_object_unref(C.gpointer(r.req))
return err
}
+2 -2
View File
@@ -20,6 +20,6 @@ var (
type ResponseWriter interface {
http.ResponseWriter
// Finish the response and flush all data.
Finish() error
// Finish the response and flush all data. A Finish after the request has already been finished has no effect.
Finish()
}
@@ -133,16 +133,15 @@ func (rw *responseWriter) WriteHeader(code int) {
C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen))
}
func (rw *responseWriter) Finish() error {
func (rw *responseWriter) Finish() {
if !rw.wroteHeader {
rw.WriteHeader(http.StatusNotImplemented)
}
if rw.finished {
return nil
return
}
rw.finished = true
C.URLSchemeTaskDidFinish(rw.r.task)
return nil
}
@@ -84,19 +84,18 @@ func (rw *responseWriter) WriteHeader(code int) {
}
}
func (rw *responseWriter) Finish() error {
func (rw *responseWriter) Finish() {
if !rw.wroteHeader {
rw.WriteHeader(http.StatusNotImplemented)
}
if rw.finished {
return nil
return
}
rw.finished = true
if rw.w != nil {
rw.w.Close()
}
return nil
}
func (rw *responseWriter) finishWithError(code int, err error) {
+6 -3
View File
@@ -8,8 +8,9 @@ type Role int
// These constants need to be kept in sync with `v2/internal/frontend/desktop/darwin/Role.h`
const (
AppMenuRole Role = 1
EditMenuRole = 2
AppMenuRole Role = 1
EditMenuRole = 2
WindowMenuRole = 3
//AboutRole Role = "about"
//UndoRole Role = "undo"
//RedoRole Role = "redo"
@@ -142,14 +143,16 @@ func ViewMenu() *MenuItem {
Role: ViewMenuRole,
}
}
*/
// WindowMenu provides a MenuItem with the whole default "Window" menu (Minimize, Zoom, etc.).
// On MacOS currently all options in there won't work if the window is frameless.
func WindowMenu() *MenuItem {
return &MenuItem{
Role: WindowMenuRole,
}
}
*/
// These roles are Mac only
// AppMenu provides a MenuItem with the whole default "App" menu (About, Services, etc.)

Some files were not shown because too many files have changed in this diff Show More