mirror of
https://github.com/taigrr/wails.git
synced 2026-04-17 12:15:02 -07:00
Compare commits
35 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b9bcd657f | ||
|
|
02038aa543 | ||
|
|
9910d1127a | ||
|
|
21a0245985 | ||
|
|
e860f3a00e | ||
|
|
2e480d2c52 | ||
|
|
2c0f93d647 | ||
|
|
41f973c7d5 | ||
|
|
2d1447d558 | ||
|
|
7c22cbcf38 | ||
|
|
e4b03f510b | ||
|
|
8dfd206aa9 | ||
|
|
6dabab1d2e | ||
|
|
c3bd8b1a85 | ||
|
|
e1b7332c47 | ||
|
|
5cd08e45f0 | ||
|
|
c2d03f0e6e | ||
|
|
d3501f4cb7 | ||
|
|
ee82cd25b7 | ||
|
|
bbb07e17d9 | ||
|
|
e6b40b55c4 | ||
|
|
7573f68df3 | ||
|
|
ceaacc7ba9 | ||
|
|
0e24f75753 | ||
|
|
82b9deeee4 | ||
|
|
cfa40b797f | ||
|
|
5aeb68acb7 | ||
|
|
b81101414f | ||
|
|
7ae89d04bb | ||
|
|
1c566f3802 | ||
|
|
c9c3c9ab90 | ||
|
|
56394ac50e | ||
|
|
f7c2f12ab2 | ||
|
|
a6bb6e0c93 | ||
|
|
4a5863e6ac |
@@ -3,12 +3,12 @@
|
|||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"amd": true,
|
"amd": true,
|
||||||
"node": true,
|
"node": true
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2016,
|
"ecmaVersion": 2016,
|
||||||
"sourceType": "module",
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
"github.com/wailsapp/wails/v2/internal/process"
|
"github.com/wailsapp/wails/v2/internal/process"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
@@ -24,14 +25,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
command := app.NewSubCommand("dev", "Development mode")
|
command := app.NewSubCommand("dev", "Development mode")
|
||||||
|
|
||||||
outputType := "desktop"
|
|
||||||
|
|
||||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
|
||||||
|
|
||||||
// Setup target type flag
|
|
||||||
description := "Type of application to develop. Valid types: " + validTargetTypes.Join(",")
|
|
||||||
command.StringFlag("t", description, &outputType)
|
|
||||||
|
|
||||||
// Passthrough ldflags
|
// Passthrough ldflags
|
||||||
ldflags := ""
|
ldflags := ""
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||||
@@ -42,15 +35,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// extensions to trigger rebuilds
|
// extensions to trigger rebuilds
|
||||||
extensions := "go"
|
extensions := "go"
|
||||||
command.StringFlag("m", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
// Validate inputs
|
|
||||||
if !validTargetTypes.Contains(outputType) {
|
|
||||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := clilogger.New(w)
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
@@ -64,7 +52,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
|
|
||||||
var debugBinaryProcess *process.Process = nil
|
var debugBinaryProcess *process.Process = nil
|
||||||
var buildFrontend bool = true
|
var buildFrontend bool = false
|
||||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
@@ -75,8 +63,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// Do initial build
|
// Do initial build
|
||||||
logger.Println("Building application for development...")
|
logger.Println("Building application for development...")
|
||||||
debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
debugBinaryProcess, err = restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||||
// logger.Println("event: %+v", event)
|
// logger.Println("event: %+v", event)
|
||||||
|
|
||||||
@@ -98,7 +88,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
// Check for file writes
|
// Check for file writes
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
|
||||||
// logger.Println("modified file: %s", event.Name)
|
logger.Println("modified file: %s", event.Name)
|
||||||
var rebuild bool = false
|
var rebuild bool = false
|
||||||
|
|
||||||
// Iterate all file patterns
|
// Iterate all file patterns
|
||||||
@@ -115,21 +105,20 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !rebuild {
|
if !rebuild {
|
||||||
logger.Println("Filename change: %s did not match extension list %s", event.Name, extensions)
|
logger.Println("Filename change: %s did not match extension list (%s)", event.Name, extensions)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if buildFrontend {
|
logger.Println("Partial build triggered: %s updated", event.Name)
|
||||||
logger.Println("Full rebuild triggered: %s updated", event.Name)
|
|
||||||
} else {
|
|
||||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a rebuild
|
// Do a rebuild
|
||||||
|
|
||||||
// Try and build the app
|
// Try and build the app
|
||||||
newBinaryProcess := restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error during build: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
// If we have a new process, save it
|
// If we have a new process, save it
|
||||||
if newBinaryProcess != nil {
|
if newBinaryProcess != nil {
|
||||||
debugBinaryProcess = newBinaryProcess
|
debugBinaryProcess = newBinaryProcess
|
||||||
@@ -167,7 +156,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
for quit == false {
|
for quit == false {
|
||||||
select {
|
select {
|
||||||
case <-quitChannel:
|
case <-quitChannel:
|
||||||
println()
|
println("Caught quit")
|
||||||
// Notify debouncer to quit
|
// Notify debouncer to quit
|
||||||
debounceQuit <- true
|
debounceQuit <- true
|
||||||
quit = true
|
quit = true
|
||||||
@@ -209,13 +198,13 @@ exit:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process) (*process.Process, error) {
|
||||||
|
|
||||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
|
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||||
println()
|
println()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Println("[ERROR] Build Failed: %s", err.Error())
|
logger.Fatal(err.Error())
|
||||||
return nil
|
return nil, errors.Wrap(err, "Build Failed:")
|
||||||
}
|
}
|
||||||
logger.Println("Build new binary: %s", appBinary)
|
logger.Println("Build new binary: %s", appBinary)
|
||||||
|
|
||||||
@@ -244,10 +233,10 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
|||||||
logger.Fatal("Unable to start application: %s", err.Error())
|
logger.Fatal("Unable to start application: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return newProcess
|
return newProcess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
||||||
|
|
||||||
// Create random output file
|
// Create random output file
|
||||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||||
@@ -262,7 +251,7 @@ func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, co
|
|||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
Compiler: compilerCommand,
|
Compiler: compilerCommand,
|
||||||
OutputFile: outputFile,
|
OutputFile: outputFile,
|
||||||
IgnoreFrontend: !buildFrontend,
|
IgnoreFrontend: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Build(buildOptions)
|
return build.Build(buildOptions)
|
||||||
|
|||||||
164
v2/cmd/wails/internal/commands/update/update.go
Normal file
164
v2/cmd/wails/internal/commands/update/update.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/github"
|
||||||
|
|
||||||
|
"github.com/leaanthony/clir"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddSubcommand adds the `init` command for the Wails application
|
||||||
|
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
||||||
|
|
||||||
|
command := app.NewSubCommand("update", "Update the Wails CLI")
|
||||||
|
command.LongDescription(`This command allows you to update your version of Wails.`)
|
||||||
|
|
||||||
|
// Setup flags
|
||||||
|
var prereleaseRequired bool
|
||||||
|
command.BoolFlag("pre", "Update to latest Prerelease", &prereleaseRequired)
|
||||||
|
|
||||||
|
var specificVersion string
|
||||||
|
command.StringFlag("version", "Install a specific version (Overrides other flags)", &specificVersion)
|
||||||
|
|
||||||
|
command.Action(func() error {
|
||||||
|
|
||||||
|
// Create logger
|
||||||
|
logger := clilogger.New(w)
|
||||||
|
|
||||||
|
// Print banner
|
||||||
|
app.PrintBanner()
|
||||||
|
logger.Println("Checking for updates...")
|
||||||
|
|
||||||
|
var desiredVersion *github.SemanticVersion
|
||||||
|
var err error
|
||||||
|
var valid bool
|
||||||
|
|
||||||
|
if len(specificVersion) > 0 {
|
||||||
|
// Check if this is a valid version
|
||||||
|
valid, err = github.IsValidTag(specificVersion)
|
||||||
|
if err == nil {
|
||||||
|
if !valid {
|
||||||
|
err = fmt.Errorf("version '%s' is invalid", specificVersion)
|
||||||
|
} else {
|
||||||
|
desiredVersion, err = github.NewSemanticVersion(specificVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if prereleaseRequired {
|
||||||
|
desiredVersion, err = github.GetLatestPreRelease()
|
||||||
|
} else {
|
||||||
|
desiredVersion, err = github.GetLatestStableRelease()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
fmt.Println(" Current Version : " + currentVersion)
|
||||||
|
|
||||||
|
if len(specificVersion) > 0 {
|
||||||
|
fmt.Printf(" Desired Version : v%s\n", desiredVersion)
|
||||||
|
} else {
|
||||||
|
if prereleaseRequired {
|
||||||
|
fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Latest Release : v%s\n", desiredVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateToVersion(logger, desiredVersion, len(specificVersion) > 0, currentVersion)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.SemanticVersion, force bool, currentVersion string) error {
|
||||||
|
|
||||||
|
var targetVersionString = "v" + targetVersion.String()
|
||||||
|
|
||||||
|
// Early exit
|
||||||
|
if targetVersionString == currentVersion {
|
||||||
|
logger.Println("Looks like you're up to date!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var desiredVersion string
|
||||||
|
|
||||||
|
if !force {
|
||||||
|
|
||||||
|
compareVersion := currentVersion
|
||||||
|
|
||||||
|
currentVersion, err := github.NewSemanticVersion(compareVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var success bool
|
||||||
|
|
||||||
|
// Release -> Pre-Release = Massage current version to prerelease format
|
||||||
|
if targetVersion.IsPreRelease() && currentVersion.IsRelease() {
|
||||||
|
testVersion, err := github.NewSemanticVersion(compareVersion + "-0")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
success, _ = targetVersion.IsGreaterThan(testVersion)
|
||||||
|
}
|
||||||
|
// Pre-Release -> Release = Massage target version to prerelease format
|
||||||
|
if targetVersion.IsRelease() && currentVersion.IsPreRelease() {
|
||||||
|
// We are ok with greater than or equal
|
||||||
|
mainversion := currentVersion.MainVersion()
|
||||||
|
targetVersion, err = github.NewSemanticVersion(targetVersion.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
success, _ = targetVersion.IsGreaterThanOrEqual(mainversion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release -> Release = Standard check
|
||||||
|
if (targetVersion.IsRelease() && currentVersion.IsRelease()) ||
|
||||||
|
(targetVersion.IsPreRelease() && currentVersion.IsPreRelease()) {
|
||||||
|
|
||||||
|
success, _ = targetVersion.IsGreaterThan(currentVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare
|
||||||
|
if !success {
|
||||||
|
logger.Println("Error: The requested version is lower than the current version.")
|
||||||
|
logger.Println("If this is what you really want to do, use `wails update -version %s`", targetVersionString)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
desiredVersion = "v" + targetVersion.String()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
desiredVersion = "v" + targetVersion.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
logger.Print("Installing Wails " + desiredVersion + "...")
|
||||||
|
|
||||||
|
// Run command in non module directory
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Cannot find home directory! Please file a bug report!")
|
||||||
|
}
|
||||||
|
|
||||||
|
sout, serr, err := shell.RunCommand(homeDir, "go", "get", "github.com/wailsapp/wails/v2/cmd/wails@"+desiredVersion)
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("Failed.")
|
||||||
|
logger.Println(sout + `\n` + serr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
logger.Println("Wails updated to " + desiredVersion)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/debug"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/debug"
|
||||||
@@ -48,6 +50,11 @@ func main() {
|
|||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = update.AddSubcommand(app, os.Stdout, version)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
err = app.Run()
|
err = app.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("\n\nERROR: " + err.Error())
|
println("\n\nERROR: " + err.Error())
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.16"
|
var version = "v2.0.0-alpha.25"
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
module github.com/wailsapp/wails/v2
|
module github.com/wailsapp/wails/v2
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Masterminds/semver v1.5.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/fatih/structtag v1.2.0
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
|
github.com/gorilla/websocket v1.4.1
|
||||||
github.com/imdario/mergo v0.3.11
|
github.com/imdario/mergo v0.3.11
|
||||||
github.com/jackmordaunt/icns v1.0.0
|
github.com/jackmordaunt/icns v1.0.0
|
||||||
github.com/leaanthony/clir v1.0.4
|
github.com/leaanthony/clir v1.0.4
|
||||||
@@ -13,7 +15,6 @@ require (
|
|||||||
github.com/leaanthony/slicer v1.5.0
|
github.com/leaanthony/slicer v1.5.0
|
||||||
github.com/matryer/is v1.4.0
|
github.com/matryer/is v1.4.0
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible
|
github.com/tdewolff/minify v2.3.6+incompatible
|
||||||
@@ -21,8 +22,8 @@ require (
|
|||||||
github.com/tdewolff/test v1.0.6 // indirect
|
github.com/tdewolff/test v1.0.6 // indirect
|
||||||
github.com/xyproto/xpm v1.2.1
|
github.com/xyproto/xpm v1.2.1
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
|
||||||
nhooyr.io/websocket v1.8.6
|
nhooyr.io/websocket v1.8.6
|
||||||
)
|
)
|
||||||
|
|||||||
20
v2/go.sum
20
v2/go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||||
|
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -9,7 +11,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
|||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
@@ -39,9 +40,6 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn
|
|||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
||||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
||||||
@@ -62,17 +60,13 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
|
|||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||||
@@ -98,35 +92,29 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up
|
|||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
|
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !desktop,!hybrid,!server
|
// +build !desktop,!hybrid,!server,!dev
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -26,10 +29,10 @@ type App struct {
|
|||||||
options *options.App
|
options *options.App
|
||||||
|
|
||||||
// Subsystems
|
// Subsystems
|
||||||
log *subsystem.Log
|
log *subsystem.Log
|
||||||
runtime *subsystem.Runtime
|
runtime *subsystem.Runtime
|
||||||
event *subsystem.Event
|
event *subsystem.Event
|
||||||
binding *subsystem.Binding
|
//binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
menu *subsystem.Menu
|
menu *subsystem.Menu
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
@@ -81,12 +84,15 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
|
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
|
||||||
|
|
||||||
|
// Create binding exemptions - Ugly hack. There must be a better way
|
||||||
|
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
appType: "desktop",
|
appType: "desktop",
|
||||||
window: window,
|
window: window,
|
||||||
servicebus: servicebus.New(myLogger),
|
servicebus: servicebus.New(myLogger),
|
||||||
logger: myLogger,
|
logger: myLogger,
|
||||||
bindings: binding.NewBindings(myLogger, appoptions.Bind),
|
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
||||||
menuManager: menuManager,
|
menuManager: menuManager,
|
||||||
startupCallback: appoptions.Startup,
|
startupCallback: appoptions.Startup,
|
||||||
shutdownCallback: appoptions.Shutdown,
|
shutdownCallback: appoptions.Shutdown,
|
||||||
@@ -106,8 +112,13 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// Setup a context
|
||||||
|
var subsystemWaitGroup sync.WaitGroup
|
||||||
|
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||||
|
ctx, cancel := context.WithCancel(parentContext)
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
signalsubsystem, err := signal.NewManager(a.servicebus, a.logger)
|
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger, a.shutdownCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -121,7 +132,7 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -135,17 +146,6 @@ func (a *App) Run() error {
|
|||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
||||||
|
|
||||||
// Start the binding subsystem
|
|
||||||
bindingsubsystem, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.binding = bindingsubsystem
|
|
||||||
err = a.binding.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the logging subsystem
|
// Start the logging subsystem
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -169,18 +169,18 @@ func (a *App) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start the eventing subsystem
|
// Start the eventing subsystem
|
||||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.event = event
|
a.event = eventsubsystem
|
||||||
err = a.event.Start()
|
err = a.event.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the menu subsystem
|
// Start the menu subsystem
|
||||||
menusubsystem, err := subsystem.NewMenu(a.servicebus, a.logger, a.menuManager)
|
menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -191,11 +191,11 @@ func (a *App) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start the call subsystem
|
// Start the call subsystem
|
||||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.call = call
|
a.call = callSubsystem
|
||||||
err = a.call.Start()
|
err = a.call.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -207,12 +207,29 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := a.window.Run(dispatcher, bindingDump, a.debug)
|
err = a.window.Run(dispatcher, bindingDump, a.debug)
|
||||||
a.logger.Trace("Ffenestri.Run() exited")
|
a.logger.Trace("Ffenestri.Run() exited")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close down all the subsystems
|
||||||
|
a.logger.Trace("Cancelling subsystems")
|
||||||
|
cancel()
|
||||||
|
subsystemWaitGroup.Wait()
|
||||||
|
|
||||||
|
a.logger.Trace("Cancelling dispatcher")
|
||||||
|
dispatcher.Close()
|
||||||
|
|
||||||
|
// Close log
|
||||||
|
a.logger.Trace("Stopping log")
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
a.logger.Trace("Stopping Service bus")
|
||||||
err = a.servicebus.Stop()
|
err = a.servicebus.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
241
v2/internal/app/dev.go
Normal file
241
v2/internal/app/dev.go
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
// +build dev
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/bridge"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/signal"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// App defines a Wails application structure
|
||||||
|
type App struct {
|
||||||
|
appType string
|
||||||
|
|
||||||
|
servicebus *servicebus.ServiceBus
|
||||||
|
logger *logger.Logger
|
||||||
|
signal *signal.Manager
|
||||||
|
options *options.App
|
||||||
|
|
||||||
|
// Subsystems
|
||||||
|
log *subsystem.Log
|
||||||
|
runtime *subsystem.Runtime
|
||||||
|
event *subsystem.Event
|
||||||
|
//binding *subsystem.Binding
|
||||||
|
call *subsystem.Call
|
||||||
|
menu *subsystem.Menu
|
||||||
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
|
|
||||||
|
menuManager *menumanager.Manager
|
||||||
|
|
||||||
|
// Indicates if the app is in debug mode
|
||||||
|
debug bool
|
||||||
|
|
||||||
|
// This is our binding DB
|
||||||
|
bindings *binding.Bindings
|
||||||
|
|
||||||
|
// Application Stores
|
||||||
|
loglevelStore *runtime.Store
|
||||||
|
appconfigStore *runtime.Store
|
||||||
|
|
||||||
|
// Startup/Shutdown
|
||||||
|
startupCallback func(*runtime.Runtime)
|
||||||
|
shutdownCallback func()
|
||||||
|
|
||||||
|
// Bridge
|
||||||
|
bridge *bridge.Bridge
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create App
|
||||||
|
func CreateApp(appoptions *options.App) (*App, error) {
|
||||||
|
|
||||||
|
// Merge default options
|
||||||
|
options.MergeDefaults(appoptions)
|
||||||
|
|
||||||
|
// Set up logger
|
||||||
|
myLogger := logger.New(appoptions.Logger)
|
||||||
|
myLogger.SetLogLevel(appoptions.LogLevel)
|
||||||
|
|
||||||
|
// Create the menu manager
|
||||||
|
menuManager := menumanager.NewManager()
|
||||||
|
|
||||||
|
// Process the application menu
|
||||||
|
menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions))
|
||||||
|
|
||||||
|
// Process context menus
|
||||||
|
contextMenus := options.GetContextMenus(appoptions)
|
||||||
|
for _, contextMenu := range contextMenus {
|
||||||
|
menuManager.AddContextMenu(contextMenu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process tray menus
|
||||||
|
trayMenus := options.GetTrayMenus(appoptions)
|
||||||
|
for _, trayMenu := range trayMenus {
|
||||||
|
menuManager.AddTrayMenu(trayMenu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create binding exemptions - Ugly hack. There must be a better way
|
||||||
|
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
||||||
|
|
||||||
|
result := &App{
|
||||||
|
appType: "dev",
|
||||||
|
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
||||||
|
logger: myLogger,
|
||||||
|
servicebus: servicebus.New(myLogger),
|
||||||
|
startupCallback: appoptions.Startup,
|
||||||
|
shutdownCallback: appoptions.Shutdown,
|
||||||
|
bridge: bridge.NewBridge(myLogger),
|
||||||
|
menuManager: menuManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
result.options = appoptions
|
||||||
|
|
||||||
|
// Initialise the app
|
||||||
|
err := result.Init()
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the application
|
||||||
|
func (a *App) Run() error {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Setup a context
|
||||||
|
var subsystemWaitGroup sync.WaitGroup
|
||||||
|
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||||
|
ctx, cancel := context.WithCancel(parentContext)
|
||||||
|
|
||||||
|
// Setup signal handler
|
||||||
|
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger, a.shutdownCallback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.signal = signalsubsystem
|
||||||
|
a.signal.Start()
|
||||||
|
|
||||||
|
// Start the service bus
|
||||||
|
a.servicebus.Debug()
|
||||||
|
err = a.servicebus.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.runtime = runtimesubsystem
|
||||||
|
err = a.runtime.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application Stores
|
||||||
|
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
||||||
|
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
||||||
|
|
||||||
|
// Start the logging subsystem
|
||||||
|
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.log = log
|
||||||
|
err = a.log.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the dispatcher
|
||||||
|
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.dispatcher = dispatcher
|
||||||
|
err = dispatcher.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the eventing subsystem
|
||||||
|
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.event = eventsubsystem
|
||||||
|
err = a.event.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the menu subsystem
|
||||||
|
menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.menu = menusubsystem
|
||||||
|
err = a.menu.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the call subsystem
|
||||||
|
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.call = callSubsystem
|
||||||
|
err = a.call.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump bindings as a debug
|
||||||
|
bindingDump, err := a.bindings.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate backend.js
|
||||||
|
a.bindings.GenerateBackendJS()
|
||||||
|
|
||||||
|
err = a.bridge.Run(dispatcher, bindingDump, a.debug)
|
||||||
|
a.logger.Trace("Bridge.Run() exited")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close down all the subsystems
|
||||||
|
a.logger.Trace("Cancelling subsystems")
|
||||||
|
cancel()
|
||||||
|
subsystemWaitGroup.Wait()
|
||||||
|
|
||||||
|
a.logger.Trace("Cancelling dispatcher")
|
||||||
|
dispatcher.Close()
|
||||||
|
|
||||||
|
// Close log
|
||||||
|
a.logger.Trace("Stopping log")
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
a.logger.Trace("Stopping Service bus")
|
||||||
|
err = a.servicebus.Stop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
11
v2/internal/binding/assets/package.json
Normal file
11
v2/internal/binding/assets/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Package to wrap backend method calls",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
@@ -2,23 +2,38 @@ package binding
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bindings struct {
|
type Bindings struct {
|
||||||
db *DB
|
db *DB
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
exemptions slicer.StringSlicer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBindings returns a new Bindings object
|
// NewBindings returns a new Bindings object
|
||||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}) *Bindings {
|
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings {
|
||||||
result := &Bindings{
|
result := &Bindings{
|
||||||
db: newDB(),
|
db: newDB(),
|
||||||
logger: logger.CustomLogger("Bindings"),
|
logger: logger.CustomLogger("Bindings"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, exemption := range exemptions {
|
||||||
|
if exemptions == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := runtime.FuncForPC(reflect.ValueOf(exemption).Pointer()).Name()
|
||||||
|
// Yuk yuk yuk! Is there a better way?
|
||||||
|
name = strings.TrimSuffix(name, "-fm")
|
||||||
|
result.exemptions.Add(name)
|
||||||
|
}
|
||||||
|
|
||||||
// Add the structs to bind
|
// Add the structs to bind
|
||||||
for _, ptr := range structPointersToBind {
|
for _, ptr := range structPointersToBind {
|
||||||
err := result.Add(ptr)
|
err := result.Add(ptr)
|
||||||
@@ -33,7 +48,7 @@ func NewBindings(logger *logger.Logger, structPointersToBind []interface{}) *Bin
|
|||||||
// Add the given struct methods to the Bindings
|
// Add the given struct methods to the Bindings
|
||||||
func (b *Bindings) Add(structPtr interface{}) error {
|
func (b *Bindings) Add(structPtr interface{}) error {
|
||||||
|
|
||||||
methods, err := getMethods(structPtr)
|
methods, err := b.getMethods(structPtr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -46,7 +61,6 @@ func (b *Bindings) Add(structPtr interface{}) error {
|
|||||||
|
|
||||||
// Add it as a regular method
|
// Add it as a regular method
|
||||||
b.db.AddMethod(packageName, structName, methodName, method)
|
b.db.AddMethod(packageName, structName, methodName, method)
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ func (b *BoundMethod) OutputCount() int {
|
|||||||
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
||||||
|
|
||||||
result := make([]interface{}, b.InputCount())
|
result := make([]interface{}, b.InputCount())
|
||||||
|
if len(args) != b.InputCount() {
|
||||||
|
return nil, fmt.Errorf("received %d arguments to method '%s', expected %d", len(args), b.Name, b.InputCount())
|
||||||
|
}
|
||||||
for index, arg := range args {
|
for index, arg := range args {
|
||||||
typ := b.Inputs[index].reflectType
|
typ := b.Inputs[index].reflectType
|
||||||
inputValue := reflect.New(typ).Interface()
|
inputValue := reflect.New(typ).Interface()
|
||||||
|
|||||||
150
v2/internal/binding/generate.go
Normal file
150
v2/internal/binding/generate.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const _comment = `
|
||||||
|
|
||||||
|
const backend = {
|
||||||
|
main: {
|
||||||
|
"xbarApp": {
|
||||||
|
"GetCategories": () => {
|
||||||
|
window.backend.main.xbarApp.GetCategories.call(arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} arg1
|
||||||
|
*/
|
||||||
|
"InstallPlugin": (arg1) => {
|
||||||
|
window.backend.main.xbarApp.InstallPlugin.call(arguments);
|
||||||
|
},
|
||||||
|
"GetPlugins": () => {
|
||||||
|
window.backend.main.xbarApp.GetPlugins.call(arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default backend;`
|
||||||
|
|
||||||
|
//go:embed assets/package.json
|
||||||
|
var packageJSON []byte
|
||||||
|
|
||||||
|
func (b *Bindings) GenerateBackendJS() {
|
||||||
|
|
||||||
|
store := b.db.store
|
||||||
|
var output bytes.Buffer
|
||||||
|
|
||||||
|
output.WriteString(`// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
const backend = {`)
|
||||||
|
output.WriteString("\n")
|
||||||
|
|
||||||
|
for packageName, packages := range store {
|
||||||
|
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
||||||
|
output.WriteString("\n")
|
||||||
|
for structName, structs := range packages {
|
||||||
|
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
||||||
|
output.WriteString("\n")
|
||||||
|
for methodName, methodDetails := range structs {
|
||||||
|
output.WriteString(" /**\n")
|
||||||
|
output.WriteString(" * " + methodName + "\n")
|
||||||
|
var args slicer.StringSlicer
|
||||||
|
for count, input := range methodDetails.Inputs {
|
||||||
|
arg := fmt.Sprintf("arg%d", count+1)
|
||||||
|
args.Add(arg)
|
||||||
|
output.WriteString(fmt.Sprintf(" * @param {%s} %s - Go Type: %s\n", goTypeToJSDocType(input.TypeName), arg, input.TypeName))
|
||||||
|
}
|
||||||
|
returnType := "Promise"
|
||||||
|
returnTypeDetails := ""
|
||||||
|
if methodDetails.OutputCount() > 0 {
|
||||||
|
firstType := goTypeToJSDocType(methodDetails.Outputs[0].TypeName)
|
||||||
|
returnType += "<" + firstType
|
||||||
|
if methodDetails.OutputCount() == 2 {
|
||||||
|
secondType := goTypeToJSDocType(methodDetails.Outputs[1].TypeName)
|
||||||
|
returnType += "|" + secondType
|
||||||
|
}
|
||||||
|
returnType += ">"
|
||||||
|
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
|
||||||
|
}
|
||||||
|
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
|
||||||
|
output.WriteString(" */\n")
|
||||||
|
argsString := args.Join(", ")
|
||||||
|
output.WriteString(fmt.Sprintf(" \"%s\": (%s) => {", methodName, argsString))
|
||||||
|
output.WriteString("\n")
|
||||||
|
output.WriteString(fmt.Sprintf(" return window.backend.%s.%s.%s(%s);", packageName, structName, methodName, argsString))
|
||||||
|
output.WriteString("\n")
|
||||||
|
output.WriteString(fmt.Sprintf(" },"))
|
||||||
|
output.WriteString("\n")
|
||||||
|
}
|
||||||
|
output.WriteString(fmt.Sprintf(" }"))
|
||||||
|
output.WriteString("\n")
|
||||||
|
}
|
||||||
|
output.WriteString(fmt.Sprintf(" }\n"))
|
||||||
|
output.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
output.WriteString(`};
|
||||||
|
export default backend;`)
|
||||||
|
output.WriteString("\n")
|
||||||
|
|
||||||
|
dirname, err := fs.RelativeToCwd("frontend/src/backend")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fs.DirExists(dirname) {
|
||||||
|
err := fs.Mkdir(dirname)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packageJsonFile := filepath.Join(dirname, "package.json")
|
||||||
|
if !fs.FileExists(packageJsonFile) {
|
||||||
|
err := os.WriteFile(packageJsonFile, packageJSON, 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := filepath.Join(dirname, "index.js")
|
||||||
|
err = os.WriteFile(filename, output.Bytes(), 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func goTypeToJSDocType(input string) string {
|
||||||
|
switch true {
|
||||||
|
case input == "string":
|
||||||
|
return "string"
|
||||||
|
case input == "error":
|
||||||
|
return "Error"
|
||||||
|
case
|
||||||
|
strings.HasPrefix(input, "int"),
|
||||||
|
strings.HasPrefix(input, "uint"),
|
||||||
|
strings.HasPrefix(input, "float"):
|
||||||
|
return "number"
|
||||||
|
case input == "bool":
|
||||||
|
return "boolean"
|
||||||
|
case strings.HasPrefix(input, "[]"):
|
||||||
|
arrayType := goTypeToJSDocType(input[2:])
|
||||||
|
return "Array.<" + arrayType + ">"
|
||||||
|
default:
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ func isStruct(value interface{}) bool {
|
|||||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
return reflect.ValueOf(value).Kind() == reflect.Struct
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMethods(value interface{}) ([]*BoundMethod, error) {
|
func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||||
|
|
||||||
// Create result placeholder
|
// Create result placeholder
|
||||||
var result []*BoundMethod
|
var result []*BoundMethod
|
||||||
@@ -56,6 +56,11 @@ func getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
fullMethodName := baseName + "." + methodName
|
fullMethodName := baseName + "." + methodName
|
||||||
method := structValue.MethodByName(methodName)
|
method := structValue.MethodByName(methodName)
|
||||||
|
|
||||||
|
methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
|
||||||
|
if b.exemptions.Contains(methodReflectName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Create new method
|
// Create new method
|
||||||
boundMethod := &BoundMethod{
|
boundMethod := &BoundMethod{
|
||||||
Name: fullMethodName,
|
Name: fullMethodName,
|
||||||
|
|||||||
99
v2/internal/bridge/bridge.go
Normal file
99
v2/internal/bridge/bridge.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bridge struct {
|
||||||
|
upgrader websocket.Upgrader
|
||||||
|
server *http.Server
|
||||||
|
myLogger *logger.Logger
|
||||||
|
|
||||||
|
bindings string
|
||||||
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
sessions map[string]*session
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBridge(myLogger *logger.Logger) *Bridge {
|
||||||
|
result := &Bridge{
|
||||||
|
myLogger: myLogger,
|
||||||
|
upgrader: websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }},
|
||||||
|
sessions: make(map[string]*session),
|
||||||
|
}
|
||||||
|
|
||||||
|
myLogger.SetLogLevel(1)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
result.ctx = ctx
|
||||||
|
result.cancel = cancel
|
||||||
|
result.server = &http.Server{Addr: ":34115"}
|
||||||
|
http.HandleFunc("/bridge", result.wsBridgeHandler)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) Run(dispatcher *messagedispatcher.Dispatcher, bindings string, debug bool) error {
|
||||||
|
|
||||||
|
// Ensure we cancel the context when we shutdown
|
||||||
|
defer b.cancel()
|
||||||
|
|
||||||
|
b.bindings = bindings
|
||||||
|
b.dispatcher = dispatcher
|
||||||
|
|
||||||
|
b.myLogger.Info("Bridge mode started.")
|
||||||
|
|
||||||
|
err := b.server.ListenAndServe()
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c, err := b.upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Print("upgrade:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
b.myLogger.Info("Connection from frontend accepted [%s].", c.RemoteAddr().String())
|
||||||
|
b.startSession(c)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) startSession(conn *websocket.Conn) {
|
||||||
|
|
||||||
|
// Create a new session for this connection
|
||||||
|
s := newSession(conn, b.bindings, b.dispatcher, b.myLogger, b.ctx)
|
||||||
|
|
||||||
|
// Setup the close handler
|
||||||
|
conn.SetCloseHandler(func(int, string) error {
|
||||||
|
b.myLogger.Info("Connection dropped [%s].", s.Identifier())
|
||||||
|
b.dispatcher.RemoveClient(s.client)
|
||||||
|
b.mu.Lock()
|
||||||
|
delete(b.sessions, s.Identifier())
|
||||||
|
b.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
go s.start(len(b.sessions) == 0)
|
||||||
|
b.sessions[s.Identifier()] = s
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
158
v2/internal/bridge/client_darwin.go
Normal file
158
v2/internal/bridge/client_darwin.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
"golang.org/x/sync/semaphore"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BridgeClient struct {
|
||||||
|
session *session
|
||||||
|
dialogSemaphore *semaphore.Weighted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) Quit() {
|
||||||
|
b.session.log.Info("Quit unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) NotifyEvent(message string) {
|
||||||
|
//b.session.sendMessage("n" + message)
|
||||||
|
b.session.log.Info("NotifyEvent: %s", message)
|
||||||
|
b.session.log.Info("NotifyEvent unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) CallResult(message string) {
|
||||||
|
b.session.sendMessage("c" + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||||
|
b.session.log.Info("OpenDialog unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
||||||
|
b.session.log.Info("SaveDialog unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
||||||
|
|
||||||
|
osa, err := exec.LookPath("osascript")
|
||||||
|
if err != nil {
|
||||||
|
b.session.log.Info("MessageDialog unavailable (osascript not found)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var btns slicer.StringSlicer
|
||||||
|
defaultButton := ""
|
||||||
|
for index, btn := range dialogOptions.Buttons {
|
||||||
|
btns.Add(strconv.Quote(btn))
|
||||||
|
if btn == dialogOptions.DefaultButton {
|
||||||
|
defaultButton = fmt.Sprintf("default button %d", index+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buttons := "{" + btns.Join(",") + "}"
|
||||||
|
script := fmt.Sprintf("display dialog \"%s\" buttons %s %s with title \"%s\"", dialogOptions.Message, buttons, defaultButton, dialogOptions.Title)
|
||||||
|
|
||||||
|
b.session.log.Info("OSASCRIPT: %s", script)
|
||||||
|
go func() {
|
||||||
|
out, err := exec.Command(osa, "-e", script).Output()
|
||||||
|
if err != nil {
|
||||||
|
b.session.log.Error(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.session.log.Info(string(out))
|
||||||
|
buttonPressed := strings.TrimSpace(strings.TrimPrefix(string(out), "button returned:"))
|
||||||
|
b.session.client.DispatchMessage("DM" + callbackID + "|" + buttonPressed)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetTitle(title string) {
|
||||||
|
b.session.log.Info("WindowSetTitle unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowShow() {
|
||||||
|
b.session.log.Info("WindowShow unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowHide() {
|
||||||
|
b.session.log.Info("WindowHide unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowCenter() {
|
||||||
|
b.session.log.Info("WindowCenter unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowMaximise() {
|
||||||
|
b.session.log.Info("WindowMaximise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowUnmaximise() {
|
||||||
|
b.session.log.Info("WindowUnmaximise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowMinimise() {
|
||||||
|
b.session.log.Info("WindowMinimise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowUnminimise() {
|
||||||
|
b.session.log.Info("WindowUnminimise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowPosition(x int, y int) {
|
||||||
|
b.session.log.Info("WindowPosition unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSize(width int, height int) {
|
||||||
|
b.session.log.Info("WindowSize unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetMinSize(width int, height int) {
|
||||||
|
b.session.log.Info("WindowSetMinSize unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetMaxSize(width int, height int) {
|
||||||
|
b.session.log.Info("WindowSetMaxSize unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowFullscreen() {
|
||||||
|
b.session.log.Info("WindowFullscreen unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowUnFullscreen() {
|
||||||
|
b.session.log.Info("WindowUnFullscreen unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetColour(colour int) {
|
||||||
|
b.session.log.Info("WindowSetColour unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) DarkModeEnabled(callbackID string) {
|
||||||
|
b.session.log.Info("DarkModeEnabled unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) SetApplicationMenu(menuJSON string) {
|
||||||
|
b.session.log.Info("SetApplicationMenu unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) SetTrayMenu(trayMenuJSON string) {
|
||||||
|
b.session.log.Info("SetTrayMenu unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) UpdateTrayMenuLabel(JSON string) {
|
||||||
|
b.session.log.Info("UpdateTrayMenuLabel unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) UpdateContextMenu(contextMenuJSON string) {
|
||||||
|
b.session.log.Info("UpdateContextMenu unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBridgeClient(session *session) *BridgeClient {
|
||||||
|
return &BridgeClient{
|
||||||
|
session: session,
|
||||||
|
}
|
||||||
|
}
|
||||||
1
v2/internal/bridge/darwin.js
Normal file
1
v2/internal/bridge/darwin.js
Normal file
File diff suppressed because one or more lines are too long
146
v2/internal/bridge/session.go
Normal file
146
v2/internal/bridge/session.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed darwin.js
|
||||||
|
var darwinRuntime string
|
||||||
|
|
||||||
|
// session represents a single websocket session
|
||||||
|
type session struct {
|
||||||
|
bindings string
|
||||||
|
conn *websocket.Conn
|
||||||
|
//eventManager interfaces.EventManager
|
||||||
|
log *logger.Logger
|
||||||
|
//ipc interfaces.IPCManager
|
||||||
|
|
||||||
|
// Mutex for writing to the socket
|
||||||
|
shutdown chan bool
|
||||||
|
writeChan chan []byte
|
||||||
|
|
||||||
|
done bool
|
||||||
|
|
||||||
|
// context
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// client
|
||||||
|
client *messagedispatcher.DispatchClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSession(conn *websocket.Conn, bindings string, dispatcher *messagedispatcher.Dispatcher, logger *logger.Logger, ctx context.Context) *session {
|
||||||
|
result := &session{
|
||||||
|
conn: conn,
|
||||||
|
bindings: bindings,
|
||||||
|
log: logger,
|
||||||
|
shutdown: make(chan bool),
|
||||||
|
writeChan: make(chan []byte, 100),
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
result.client = dispatcher.RegisterClient(newBridgeClient(result))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier returns a string identifier for the remote connection.
|
||||||
|
// Taking the form of the client's <ip address>:<port>.
|
||||||
|
func (s *session) Identifier() string {
|
||||||
|
if s.conn != nil {
|
||||||
|
return s.conn.RemoteAddr().String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) sendMessage(msg string) error {
|
||||||
|
if !s.done {
|
||||||
|
s.writeChan <- []byte(msg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) start(firstSession bool) {
|
||||||
|
s.log.SetLogLevel(1)
|
||||||
|
s.log.Info("Connected to frontend.")
|
||||||
|
go s.writePump()
|
||||||
|
|
||||||
|
var wailsRuntime string
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
wailsRuntime = darwinRuntime
|
||||||
|
default:
|
||||||
|
log.Fatal("platform not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingsMessage := "window.wailsbindings = `" + s.bindings + "`;"
|
||||||
|
s.log.Info(bindingsMessage)
|
||||||
|
bootstrapMessage := bindingsMessage + wailsRuntime
|
||||||
|
|
||||||
|
s.sendMessage("b" + bootstrapMessage)
|
||||||
|
|
||||||
|
for {
|
||||||
|
messageType, buffer, err := s.conn.ReadMessage()
|
||||||
|
if messageType == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error("Error reading message: %v", err)
|
||||||
|
err = s.conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
message := string(buffer)
|
||||||
|
|
||||||
|
s.log.Debug("Got message: %#v\n", message)
|
||||||
|
|
||||||
|
// Dispatch message as normal
|
||||||
|
s.client.DispatchMessage(message)
|
||||||
|
|
||||||
|
if s.done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown
|
||||||
|
func (s *session) Shutdown() {
|
||||||
|
s.conn.Close()
|
||||||
|
s.done = true
|
||||||
|
s.log.Info("session %v exit", s.Identifier())
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePump pulls messages from the writeChan and sends them to the client
|
||||||
|
// since it uses a channel to read the messages the socket is protected without locks
|
||||||
|
func (s *session) writePump() {
|
||||||
|
s.log.Debug("Session %v - writePump start", s.Identifier())
|
||||||
|
defer s.log.Debug("Session %v - writePump shutdown", s.Identifier())
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
s.Shutdown()
|
||||||
|
return
|
||||||
|
case msg, ok := <-s.writeChan:
|
||||||
|
s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||||
|
if !ok {
|
||||||
|
s.log.Debug("writeChan was closed!")
|
||||||
|
s.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
|
||||||
|
s.log.Debug(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
v2/internal/deepcopy/LICENSE
Normal file
21
v2/internal/deepcopy/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Joel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
125
v2/internal/deepcopy/deepcopy.go
Normal file
125
v2/internal/deepcopy/deepcopy.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
// deepcopy makes deep copies of things. A standard copy will copy the
|
||||||
|
// pointers: deep copy copies the values pointed to. Unexported field
|
||||||
|
// values are not copied.
|
||||||
|
//
|
||||||
|
// Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved.
|
||||||
|
// License: MIT, for more details check the included LICENSE file.
|
||||||
|
package deepcopy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface for delegating copy process to type
|
||||||
|
type Interface interface {
|
||||||
|
DeepCopy() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iface is an alias to Copy; this exists for backwards compatibility reasons.
|
||||||
|
func Iface(iface interface{}) interface{} {
|
||||||
|
return Copy(iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of whatever is passed to it and returns the copy
|
||||||
|
// in an interface{}. The returned value will need to be asserted to the
|
||||||
|
// correct type.
|
||||||
|
func Copy(src interface{}) interface{} {
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the interface a reflect.Value
|
||||||
|
original := reflect.ValueOf(src)
|
||||||
|
|
||||||
|
// Make a copy of the same type as the original.
|
||||||
|
cpy := reflect.New(original.Type()).Elem()
|
||||||
|
|
||||||
|
// Recursively copy the original.
|
||||||
|
copyRecursive(original, cpy)
|
||||||
|
|
||||||
|
// Return the copy as an interface.
|
||||||
|
return cpy.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyRecursive does the actual copying of the interface. It currently has
|
||||||
|
// limited support for what it can handle. Add as needed.
|
||||||
|
func copyRecursive(original, cpy reflect.Value) {
|
||||||
|
// check for implement deepcopy.Interface
|
||||||
|
if original.CanInterface() {
|
||||||
|
if copier, ok := original.Interface().(Interface); ok {
|
||||||
|
cpy.Set(reflect.ValueOf(copier.DeepCopy()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle according to original's Kind
|
||||||
|
switch original.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Get the actual value being pointed to.
|
||||||
|
originalValue := original.Elem()
|
||||||
|
|
||||||
|
// if it isn't valid, return.
|
||||||
|
if !originalValue.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cpy.Set(reflect.New(originalValue.Type()))
|
||||||
|
copyRecursive(originalValue, cpy.Elem())
|
||||||
|
|
||||||
|
case reflect.Interface:
|
||||||
|
// If this is a nil, don't do anything
|
||||||
|
if original.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get the value for the interface, not the pointer.
|
||||||
|
originalValue := original.Elem()
|
||||||
|
|
||||||
|
// Get the value by calling Elem().
|
||||||
|
copyValue := reflect.New(originalValue.Type()).Elem()
|
||||||
|
copyRecursive(originalValue, copyValue)
|
||||||
|
cpy.Set(copyValue)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
t, ok := original.Interface().(time.Time)
|
||||||
|
if ok {
|
||||||
|
cpy.Set(reflect.ValueOf(t))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Go through each field of the struct and copy it.
|
||||||
|
for i := 0; i < original.NumField(); i++ {
|
||||||
|
// The Type's StructField for a given field is checked to see if StructField.PkgPath
|
||||||
|
// is set to determine if the field is exported or not because CanSet() returns false
|
||||||
|
// for settable fields. I'm not sure why. -mohae
|
||||||
|
if original.Type().Field(i).PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
copyRecursive(original.Field(i), cpy.Field(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if original.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Make a new slice and copy each element.
|
||||||
|
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
|
||||||
|
for i := 0; i < original.Len(); i++ {
|
||||||
|
copyRecursive(original.Index(i), cpy.Index(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
if original.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cpy.Set(reflect.MakeMap(original.Type()))
|
||||||
|
for _, key := range original.MapKeys() {
|
||||||
|
originalValue := original.MapIndex(key)
|
||||||
|
copyValue := reflect.New(originalValue.Type()).Elem()
|
||||||
|
copyRecursive(originalValue, copyValue)
|
||||||
|
copyKey := Copy(key.Interface())
|
||||||
|
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
cpy.Set(original)
|
||||||
|
}
|
||||||
|
}
|
||||||
1110
v2/internal/deepcopy/deepcopy_test.go
Normal file
1110
v2/internal/deepcopy/deepcopy_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
|||||||
package ffenestri
|
package ffenestri
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
@@ -118,7 +119,8 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
||||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
startHidden := a.bool2Cint(a.config.StartHidden)
|
||||||
logLevel := C.int(a.config.LogLevel)
|
logLevel := C.int(a.config.LogLevel)
|
||||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel)
|
hideWindowOnClose := a.bool2Cint(a.config.HideWindowOnClose)
|
||||||
|
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel, hideWindowOnClose)
|
||||||
|
|
||||||
// Save app reference
|
// Save app reference
|
||||||
a.app = (*C.struct_Application)(app)
|
a.app = (*C.struct_Application)(app)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
struct Application;
|
struct Application;
|
||||||
|
|
||||||
extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel);
|
extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose);
|
||||||
extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight);
|
extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight);
|
||||||
extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
||||||
extern void Run(struct Application*, int argc, char **argv);
|
extern void Run(struct Application*, int argc, char **argv);
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ package ffenestri
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -113,6 +114,14 @@ func (c *Client) WindowSize(width int, height int) {
|
|||||||
C.SetSize(c.app.app, C.int(width), C.int(height))
|
C.SetSize(c.app.app, C.int(width), C.int(height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) WindowSetMinSize(width int, height int) {
|
||||||
|
C.SetMinWindowSize(c.app.app, C.int(width), C.int(height))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WindowSetMaxSize(width int, height int) {
|
||||||
|
C.SetMaxWindowSize(c.app.app, C.int(width), C.int(height))
|
||||||
|
}
|
||||||
|
|
||||||
// WindowSetColour sets the window colour
|
// WindowSetColour sets the window colour
|
||||||
func (c *Client) WindowSetColour(colour int) {
|
func (c *Client) WindowSetColour(colour int) {
|
||||||
r, g, b, a := intToColour(colour)
|
r, g, b, a := intToColour(colour)
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ BOOL yes(id self, SEL cmd)
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no command simply returns NO!
|
||||||
|
BOOL no(id self, SEL cmd)
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
// Prints a hashmap entry
|
// Prints a hashmap entry
|
||||||
int hashmap_log(void *const context, struct hashmap_element_s *const e) {
|
int hashmap_log(void *const context, struct hashmap_element_s *const e) {
|
||||||
printf("%s: %p ", (char*)e->key, e->data);
|
printf("%s: %p ", (char*)e->key, e->data);
|
||||||
@@ -65,6 +71,7 @@ struct Application {
|
|||||||
// Cocoa data
|
// Cocoa data
|
||||||
id application;
|
id application;
|
||||||
id delegate;
|
id delegate;
|
||||||
|
id windowDelegate;
|
||||||
id mainWindow;
|
id mainWindow;
|
||||||
id wkwebview;
|
id wkwebview;
|
||||||
id manager;
|
id manager;
|
||||||
@@ -92,6 +99,7 @@ struct Application {
|
|||||||
const char *appearance;
|
const char *appearance;
|
||||||
int decorations;
|
int decorations;
|
||||||
int logLevel;
|
int logLevel;
|
||||||
|
int hideWindowOnClose;
|
||||||
|
|
||||||
// Features
|
// Features
|
||||||
int frame;
|
int frame;
|
||||||
@@ -120,6 +128,9 @@ struct Application {
|
|||||||
// Bindings
|
// Bindings
|
||||||
const char *bindings;
|
const char *bindings;
|
||||||
|
|
||||||
|
// shutting down flag
|
||||||
|
bool shuttingDown;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug works like sprintf but mutes if the global debug flag is true
|
// Debug works like sprintf but mutes if the global debug flag is true
|
||||||
@@ -140,6 +151,16 @@ void Debug(struct Application *app, const char *message, ... ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Error(struct Application *app, const char *message, ... ) {
|
||||||
|
const char *temp = concat("LEFfenestri (C) | ", message);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, message);
|
||||||
|
vsnprintf(logbuffer, MAXMESSAGE, temp, args);
|
||||||
|
app->sendMessageToBackend(&logbuffer[0]);
|
||||||
|
MEMFREE(temp);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
void Fatal(struct Application *app, const char *message, ... ) {
|
void Fatal(struct Application *app, const char *message, ... ) {
|
||||||
const char *temp = concat("LFFfenestri (C) | ", message);
|
const char *temp = concat("LFFfenestri (C) | ", message);
|
||||||
va_list args;
|
va_list args;
|
||||||
@@ -212,6 +233,9 @@ void applyWindowColour(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetColour(struct Application *app, int red, int green, int blue, int alpha) {
|
void SetColour(struct Application *app, int red, int green, int blue, int alpha) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
app->red = red;
|
app->red = red;
|
||||||
app->green = green;
|
app->green = green;
|
||||||
app->blue = blue;
|
app->blue = blue;
|
||||||
@@ -225,12 +249,18 @@ void FullSizeContent(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Hide(struct Application *app) {
|
void Hide(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
msg(app->application, s("hide:"))
|
msg(app->application, s("hide:"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Show(struct Application *app) {
|
void Show(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
msg(app->mainWindow, s("makeKeyAndOrderFront:"), NULL);
|
msg(app->mainWindow, s("makeKeyAndOrderFront:"), NULL);
|
||||||
msg(app->application, s("activateIgnoringOtherApps:"), YES);
|
msg(app->application, s("activateIgnoringOtherApps:"), YES);
|
||||||
@@ -246,7 +276,10 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
|
|||||||
struct Application *app = (struct Application *)objc_getAssociatedObject(
|
struct Application *app = (struct Application *)objc_getAssociatedObject(
|
||||||
self, "application");
|
self, "application");
|
||||||
const char *name = (const char *)msg(msg(message, s("name")), s("UTF8String"));
|
const char *name = (const char *)msg(msg(message, s("name")), s("UTF8String"));
|
||||||
if( strcmp(name, "completed") == 0) {
|
if( strcmp(name, "error") == 0 ) {
|
||||||
|
printf("There was a Javascript error. Please open the devtools for more information.\n");
|
||||||
|
Show(app);
|
||||||
|
} else if( strcmp(name, "completed") == 0) {
|
||||||
// Delete handler
|
// Delete handler
|
||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("completed"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("completed"));
|
||||||
|
|
||||||
@@ -409,6 +442,7 @@ void freeDialogIconCache(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DestroyApplication(struct Application *app) {
|
void DestroyApplication(struct Application *app) {
|
||||||
|
app->shuttingDown = true;
|
||||||
Debug(app, "Destroying Application");
|
Debug(app, "Destroying Application");
|
||||||
|
|
||||||
// Free the bindings
|
// Free the bindings
|
||||||
@@ -450,12 +484,17 @@ void DestroyApplication(struct Application *app) {
|
|||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("contextMenu"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("contextMenu"));
|
||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("windowDrag"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("windowDrag"));
|
||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("external"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("external"));
|
||||||
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("error"));
|
||||||
|
|
||||||
// Close main window
|
// Close main window
|
||||||
msg(app->mainWindow, s("close"));
|
if( app->windowDelegate != NULL ) {
|
||||||
|
msg(app->windowDelegate, s("release"));
|
||||||
|
msg(app->mainWindow, s("setDelegate:"), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// msg(app->mainWindow, s("close"));
|
||||||
|
|
||||||
|
|
||||||
// Terminate app
|
|
||||||
msg(c("NSApp"), s("terminate:"), NULL);
|
|
||||||
Debug(app, "Finished Destroying Application");
|
Debug(app, "Finished Destroying Application");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,11 +502,32 @@ void DestroyApplication(struct Application *app) {
|
|||||||
// used by the application
|
// used by the application
|
||||||
void Quit(struct Application *app) {
|
void Quit(struct Application *app) {
|
||||||
Debug(app, "Quit Called");
|
Debug(app, "Quit Called");
|
||||||
DestroyApplication(app);
|
ON_MAIN_THREAD (
|
||||||
|
// Terminate app
|
||||||
|
msg(app->application, s("stop:"), NULL);
|
||||||
|
id fakeevent = msg(c("NSEvent"),
|
||||||
|
s("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"),
|
||||||
|
15, // Type
|
||||||
|
msg(c("CGPoint"), s("init:x:y:"), 0, 0), // location
|
||||||
|
0, // flags
|
||||||
|
0, // timestamp
|
||||||
|
0, // window
|
||||||
|
NULL, // context
|
||||||
|
0, // subtype
|
||||||
|
0, // data1
|
||||||
|
0 // data2
|
||||||
|
);
|
||||||
|
msg(c("NSApp"), s("postEvent:atStart:"), fakeevent, true);
|
||||||
|
// msg(c(app->mainWindow), s("performClose:"))
|
||||||
|
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTitle sets the main window title to the given string
|
// SetTitle sets the main window title to the given string
|
||||||
void SetTitle(struct Application *app, const char *title) {
|
void SetTitle(struct Application *app, const char *title) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "SetTitle Called");
|
Debug(app, "SetTitle Called");
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
msg(app->mainWindow, s("setTitle:"), str(title));
|
msg(app->mainWindow, s("setTitle:"), str(title));
|
||||||
@@ -489,6 +549,9 @@ bool isFullScreen(struct Application *app) {
|
|||||||
|
|
||||||
// Fullscreen sets the main window to be fullscreen
|
// Fullscreen sets the main window to be fullscreen
|
||||||
void Fullscreen(struct Application *app) {
|
void Fullscreen(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "Fullscreen Called");
|
Debug(app, "Fullscreen Called");
|
||||||
if( ! isFullScreen(app) ) {
|
if( ! isFullScreen(app) ) {
|
||||||
ToggleFullscreen(app);
|
ToggleFullscreen(app);
|
||||||
@@ -497,6 +560,9 @@ void Fullscreen(struct Application *app) {
|
|||||||
|
|
||||||
// UnFullscreen resets the main window after a fullscreen
|
// UnFullscreen resets the main window after a fullscreen
|
||||||
void UnFullscreen(struct Application *app) {
|
void UnFullscreen(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "UnFullscreen Called");
|
Debug(app, "UnFullscreen Called");
|
||||||
if( isFullScreen(app) ) {
|
if( isFullScreen(app) ) {
|
||||||
ToggleFullscreen(app);
|
ToggleFullscreen(app);
|
||||||
@@ -504,6 +570,9 @@ void UnFullscreen(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Center(struct Application *app) {
|
void Center(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "Center Called");
|
Debug(app, "Center Called");
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
MAIN_WINDOW_CALL("center");
|
MAIN_WINDOW_CALL("center");
|
||||||
@@ -518,23 +587,35 @@ void ToggleMaximise(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Maximise(struct Application *app) {
|
void Maximise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if( app->maximised == 0) {
|
if( app->maximised == 0) {
|
||||||
ToggleMaximise(app);
|
ToggleMaximise(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unmaximise(struct Application *app) {
|
void Unmaximise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if( app->maximised == 1) {
|
if( app->maximised == 1) {
|
||||||
ToggleMaximise(app);
|
ToggleMaximise(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Minimise(struct Application *app) {
|
void Minimise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
MAIN_WINDOW_CALL("miniaturize:");
|
MAIN_WINDOW_CALL("miniaturize:");
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
void Unminimise(struct Application *app) {
|
void Unminimise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
MAIN_WINDOW_CALL("deminiaturize:");
|
MAIN_WINDOW_CALL("deminiaturize:");
|
||||||
);
|
);
|
||||||
@@ -558,6 +639,9 @@ void dumpFrame(struct Application *app, const char *message, CGRect frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetSize(struct Application *app, int width, int height) {
|
void SetSize(struct Application *app, int width, int height) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
id screen = getCurrentScreen(app);
|
id screen = getCurrentScreen(app);
|
||||||
|
|
||||||
@@ -574,6 +658,9 @@ void SetSize(struct Application *app, int width, int height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetPosition(struct Application *app, int x, int y) {
|
void SetPosition(struct Application *app, int x, int y) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
id screen = getCurrentScreen(app);
|
id screen = getCurrentScreen(app);
|
||||||
CGRect screenFrame = GET_FRAME(screen);
|
CGRect screenFrame = GET_FRAME(screen);
|
||||||
@@ -599,6 +686,9 @@ void processDialogButton(id alert, char *buttonTitle, char *cancelButton, char *
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern void MessageDialog(struct Application *app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
extern void MessageDialog(struct Application *app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
id alert = ALLOC_INIT("NSAlert");
|
id alert = ALLOC_INIT("NSAlert");
|
||||||
char *dialogType = type;
|
char *dialogType = type;
|
||||||
@@ -695,7 +785,7 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
|
|||||||
buttonPressed = button4;
|
buttonPressed = button4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct callback message. Format "DS<callbackID>|<selected button index>"
|
// Construct callback message. Format "DM<callbackID>|<selected button index>"
|
||||||
const char *callback = concat("DM", callbackID);
|
const char *callback = concat("DM", callbackID);
|
||||||
const char *header = concat(callback, "|");
|
const char *header = concat(callback, "|");
|
||||||
const char *responseMessage = concat(header, buttonPressed);
|
const char *responseMessage = concat(header, buttonPressed);
|
||||||
@@ -712,6 +802,9 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
|
|||||||
|
|
||||||
// OpenDialog opens a dialog to select files/directories
|
// OpenDialog opens a dialog to select files/directories
|
||||||
void OpenDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) {
|
void OpenDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "OpenDialog Called with callback id: %s", callbackID);
|
Debug(app, "OpenDialog Called with callback id: %s", callbackID);
|
||||||
|
|
||||||
// Create an open panel
|
// Create an open panel
|
||||||
@@ -800,6 +893,9 @@ void OpenDialog(struct Application *app, char *callbackID, char *title, char *fi
|
|||||||
|
|
||||||
// SaveDialog opens a dialog to select files/directories
|
// SaveDialog opens a dialog to select files/directories
|
||||||
void SaveDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {
|
void SaveDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "SaveDialog Called with callback id: %s", callbackID);
|
Debug(app, "SaveDialog Called with callback id: %s", callbackID);
|
||||||
|
|
||||||
// Create an open panel
|
// Create an open panel
|
||||||
@@ -877,6 +973,9 @@ void DisableFrame(struct Application *app)
|
|||||||
|
|
||||||
void setMinMaxSize(struct Application *app)
|
void setMinMaxSize(struct Application *app)
|
||||||
{
|
{
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if (app->maxHeight > 0 && app->maxWidth > 0)
|
if (app->maxHeight > 0 && app->maxWidth > 0)
|
||||||
{
|
{
|
||||||
msg(app->mainWindow, s("setMaxSize:"), CGSizeMake(app->maxWidth, app->maxHeight));
|
msg(app->mainWindow, s("setMaxSize:"), CGSizeMake(app->maxWidth, app->maxHeight));
|
||||||
@@ -885,10 +984,27 @@ void setMinMaxSize(struct Application *app)
|
|||||||
{
|
{
|
||||||
msg(app->mainWindow, s("setMinSize:"), CGSizeMake(app->minWidth, app->minHeight));
|
msg(app->mainWindow, s("setMinSize:"), CGSizeMake(app->minWidth, app->minHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate if window needs resizing
|
||||||
|
int newWidth = app->width;
|
||||||
|
int newHeight = app->height;
|
||||||
|
|
||||||
|
if (app->maxWidth > 0 && app->width > app->maxWidth) newWidth = app->maxWidth;
|
||||||
|
if (app->minWidth > 0 && app->width < app->minWidth) newWidth = app->minWidth;
|
||||||
|
if (app->maxHeight > 0 && app->height > app->maxHeight ) newHeight = app->maxHeight;
|
||||||
|
if (app->minHeight > 0 && app->height < app->minHeight ) newHeight = app->minHeight;
|
||||||
|
|
||||||
|
// If we have any change, resize window
|
||||||
|
if ( newWidth != app->width || newHeight != app->height ) {
|
||||||
|
SetSize(app, newWidth, newHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
||||||
{
|
{
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
app->minWidth = minWidth;
|
app->minWidth = minWidth;
|
||||||
app->minHeight = minHeight;
|
app->minHeight = minHeight;
|
||||||
|
|
||||||
@@ -902,6 +1018,9 @@ void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
|||||||
|
|
||||||
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
||||||
{
|
{
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
app->maxWidth = maxWidth;
|
app->maxWidth = maxWidth;
|
||||||
app->maxHeight = maxHeight;
|
app->maxHeight = maxHeight;
|
||||||
|
|
||||||
@@ -922,24 +1041,40 @@ void SetDebug(void *applicationPointer, int flag) {
|
|||||||
|
|
||||||
// AddContextMenu sets the context menu map for this application
|
// AddContextMenu sets the context menu map for this application
|
||||||
void AddContextMenu(struct Application *app, const char *contextMenuJSON) {
|
void AddContextMenu(struct Application *app, const char *contextMenuJSON) {
|
||||||
AddContextMenuToStore(app->contextMenuStore, contextMenuJSON);
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
|
AddContextMenuToStore(app->contextMenuStore, contextMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateContextMenu(struct Application *app, const char* contextMenuJSON) {
|
void UpdateContextMenu(struct Application *app, const char* contextMenuJSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON);
|
UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
AddTrayMenuToStore(app->trayMenuStore, trayMenuJSON);
|
AddTrayMenuToStore(app->trayMenuStore, trayMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
||||||
|
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuInStore(app->trayMenuStore, trayMenuJSON);
|
UpdateTrayMenuInStore(app->trayMenuStore, trayMenuJSON);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON);
|
UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON);
|
||||||
);
|
);
|
||||||
@@ -1006,6 +1141,9 @@ void createApplication(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
const char *result = isDarkMode(app) ? "T" : "F";
|
const char *result = isDarkMode(app) ? "T" : "F";
|
||||||
|
|
||||||
@@ -1026,9 +1164,9 @@ void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
|||||||
void createDelegate(struct Application *app) {
|
void createDelegate(struct Application *app) {
|
||||||
// Define delegate
|
// Define delegate
|
||||||
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
||||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
||||||
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) yes, "c@:@");
|
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
||||||
class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
// class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
||||||
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
||||||
|
|
||||||
// All Menu Items use a common callback
|
// All Menu Items use a common callback
|
||||||
@@ -1054,6 +1192,11 @@ void createDelegate(struct Application *app) {
|
|||||||
msg(app->application, s("setDelegate:"), delegate);
|
msg(app->application, s("setDelegate:"), delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool windowShouldClose(id self, SEL cmd, id sender) {
|
||||||
|
msg(sender, s("orderBack:"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void createMainWindow(struct Application *app) {
|
void createMainWindow(struct Application *app) {
|
||||||
// Create main window
|
// Create main window
|
||||||
id mainWindow = ALLOC("NSWindow");
|
id mainWindow = ALLOC("NSWindow");
|
||||||
@@ -1072,6 +1215,15 @@ void createMainWindow(struct Application *app) {
|
|||||||
msg(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO);
|
msg(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO);
|
||||||
msg(mainWindow, s("setTitleVisibility:"), app->hideTitle);
|
msg(mainWindow, s("setTitleVisibility:"), app->hideTitle);
|
||||||
|
|
||||||
|
if( app->hideWindowOnClose ) {
|
||||||
|
// Create window delegate to override windowShouldClose
|
||||||
|
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "WindowDelegate", 0);
|
||||||
|
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSWindowDelegate"));
|
||||||
|
class_replaceMethod(delegateClass, s("windowShouldClose:"), (IMP) windowShouldClose, "v@:@");
|
||||||
|
app->windowDelegate = msg((id)delegateClass, s("new"));
|
||||||
|
msg(mainWindow, s("setDelegate:"), app->windowDelegate);
|
||||||
|
}
|
||||||
|
|
||||||
app->mainWindow = mainWindow;
|
app->mainWindow = mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1158,7 +1310,7 @@ void parseMenuRole(struct Application *app, id parentMenu, JsonNode *item) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( STREQ(roleName, "quit")) {
|
if ( STREQ(roleName, "quit")) {
|
||||||
addMenuItem(parentMenu, "Quit (More work TBD)", "terminate:", "q", FALSE);
|
addMenuItem(parentMenu, "Quit", "terminate:", "q", FALSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( STREQ(roleName, "togglefullscreen")) {
|
if ( STREQ(roleName, "togglefullscreen")) {
|
||||||
@@ -1436,6 +1588,9 @@ void updateMenu(struct Application *app, const char *menuAsJSON) {
|
|||||||
|
|
||||||
// SetApplicationMenu sets the initial menu for the application
|
// SetApplicationMenu sets the initial menu for the application
|
||||||
void SetApplicationMenu(struct Application *app, const char *menuAsJSON) {
|
void SetApplicationMenu(struct Application *app, const char *menuAsJSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if ( app->applicationMenu == NULL ) {
|
if ( app->applicationMenu == NULL ) {
|
||||||
app->applicationMenu = NewApplicationMenu(menuAsJSON);
|
app->applicationMenu = NewApplicationMenu(menuAsJSON);
|
||||||
return;
|
return;
|
||||||
@@ -1534,6 +1689,7 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
id manager = msg(config, s("userContentController"));
|
id manager = msg(config, s("userContentController"));
|
||||||
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("external"));
|
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("external"));
|
||||||
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("completed"));
|
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("completed"));
|
||||||
|
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("error"));
|
||||||
app->manager = manager;
|
app->manager = manager;
|
||||||
|
|
||||||
id wkwebview = msg(c("WKWebView"), s("alloc"));
|
id wkwebview = msg(c("WKWebView"), s("alloc"));
|
||||||
@@ -1676,11 +1832,13 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
Debug(app, "Run called");
|
Debug(app, "Run called");
|
||||||
msg(app->application, s("run"));
|
msg(app->application, s("run"));
|
||||||
|
|
||||||
|
DestroyApplication(app);
|
||||||
|
|
||||||
MEMFREE(internalCode);
|
MEMFREE(internalCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel) {
|
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
||||||
|
|
||||||
// Load the tray icons
|
// Load the tray icons
|
||||||
LoadTrayIcons();
|
LoadTrayIcons();
|
||||||
@@ -1701,6 +1859,7 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
result->startHidden = startHidden;
|
result->startHidden = startHidden;
|
||||||
result->decorations = 0;
|
result->decorations = 0;
|
||||||
result->logLevel = logLevel;
|
result->logLevel = logLevel;
|
||||||
|
result->hideWindowOnClose = hideWindowOnClose;
|
||||||
|
|
||||||
result->mainWindow = NULL;
|
result->mainWindow = NULL;
|
||||||
result->mouseEvent = NULL;
|
result->mouseEvent = NULL;
|
||||||
@@ -1729,12 +1888,17 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
// Context Menus
|
// Context Menus
|
||||||
result->contextMenuStore = NewContextMenuStore();
|
result->contextMenuStore = NewContextMenuStore();
|
||||||
|
|
||||||
|
// Window delegate
|
||||||
|
result->windowDelegate = NULL;
|
||||||
|
|
||||||
// Window Appearance
|
// Window Appearance
|
||||||
result->titlebarAppearsTransparent = 0;
|
result->titlebarAppearsTransparent = 0;
|
||||||
result->webviewIsTranparent = 0;
|
result->webviewIsTranparent = 0;
|
||||||
|
|
||||||
result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback;
|
result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback;
|
||||||
|
|
||||||
|
result->shuttingDown = false;
|
||||||
|
|
||||||
return (void*) result;
|
return (void*) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -740,7 +740,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
const char *image = getJSONString(item, "Image");
|
const char *image = getJSONString(item, "Image");
|
||||||
const char *fontName = getJSONString(item, "FontName");
|
const char *fontName = getJSONString(item, "FontName");
|
||||||
const char *RGBA = getJSONString(item, "RGBA");
|
const char *RGBA = getJSONString(item, "RGBA");
|
||||||
int fontSize = 0;
|
int fontSize = 12;
|
||||||
getJSONInt(item, "FontSize", &fontSize);
|
getJSONInt(item, "FontSize", &fontSize);
|
||||||
|
|
||||||
// If we have an accelerator
|
// If we have an accelerator
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ typedef struct _NSRange {
|
|||||||
#define NSFontWeightUltraLight -0.8
|
#define NSFontWeightUltraLight -0.8
|
||||||
#define NSFontWeightThin -0.6
|
#define NSFontWeightThin -0.6
|
||||||
#define NSFontWeightLight -0.4
|
#define NSFontWeightLight -0.4
|
||||||
#define NSFontWeightRegular 0
|
#define NSFontWeightRegular 0.0
|
||||||
#define NSFontWeightMedium 0.23
|
#define NSFontWeightMedium 0.23
|
||||||
#define NSFontWeightSemibold 0.3
|
#define NSFontWeightSemibold 0.3
|
||||||
#define NSFontWeightBold 0.4
|
#define NSFontWeightBold 0.4
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
103
v2/internal/github/github.go
Normal file
103
v2/internal/github/github.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package github
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetVersionTags gets the list of tags on the Wails repo
|
||||||
|
// It returns a list of sorted tags in descending order
|
||||||
|
func GetVersionTags() ([]*SemanticVersion, error) {
|
||||||
|
|
||||||
|
result := []*SemanticVersion{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags")
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []map[string]interface{}{}
|
||||||
|
err = json.Unmarshal(body, &data)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert tag data to Version structs
|
||||||
|
for _, tag := range data {
|
||||||
|
version := tag["name"].(string)
|
||||||
|
if !strings.HasPrefix(version, "v2") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
semver, err := NewSemanticVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result = append(result, semver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse Sort
|
||||||
|
sort.Sort(sort.Reverse(SemverCollection(result)))
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestStableRelease gets the latest stable release on GitHub
|
||||||
|
func GetLatestStableRelease() (result *SemanticVersion, err error) {
|
||||||
|
|
||||||
|
tags, err := GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.IsRelease() {
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no release tag found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestPreRelease gets the latest prerelease on GitHub
|
||||||
|
func GetLatestPreRelease() (result *SemanticVersion, err error) {
|
||||||
|
|
||||||
|
tags, err := GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.IsPreRelease() {
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no prerelease tag found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidTag returns true if the given string is a valid tag
|
||||||
|
func IsValidTag(tagVersion string) (bool, error) {
|
||||||
|
if tagVersion[0] == 'v' {
|
||||||
|
tagVersion = tagVersion[1:]
|
||||||
|
}
|
||||||
|
tags, err := GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.String() == tagVersion {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
106
v2/internal/github/semver.go
Normal file
106
v2/internal/github/semver.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package github
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SemanticVersion is a struct containing a semantic version
|
||||||
|
type SemanticVersion struct {
|
||||||
|
Version *semver.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSemanticVersion creates a new SemanticVersion object with the given version string
|
||||||
|
func NewSemanticVersion(version string) (*SemanticVersion, error) {
|
||||||
|
semverVersion, err := semver.NewVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SemanticVersion{
|
||||||
|
Version: semverVersion,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRelease returns true if it's a release version
|
||||||
|
func (s *SemanticVersion) IsRelease() bool {
|
||||||
|
// Limit to v2
|
||||||
|
if s.Version.Major() != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPreRelease returns true if it's a prerelease version
|
||||||
|
func (s *SemanticVersion) IsPreRelease() bool {
|
||||||
|
// Limit to v1
|
||||||
|
if s.Version.Major() != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(s.Version.Prerelease()) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SemanticVersion) String() string {
|
||||||
|
return s.Version.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGreaterThan returns true if this version is greater than the given version
|
||||||
|
func (s *SemanticVersion) IsGreaterThan(version *SemanticVersion) (bool, error) {
|
||||||
|
// Set up new constraint
|
||||||
|
constraint, err := semver.NewConstraint("> " + version.Version.String())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the desired one is greater than the requested on
|
||||||
|
success, msgs := constraint.Validate(s.Version)
|
||||||
|
if !success {
|
||||||
|
return false, msgs[0]
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGreaterThanOrEqual returns true if this version is greater than or equal the given version
|
||||||
|
func (s *SemanticVersion) IsGreaterThanOrEqual(version *SemanticVersion) (bool, error) {
|
||||||
|
// Set up new constraint
|
||||||
|
constraint, err := semver.NewConstraint(">= " + version.Version.String())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the desired one is greater than the requested on
|
||||||
|
success, msgs := constraint.Validate(s.Version)
|
||||||
|
if !success {
|
||||||
|
return false, msgs[0]
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MainVersion returns the main version of any version+prerelease+metadata
|
||||||
|
// EG: MainVersion("1.2.3-pre") => "1.2.3"
|
||||||
|
func (s *SemanticVersion) MainVersion() *SemanticVersion {
|
||||||
|
mainVersion := fmt.Sprintf("%d.%d.%d", s.Version.Major(), s.Version.Minor(), s.Version.Patch())
|
||||||
|
result, _ := NewSemanticVersion(mainVersion)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemverCollection is a collection of SemanticVersion objects
|
||||||
|
type SemverCollection []*SemanticVersion
|
||||||
|
|
||||||
|
// Len returns the length of a collection. The number of Version instances
|
||||||
|
// on the slice.
|
||||||
|
func (c SemverCollection) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is needed for the sort interface to compare two Version objects on the
|
||||||
|
// slice. If checks if one is less than the other.
|
||||||
|
func (c SemverCollection) Less(i, j int) bool {
|
||||||
|
return c[i].Version.LessThan(c[j].Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is needed for the sort interface to replace the Version objects
|
||||||
|
// at two different positions in the slice.
|
||||||
|
func (c SemverCollection) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package messagedispatcher
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
@@ -26,6 +27,8 @@ type Client interface {
|
|||||||
WindowUnminimise()
|
WindowUnminimise()
|
||||||
WindowPosition(x int, y int)
|
WindowPosition(x int, y int)
|
||||||
WindowSize(width int, height int)
|
WindowSize(width int, height int)
|
||||||
|
WindowSetMinSize(width int, height int)
|
||||||
|
WindowSetMaxSize(width int, height int)
|
||||||
WindowFullscreen()
|
WindowFullscreen()
|
||||||
WindowUnFullscreen()
|
WindowUnFullscreen()
|
||||||
WindowSetColour(colour int)
|
WindowSetColour(colour int)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func dialogMessageParser(message string) (*parsedMessage, error) {
|
|||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
return nil, fmt.Errorf("Invalid dialog response message format: %+v", message)
|
return nil, fmt.Errorf("Invalid dialog response message format: %+v", message)
|
||||||
}
|
}
|
||||||
callbackID := message[:idx+1]
|
callbackID := message[:idx]
|
||||||
payloadData := message[idx+1:]
|
payloadData := message[idx+1:]
|
||||||
|
|
||||||
switch dialogType {
|
switch dialogType {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package messagedispatcher
|
package messagedispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/crypto"
|
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
@@ -23,7 +25,6 @@ type Dispatcher struct {
|
|||||||
dialogChannel <-chan *servicebus.Message
|
dialogChannel <-chan *servicebus.Message
|
||||||
systemChannel <-chan *servicebus.Message
|
systemChannel <-chan *servicebus.Message
|
||||||
menuChannel <-chan *servicebus.Message
|
menuChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -31,6 +32,13 @@ type Dispatcher struct {
|
|||||||
// Clients
|
// Clients
|
||||||
clients map[string]*DispatchClient
|
clients map[string]*DispatchClient
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
||||||
|
// Context for cancellation
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// internal wait group
|
||||||
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// New dispatcher. Needs a service bus to send to.
|
// New dispatcher. Needs a service bus to send to.
|
||||||
@@ -75,6 +83,9 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create context
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
result := &Dispatcher{
|
result := &Dispatcher{
|
||||||
servicebus: servicebus,
|
servicebus: servicebus,
|
||||||
eventChannel: eventChannel,
|
eventChannel: eventChannel,
|
||||||
@@ -86,6 +97,8 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
dialogChannel: dialogChannel,
|
dialogChannel: dialogChannel,
|
||||||
systemChannel: systemChannel,
|
systemChannel: systemChannel,
|
||||||
menuChannel: menuChannel,
|
menuChannel: menuChannel,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -96,15 +109,18 @@ func (d *Dispatcher) Start() error {
|
|||||||
|
|
||||||
d.logger.Trace("Starting")
|
d.logger.Trace("Starting")
|
||||||
|
|
||||||
d.running = true
|
d.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for d.running {
|
defer d.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-d.ctx.Done():
|
||||||
|
d.wg.Done()
|
||||||
|
return
|
||||||
case <-d.quitChannel:
|
case <-d.quitChannel:
|
||||||
d.processQuit()
|
d.processQuit()
|
||||||
d.running = false
|
|
||||||
case resultMessage := <-d.resultChannel:
|
case resultMessage := <-d.resultChannel:
|
||||||
d.processCallResult(resultMessage)
|
d.processCallResult(resultMessage)
|
||||||
case eventMessage := <-d.eventChannel:
|
case eventMessage := <-d.eventChannel:
|
||||||
@@ -119,9 +135,6 @@ func (d *Dispatcher) Start() error {
|
|||||||
d.processMenuMessage(menuMessage)
|
d.processMenuMessage(menuMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
d.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -135,10 +148,6 @@ func (d *Dispatcher) processQuit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatcher) shutdown() {
|
|
||||||
d.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterClient will register the given callback with the dispatcher
|
// RegisterClient will register the given callback with the dispatcher
|
||||||
// and return a DispatchClient that the caller can use to send messages
|
// and return a DispatchClient that the caller can use to send messages
|
||||||
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
||||||
@@ -349,6 +358,38 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.WindowSize(w, h)
|
client.frontend.WindowSize(w, h)
|
||||||
}
|
}
|
||||||
|
case "minsize":
|
||||||
|
// We need 2 arguments
|
||||||
|
if len(splitTopic) != 4 {
|
||||||
|
d.logger.Error("Invalid number of parameters for 'window:minsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w, err1 := strconv.Atoi(splitTopic[2])
|
||||||
|
h, err2 := strconv.Atoi(splitTopic[3])
|
||||||
|
if err1 != nil || err2 != nil {
|
||||||
|
d.logger.Error("Invalid integer parameters for 'window:minsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Notifh clients
|
||||||
|
for _, client := range d.clients {
|
||||||
|
client.frontend.WindowSetMinSize(w, h)
|
||||||
|
}
|
||||||
|
case "maxsize":
|
||||||
|
// We need 2 arguments
|
||||||
|
if len(splitTopic) != 4 {
|
||||||
|
d.logger.Error("Invalid number of parameters for 'window:maxsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w, err1 := strconv.Atoi(splitTopic[2])
|
||||||
|
h, err2 := strconv.Atoi(splitTopic[3])
|
||||||
|
if err1 != nil || err2 != nil {
|
||||||
|
d.logger.Error("Invalid integer parameters for 'window:maxsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Notifh clients
|
||||||
|
for _, client := range d.clients {
|
||||||
|
client.frontend.WindowSetMaxSize(w, h)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
d.logger.Error("Unknown window command: %s", command)
|
d.logger.Error("Unknown window command: %s", command)
|
||||||
}
|
}
|
||||||
@@ -491,3 +532,8 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
|||||||
d.logger.Error("Unknown menufrontend command: %s", command)
|
d.logger.Error("Unknown menufrontend command: %s", command)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) Close() {
|
||||||
|
d.cancel()
|
||||||
|
d.wg.Wait()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
@@ -16,11 +17,14 @@ type Process struct {
|
|||||||
|
|
||||||
// NewProcess creates a new process struct
|
// NewProcess creates a new process struct
|
||||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||||
return &Process{
|
result := &Process{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
cmd: exec.Command(cmd, args...),
|
cmd: exec.Command(cmd, args...),
|
||||||
exitChannel: make(chan bool, 1),
|
exitChannel: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
|
result.cmd.Stdout = os.Stdout
|
||||||
|
result.cmd.Stderr = os.Stderr
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the process
|
// Start the process
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -65,7 +65,7 @@ export function SetBindings(bindingsMap) {
|
|||||||
window.backend[packageName][structName][methodName] = function () {
|
window.backend[packageName][structName][methodName] = function () {
|
||||||
|
|
||||||
// No timeout by default
|
// No timeout by default
|
||||||
var timeout = 0;
|
let timeout = 0;
|
||||||
|
|
||||||
// Actual function
|
// Actual function
|
||||||
function dynamic() {
|
function dynamic() {
|
||||||
@@ -89,19 +89,3 @@ export function SetBindings(bindingsMap) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// /**
|
|
||||||
// * Determines if the given identifier is valid Javascript
|
|
||||||
// *
|
|
||||||
// * @param {boolean} name
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// function isValidIdentifier(name) {
|
|
||||||
// // Don't xss yourself :-)
|
|
||||||
// try {
|
|
||||||
// new Function('var ' + name);
|
|
||||||
// return true;
|
|
||||||
// } catch (e) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
import { SetBindings } from './bindings';
|
import { SetBindings } from './bindings';
|
||||||
import { Init } from './main';
|
import { Init } from './main';
|
||||||
|
import {RaiseError} from '../desktop/darwin';
|
||||||
|
|
||||||
// Setup global error handler
|
// Setup global error handler
|
||||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||||
@@ -21,7 +22,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
|
|||||||
error: JSON.stringify(error),
|
error: JSON.stringify(error),
|
||||||
stack: function() { return JSON.stringify(new Error().stack); }(),
|
stack: function() { return JSON.stringify(new Error().stack); }(),
|
||||||
};
|
};
|
||||||
window.wails.Log.Error(JSON.stringify(errorMessage));
|
RaiseError(errorMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialise the Runtime
|
// Initialise the Runtime
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Error } from './log';
|
|||||||
import { SendMessage } from 'ipc';
|
import { SendMessage } from 'ipc';
|
||||||
|
|
||||||
// Defines a single listener with a maximum number of times to callback
|
// Defines a single listener with a maximum number of times to callback
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Listener class defines a listener! :-)
|
* The Listener class defines a listener! :-)
|
||||||
*
|
*
|
||||||
@@ -43,7 +44,7 @@ class Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventListeners = {};
|
let eventListeners = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
@@ -96,7 +97,7 @@ export function Once(eventName, callback) {
|
|||||||
function notifyListeners(eventData) {
|
function notifyListeners(eventData) {
|
||||||
|
|
||||||
// Get the event name
|
// Get the event name
|
||||||
var eventName = eventData.name;
|
let eventName = eventData.name;
|
||||||
|
|
||||||
// Check if we have any listeners for this event
|
// Check if we have any listeners for this event
|
||||||
if (eventListeners[eventName]) {
|
if (eventListeners[eventName]) {
|
||||||
@@ -110,7 +111,7 @@ function notifyListeners(eventData) {
|
|||||||
// Get next listener
|
// Get next listener
|
||||||
const listener = eventListeners[eventName][count];
|
const listener = eventListeners[eventName][count];
|
||||||
|
|
||||||
var data = eventData.data;
|
let data = eventData.data;
|
||||||
|
|
||||||
// Do the callback
|
// Do the callback
|
||||||
const destroy = listener.Callback(data);
|
const destroy = listener.Callback(data);
|
||||||
@@ -120,7 +121,7 @@ function notifyListeners(eventData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update callbacks with new list of listners
|
// Update callbacks with new list of listeners
|
||||||
eventListeners[eventName] = newEventListenerList;
|
eventListeners[eventName] = newEventListenerList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,4 @@ export function Init() {
|
|||||||
|
|
||||||
// Do platform specific Init
|
// Do platform specific Init
|
||||||
Platform.Init();
|
Platform.Init();
|
||||||
|
|
||||||
window.wailsloader.runtime = true;
|
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,10 @@ export function SendMessage(message) {
|
|||||||
window.webkit.messageHandlers.external.postMessage(message);
|
window.webkit.messageHandlers.external.postMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function RaiseError(message) {
|
||||||
|
window.webkit.messageHandlers.error.postMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
export function Init() {
|
export function Init() {
|
||||||
|
|
||||||
// Setup drag handler
|
// Setup drag handler
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function Init() {
|
|||||||
// Setup drag handler
|
// Setup drag handler
|
||||||
// Based on code from: https://github.com/patr0nus/DeskGap
|
// Based on code from: https://github.com/patr0nus/DeskGap
|
||||||
window.addEventListener('mousedown', function (e) {
|
window.addEventListener('mousedown', function (e) {
|
||||||
var currentElement = e.target;
|
let currentElement = e.target;
|
||||||
while (currentElement != null) {
|
while (currentElement != null) {
|
||||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
bridge.js
|
|
||||||
239
v2/internal/runtime/js/runtime/bridge.js
Normal file
239
v2/internal/runtime/js/runtime/bridge.js
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
|
||||||
|
// Bridge object
|
||||||
|
window.wailsbridge = {
|
||||||
|
reconnectOverlay: null,
|
||||||
|
reconnectTimer: 300,
|
||||||
|
wsURL: 'ws://' + window.location.hostname + ':34115/bridge',
|
||||||
|
connectionState: null,
|
||||||
|
config: {},
|
||||||
|
websocket: null,
|
||||||
|
callback: null,
|
||||||
|
overlayHTML:
|
||||||
|
'<div class="wails-reconnect-overlay"><div class="wails-reconnect-overlay-content"><div class="wails-reconnect-overlay-loadingspinner"></div></div></div>',
|
||||||
|
overlayCSS:
|
||||||
|
'.wails-reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;backdrop-filter: blur(20px) saturate(160%) contrast(45%) brightness(140%);display:none;z-index:999999}.wails-reconnect-overlay-content{position:relative;top:50%;transform:translateY(-50%);margin: 0;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAflBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAAAAABAQEEBAQAAAAAAAAEBAQAAAADAwMAAAABAQEAAAAAAAAAAAAAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWCC3waAAAAKXRSTlMALgUMIBk0+xEqJs70Xhb3lu3EjX2EZTlv5eHXvbarQj3cdmpXSqOeUDwaqNAAAAKCSURBVEjHjZTntqsgEIUPVVCwtxg1vfD+L3hHRe8K6snZf+KKn8OewvzsSSeXLruLnz+KHs0gr6DkT3xsRkU6VVn4Ha/UxLe1Z4y64i847sykPBh/AvQ7ry3eFN70oKrfcBJYvm/tQ1qxP4T3emXPeXAkvodPUvtdjbhk+Ft4c0hslTiXVOzxOJ15NWUblQhRsdu3E1AfCjj3Gdm18zSOsiH8Lk4TB480ksy62fiqNo4OpyU8O21l6+hyRtS6z8r1pHlmle5sR1/WXS6Mq2Nl+YeKt3vr+vdH/q4O68tzXuwkiZmngYb4R8Co1jh0+Ww2UTyWxBvtyxLO7QVjO3YOD/lWZpbXDGellFG2Mws58mMnjVZSn7p+XvZ6IF4nn02OJZV0aTO22arp/DgLPtrgpVoi6TPbZm4XQBjY159w02uO0BDdYsfrOEi0M2ulRXlCIPAOuN1NOVhi+riBR3dgwQplYsZRZJLXq23Mlo5njkbY0rZFu3oiNIYG2kqsbVz67OlNuZZIOlfxHDl0UpyRX86z/OYC/3qf1A1xTrMp/PWWM4ePzf8DDp1nesQRpcFk7BlwdzN08ZIALJpCaciQXO0f6k4dnuT/Ewg4l7qSTNzm2SykdHn6GJ12mWc6aCNj/g1cTXpB8YFfr0uVc96aFkkqiIiX4nO+salKwGtIkvfB+Ja8DxMeD3hIXP5mTOYPB4eVT0+32I5ykvPZjesnkGgIREgYnmLrPb0PdV3hoLup2TjcGBPM4mgsfF5BrawZR4/GpzYQzQfrUZCf0TCWYo2DqhdhTJBQ6j4xqmmLN5LjdRIY8LWExiFUsSrza/nmFBqw3I9tEZB9h0lIQSO9if8DkISDAj8CDawAAAAASUVORK5CYII=);background-repeat:no-repeat;background-position:center}.wails-reconnect-overlay-loadingspinner{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#f00 #eee0 #f00 #eee0;border-radius:50%;animation:loadingspin 1s linear infinite;margin:auto;padding:2.5em}@keyframes loadingspin{100%{transform:rotate(360deg); opacity}}',
|
||||||
|
log: function (message) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.log(
|
||||||
|
'%c wails bridge %c ' + message + ' ',
|
||||||
|
'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
|
||||||
|
'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onbeforeunload = function() {
|
||||||
|
if( window.wails.websocket ) {
|
||||||
|
window.wails.websocket.onclose = function () { };
|
||||||
|
window.wails.websocket.close();
|
||||||
|
window.wails.websocket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupIPCBridge() {
|
||||||
|
// darwin
|
||||||
|
window.webkit = {
|
||||||
|
messageHandlers: {
|
||||||
|
external: {
|
||||||
|
postMessage: (message) => {
|
||||||
|
window.wailsbridge.websocket.send(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from webview - thanks zserge!
|
||||||
|
function injectCSS(css) {
|
||||||
|
const elem = document.createElement('style');
|
||||||
|
elem.setAttribute('type', 'text/css');
|
||||||
|
elem.appendChild(document.createTextNode(css));
|
||||||
|
const head = document.head || document.getElementsByTagName('head')[0];
|
||||||
|
head.appendChild(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a node in the Dom
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} parent
|
||||||
|
* @param {string} elementType
|
||||||
|
* @param {string} id
|
||||||
|
* @param {string|null} className
|
||||||
|
* @param {string|null} content
|
||||||
|
*/
|
||||||
|
function createNode(parent, elementType, id, className, content) {
|
||||||
|
const d = document.createElement(elementType);
|
||||||
|
if (id) {
|
||||||
|
d.id = id;
|
||||||
|
}
|
||||||
|
if (className) {
|
||||||
|
d.className = className;
|
||||||
|
}
|
||||||
|
if (content) {
|
||||||
|
d.innerHTML = content;
|
||||||
|
}
|
||||||
|
parent.appendChild(d);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets up the overlay
|
||||||
|
function setupOverlay() {
|
||||||
|
const body = document.body;
|
||||||
|
const wailsBridgeNode = createNode(body, 'div', 'wails-bridge', null, null);
|
||||||
|
wailsBridgeNode.innerHTML = window.wailsbridge.overlayHTML;
|
||||||
|
|
||||||
|
// Inject the overlay CSS
|
||||||
|
injectCSS(window.wailsbridge.overlayCSS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the Wails Bridge
|
||||||
|
function startBridge() {
|
||||||
|
// Setup the overlay
|
||||||
|
setupOverlay();
|
||||||
|
|
||||||
|
window.wailsbridge.websocket = null;
|
||||||
|
window.wailsbridge.connectTimer = null;
|
||||||
|
window.wailsbridge.reconnectOverlay = document.querySelector(
|
||||||
|
'.wails-reconnect-overlay'
|
||||||
|
);
|
||||||
|
window.wailsbridge.connectionState = 'disconnected';
|
||||||
|
|
||||||
|
// Shows the overlay
|
||||||
|
function showReconnectOverlay() {
|
||||||
|
window.wailsbridge.reconnectOverlay.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hides the overlay
|
||||||
|
const hideReconnectOverlay = function () {
|
||||||
|
window.wailsbridge.reconnectOverlay.style.display = 'none';
|
||||||
|
}
|
||||||
|
window.wailsbridge.hideReconnectOverlay = hideReconnectOverlay;
|
||||||
|
|
||||||
|
// Adds a script to the Dom.
|
||||||
|
// Removes it if second parameter is true.
|
||||||
|
function addScript(script, remove) {
|
||||||
|
const s = document.createElement('script');
|
||||||
|
s.setAttribute('type', 'text/javascript');
|
||||||
|
s.textContent = script;
|
||||||
|
document.head.appendChild(s);
|
||||||
|
|
||||||
|
// Remove internal messages from the DOM
|
||||||
|
if (remove) {
|
||||||
|
s.parentNode.removeChild(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles incoming websocket connections
|
||||||
|
function handleConnect() {
|
||||||
|
window.wailsbridge.log('Connected to backend');
|
||||||
|
setupIPCBridge();
|
||||||
|
hideReconnectOverlay();
|
||||||
|
clearInterval(window.wailsbridge.connectTimer);
|
||||||
|
window.wailsbridge.websocket.onclose = handleDisconnect;
|
||||||
|
window.wailsbridge.websocket.onmessage = handleMessage;
|
||||||
|
window.wailsbridge.connectionState = 'connected';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles websocket disconnects
|
||||||
|
function handleDisconnect() {
|
||||||
|
window.wailsbridge.log('Disconnected from backend');
|
||||||
|
window.wailsbridge.websocket = null;
|
||||||
|
window.wailsbridge.connectionState = 'disconnected';
|
||||||
|
showReconnectOverlay();
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect to the backend every 1s (default value).
|
||||||
|
// Change this value in the main wailsbridge object.
|
||||||
|
function connect() {
|
||||||
|
window.wailsbridge.connectTimer = setInterval(function () {
|
||||||
|
if (window.wailsbridge.websocket == null) {
|
||||||
|
window.wailsbridge.websocket = new WebSocket(window.wailsbridge.wsURL);
|
||||||
|
window.wailsbridge.websocket.onopen = handleConnect;
|
||||||
|
window.wailsbridge.websocket.onerror = function (e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
window.wailsbridge.websocket = null;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, window.wailsbridge.reconnectTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessage(message) {
|
||||||
|
// As a bridge we ignore js and css injections
|
||||||
|
switch (message.data[0]) {
|
||||||
|
// Wails library - inject!
|
||||||
|
case 'b':
|
||||||
|
message = message.data.slice(1)
|
||||||
|
addScript(message);
|
||||||
|
window.wailsbridge.log('Loaded Wails Runtime');
|
||||||
|
|
||||||
|
// We need to now send a message to the backend telling it
|
||||||
|
// we have loaded (System Start)
|
||||||
|
window.webkit.messageHandlers.external.postMessage("SS");
|
||||||
|
|
||||||
|
// Now wails runtime is loaded, wails for the ready event
|
||||||
|
// and callback to the main app
|
||||||
|
// window.wails.Events.On('wails:loaded', function () {
|
||||||
|
if (window.wailsbridge.callback) {
|
||||||
|
window.wailsbridge.log('Notifying application');
|
||||||
|
window.wailsbridge.callback(window.wails);
|
||||||
|
}
|
||||||
|
// });
|
||||||
|
break;
|
||||||
|
// // Notifications
|
||||||
|
// case 'n':
|
||||||
|
// addScript(message.data.slice(1), true);
|
||||||
|
// break;
|
||||||
|
// // Binding
|
||||||
|
// case 'b':
|
||||||
|
// const binding = message.data.slice(1);
|
||||||
|
// //log("Binding: " + binding)
|
||||||
|
// window.wails._.NewBinding(binding);
|
||||||
|
// break;
|
||||||
|
// // Call back
|
||||||
|
case 'c':
|
||||||
|
const callbackData = message.data.slice(1);
|
||||||
|
window.wails._.Callback(callbackData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
window.wailsbridge.log('Unknown message: ' + message.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start by showing the overlay...
|
||||||
|
showReconnectOverlay();
|
||||||
|
|
||||||
|
// ...and attempt to connect
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
function InitBridge(callback) {
|
||||||
|
// Set up the bridge
|
||||||
|
init();
|
||||||
|
|
||||||
|
// Save the callback
|
||||||
|
window.wailsbridge.callback = callback;
|
||||||
|
|
||||||
|
// Start Bridge
|
||||||
|
startBridge();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
InitBridge: InitBridge,
|
||||||
|
}
|
||||||
@@ -9,13 +9,25 @@ The lightweight framework for web-like apps
|
|||||||
*/
|
*/
|
||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import { InitBridge } from './bridge';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises the Wails runtime
|
* ready will execute the callback when Wails has loaded
|
||||||
|
* and initialised.
|
||||||
*
|
*
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
function Init(callback) {
|
function ready(callback) {
|
||||||
window.wails._.Init(callback);
|
|
||||||
|
// If window.wails exists, we are ready
|
||||||
|
if( window.wails ) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not we need to setup the bridge
|
||||||
|
InitBridge(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Init;
|
module.exports = {
|
||||||
|
ready: ready,
|
||||||
|
};
|
||||||
@@ -23,7 +23,7 @@ module.exports = {
|
|||||||
Browser: Browser,
|
Browser: Browser,
|
||||||
Dialog: Dialog,
|
Dialog: Dialog,
|
||||||
Events: Events,
|
Events: Events,
|
||||||
Init: Init,
|
ready: Init.ready,
|
||||||
Log: Log,
|
Log: Log,
|
||||||
System: System,
|
System: System,
|
||||||
Store: Store,
|
Store: Store,
|
||||||
|
|||||||
2
v2/internal/runtime/js/runtime/package-lock.json
generated
2
v2/internal/runtime/js/runtime/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wails/runtime",
|
"name": "@wails/runtime",
|
||||||
"version": "1.2.13",
|
"version": "1.2.24",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wails/runtime",
|
"name": "@wails/runtime",
|
||||||
"version": "1.2.22",
|
"version": "1.3.8",
|
||||||
"description": "Wails V2 Javascript runtime library",
|
"description": "Wails V2 Javascript runtime library",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"types": "runtime.d.ts",
|
"types": "runtime.d.ts",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const Events = require('./events');
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked when the user changes the
|
* Registers an event listener that will be invoked when the user changes the
|
||||||
* desktop theme (light mode / dark mode). The callback receives a boolean which
|
* desktop theme (light mode / dark mode). The callback receives a booleanean which
|
||||||
* indicates if dark mode is enabled.
|
* indicates if dark mode is enabled.
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
@@ -43,12 +43,12 @@ function DarkModeEnabled() {
|
|||||||
* Mac Title Bar Config
|
* Mac Title Bar Config
|
||||||
* Check out https://github.com/lukakerr/NSWindowStyles for some examples of these settings
|
* Check out https://github.com/lukakerr/NSWindowStyles for some examples of these settings
|
||||||
* @typedef {Object} MacTitleBar
|
* @typedef {Object} MacTitleBar
|
||||||
* @param {bool} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent
|
* @param {boolean} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent
|
||||||
* @param {bool} HideTitle - NSWindow.hideTitle
|
* @param {boolean} HideTitle - NSWindow.hideTitle
|
||||||
* @param {bool} HideTitleBar - NSWindow.hideTitleBar
|
* @param {boolean} HideTitleBar - NSWindow.hideTitleBar
|
||||||
* @param {bool} FullSizeContent - Makes the webview portion of the window the full size of the window, even over the titlebar
|
* @param {boolean} FullSizeContent - Makes the webview portion of the window the full size of the window, even over the titlebar
|
||||||
* @param {bool} UseToolbar - Set true to add a blank toolbar to the window (makes the title bar larger)
|
* @param {boolean} UseToolbar - Set true to add a blank toolbar to the window (makes the title bar larger)
|
||||||
* @param {bool} HideToolbarSeparator - Set true to remove the separator between the toolbar and the main content area
|
* @param {boolean} HideToolbarSeparator - Set true to remove the separator between the toolbar and the main content area
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ function DarkModeEnabled() {
|
|||||||
* @param {number} MinHeight - Window Minimum Height
|
* @param {number} MinHeight - Window Minimum Height
|
||||||
* @param {number} MaxWidth - Window Maximum Width
|
* @param {number} MaxWidth - Window Maximum Width
|
||||||
* @param {number} MaxHeight - Window Maximum Height
|
* @param {number} MaxHeight - Window Maximum Height
|
||||||
* @param {bool} StartHidden - Start with window hidden
|
* @param {boolean} StartHidden - Start with window hidden
|
||||||
* @param {bool} DevTools - Enables the window devtools
|
* @param {boolean} DevTools - Enables the window devtools
|
||||||
* @param {number} RBGA - The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA
|
* @param {number} RBGA - The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA
|
||||||
* @param {MacAppConfig} [Mac] - Configuration when running on Mac
|
* @param {MacAppConfig} [Mac] - Configuration when running on Mac
|
||||||
* @param {LinuxAppConfig} [Linux] - Configuration when running on Linux
|
* @param {LinuxAppConfig} [Linux] - Configuration when running on Linux
|
||||||
@@ -88,11 +88,23 @@ function AppConfig() {
|
|||||||
return window.wails.System.AppConfig.get();
|
return window.wails.System.AppConfig.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LogLevel() {
|
||||||
|
return window.wails.System.LogLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Platform() {
|
||||||
|
return window.wails.System.Platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppType() {
|
||||||
|
return window.wails.System.AppType();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
OnThemeChange: OnThemeChange,
|
OnThemeChange: OnThemeChange,
|
||||||
DarkModeEnabled: DarkModeEnabled,
|
DarkModeEnabled: DarkModeEnabled,
|
||||||
LogLevel: window.wails.System.LogLevel,
|
LogLevel: LogLevel,
|
||||||
Platform: window.wails.System.Platform,
|
Platform: Platform,
|
||||||
AppType: window.wails.System.AppType,
|
AppType: AppType,
|
||||||
AppConfig: AppConfig,
|
AppConfig: AppConfig,
|
||||||
};
|
};
|
||||||
@@ -13,7 +13,7 @@ module.exports = {
|
|||||||
mode: 'production',
|
mode: 'production',
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, '..', 'assets'),
|
path: path.resolve(__dirname, '..', 'assets'),
|
||||||
filename: 'desktop.js',
|
filename: 'desktop_'+platform+'.js',
|
||||||
library: 'Wails'
|
library: 'Wails'
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
@@ -6,19 +6,20 @@ import (
|
|||||||
|
|
||||||
// Runtime is a means for the user to interact with the application at runtime
|
// Runtime is a means for the user to interact with the application at runtime
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
Browser Browser
|
Browser Browser
|
||||||
Events Events
|
Events Events
|
||||||
Window Window
|
Window Window
|
||||||
Dialog Dialog
|
Dialog Dialog
|
||||||
System System
|
System System
|
||||||
Menu Menu
|
Menu Menu
|
||||||
Store *StoreProvider
|
Store *StoreProvider
|
||||||
Log Log
|
Log Log
|
||||||
bus *servicebus.ServiceBus
|
bus *servicebus.ServiceBus
|
||||||
|
shutdownCallback func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new runtime
|
// New creates a new runtime
|
||||||
func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
func New(serviceBus *servicebus.ServiceBus, shutdownCallback func()) *Runtime {
|
||||||
result := &Runtime{
|
result := &Runtime{
|
||||||
Browser: newBrowser(),
|
Browser: newBrowser(),
|
||||||
Events: newEvents(serviceBus),
|
Events: newEvents(serviceBus),
|
||||||
@@ -35,5 +36,11 @@ func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
|||||||
|
|
||||||
// Quit the application
|
// Quit the application
|
||||||
func (r *Runtime) Quit() {
|
func (r *Runtime) Quit() {
|
||||||
|
// Call back to user's shutdown method if defined
|
||||||
|
if r.shutdownCallback != nil {
|
||||||
|
r.shutdownCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start shutdown of Wails
|
||||||
r.bus.Publish("quit", "runtime.Quit()")
|
r.bus.Publish("quit", "runtime.Quit()")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/shell"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -49,12 +50,20 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wailsJS := fs.RelativePath("../../../internal/runtime/assets/desktop.js")
|
wailsJS := fs.RelativePath("../assets/desktop_" + platform + ".js")
|
||||||
runtimeData, err := ioutil.ReadFile(wailsJS)
|
runtimeData, err := ioutil.ReadFile(wailsJS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy this file to bridge directory for embedding
|
||||||
|
bridgeDir := fs.RelativePath("../../bridge/" + platform + ".js")
|
||||||
|
println("Copying", wailsJS, "to", bridgeDir)
|
||||||
|
err = fs.CopyFile(wailsJS, bridgeDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Convert to C structure
|
// Convert to C structure
|
||||||
runtimeC := `
|
runtimeC := `
|
||||||
// runtime.c (c) 2019-Present Lea Anthony.
|
// runtime.c (c) 2019-Present Lea Anthony.
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
golog "log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/deepcopy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options defines the optional data that may be used
|
// Options defines the optional data that may be used
|
||||||
@@ -64,21 +67,31 @@ func fatal(err error) {
|
|||||||
// New creates a new store
|
// New creates a new store
|
||||||
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
|
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
|
||||||
|
|
||||||
dataType := reflect.TypeOf(defaultValue)
|
if defaultValue == nil {
|
||||||
|
golog.Fatal("Cannot initialise a store with nil")
|
||||||
|
}
|
||||||
|
|
||||||
result := Store{
|
result := Store{
|
||||||
name: name,
|
name: name,
|
||||||
runtime: p.runtime,
|
runtime: p.runtime,
|
||||||
data: reflect.ValueOf(defaultValue),
|
|
||||||
dataType: dataType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the sync listener
|
// Setup the sync listener
|
||||||
result.setupListener()
|
result.setupListener()
|
||||||
|
|
||||||
|
result.Set(defaultValue)
|
||||||
|
|
||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) lock() {
|
||||||
|
s.mux.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) unlock() {
|
||||||
|
s.mux.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// OnError takes a function that will be called
|
// OnError takes a function that will be called
|
||||||
// whenever an error occurs
|
// whenever an error occurs
|
||||||
func (s *Store) OnError(callback func(error)) {
|
func (s *Store) OnError(callback func(error)) {
|
||||||
@@ -105,7 +118,7 @@ func (s *Store) processUpdatedData(data string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lock mutex for writing
|
// Lock mutex for writing
|
||||||
s.mux.Lock()
|
s.lock()
|
||||||
|
|
||||||
// Handle nulls
|
// Handle nulls
|
||||||
if newData == nil {
|
if newData == nil {
|
||||||
@@ -116,7 +129,7 @@ func (s *Store) processUpdatedData(data string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unlock mutex
|
// Unlock mutex
|
||||||
s.mux.Unlock()
|
s.unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -153,17 +166,27 @@ func (s *Store) setupListener() {
|
|||||||
|
|
||||||
func (s *Store) resync() {
|
func (s *Store) resync() {
|
||||||
|
|
||||||
// Stringify data
|
// Lock
|
||||||
newdata, err := json.Marshal(s.data.Interface())
|
s.lock()
|
||||||
if err != nil {
|
defer s.unlock()
|
||||||
if s.errorHandler != nil {
|
|
||||||
s.errorHandler(err)
|
var result string
|
||||||
return
|
|
||||||
|
if s.data.IsValid() {
|
||||||
|
rawdata, err := json.Marshal(s.data.Interface())
|
||||||
|
if err != nil {
|
||||||
|
if s.errorHandler != nil {
|
||||||
|
s.errorHandler(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
result = string(rawdata)
|
||||||
|
} else {
|
||||||
|
result = "{}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit event to front end
|
// Emit event to front end
|
||||||
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, string(newdata))
|
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, result)
|
||||||
|
|
||||||
// Notify subscribers
|
// Notify subscribers
|
||||||
s.notify()
|
s.notify()
|
||||||
@@ -176,7 +199,9 @@ func (s *Store) notify() {
|
|||||||
for _, callback := range s.callbacks {
|
for _, callback := range s.callbacks {
|
||||||
|
|
||||||
// Build args
|
// Build args
|
||||||
|
s.lock()
|
||||||
args := []reflect.Value{s.data}
|
args := []reflect.Value{s.data}
|
||||||
|
s.unlock()
|
||||||
|
|
||||||
if s.notifySynchronously {
|
if s.notifySynchronously {
|
||||||
callback.Call(args)
|
callback.Call(args)
|
||||||
@@ -191,16 +216,31 @@ func (s *Store) notify() {
|
|||||||
// and notify listeners of the change
|
// and notify listeners of the change
|
||||||
func (s *Store) Set(data interface{}) error {
|
func (s *Store) Set(data interface{}) error {
|
||||||
|
|
||||||
inType := reflect.TypeOf(data)
|
if data == nil {
|
||||||
|
return fmt.Errorf("cannot set store to nil")
|
||||||
|
}
|
||||||
|
|
||||||
if inType != s.dataType {
|
s.lock()
|
||||||
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
|
|
||||||
|
dataCopy := deepcopy.Copy(data)
|
||||||
|
|
||||||
|
if dataCopy != nil {
|
||||||
|
inType := reflect.TypeOf(dataCopy)
|
||||||
|
|
||||||
|
if inType != s.dataType && s.data.IsValid() {
|
||||||
|
s.unlock()
|
||||||
|
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.dataType == nil {
|
||||||
|
s.dataType = reflect.TypeOf(dataCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save data
|
// Save data
|
||||||
s.mux.Lock()
|
s.data = reflect.ValueOf(dataCopy)
|
||||||
s.data = reflect.ValueOf(data)
|
|
||||||
s.mux.Unlock()
|
s.unlock()
|
||||||
|
|
||||||
// Resync with subscribers
|
// Resync with subscribers
|
||||||
s.resync()
|
s.resync()
|
||||||
@@ -251,7 +291,9 @@ func (s *Store) Subscribe(callback interface{}) {
|
|||||||
|
|
||||||
callbackFunc := reflect.ValueOf(callback)
|
callbackFunc := reflect.ValueOf(callback)
|
||||||
|
|
||||||
|
s.lock()
|
||||||
s.callbacks = append(s.callbacks, callbackFunc)
|
s.callbacks = append(s.callbacks, callbackFunc)
|
||||||
|
s.unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// updaterCheck ensures the given function to Update() is
|
// updaterCheck ensures the given function to Update() is
|
||||||
@@ -301,7 +343,9 @@ func (s *Store) Update(updater interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build args
|
// Build args
|
||||||
|
s.lock()
|
||||||
args := []reflect.Value{s.data}
|
args := []reflect.Value{s.data}
|
||||||
|
s.unlock()
|
||||||
|
|
||||||
// Make call
|
// Make call
|
||||||
results := reflect.ValueOf(updater).Call(args)
|
results := reflect.ValueOf(updater).Call(args)
|
||||||
@@ -312,5 +356,12 @@ func (s *Store) Update(updater interface{}) {
|
|||||||
|
|
||||||
// Get returns the value of the data that's kept in the current state / Store
|
// Get returns the value of the data that's kept in the current state / Store
|
||||||
func (s *Store) Get() interface{} {
|
func (s *Store) Get() interface{} {
|
||||||
|
s.lock()
|
||||||
|
defer s.unlock()
|
||||||
|
|
||||||
|
if !s.data.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return s.data.Interface()
|
return s.data.Interface()
|
||||||
}
|
}
|
||||||
|
|||||||
165
v2/internal/runtime/store_test.go
Normal file
165
v2/internal/runtime/store_test.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
internallogger "github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
|
|
||||||
|
is2 "github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStoreProvider_NewWithNilDefault(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
|
||||||
|
testStore := storeProvider.New("test", 0)
|
||||||
|
|
||||||
|
// You should be able to write a new value into a
|
||||||
|
// store initialised with nil
|
||||||
|
err = testStore.Set(100)
|
||||||
|
is.NoErr(err)
|
||||||
|
|
||||||
|
// You shouldn't be able to write different types to the
|
||||||
|
// store
|
||||||
|
err = testStore.Set(false)
|
||||||
|
is.True(err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreProvider_NewWithScalarDefault(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
testStore := storeProvider.New("test", 100)
|
||||||
|
value := testStore.Get()
|
||||||
|
is.Equal(value, 100)
|
||||||
|
testStore.resync()
|
||||||
|
value = testStore.Get()
|
||||||
|
is.Equal(value, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreProvider_NewWithStructDefault(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
|
||||||
|
type TestValue struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
testValue := &TestValue{
|
||||||
|
Name: "hi",
|
||||||
|
}
|
||||||
|
|
||||||
|
testStore := storeProvider.New("test", testValue)
|
||||||
|
|
||||||
|
err = testStore.Set(testValue)
|
||||||
|
is.NoErr(err)
|
||||||
|
testStore.resync()
|
||||||
|
value := testStore.Get()
|
||||||
|
is.Equal(value, testValue)
|
||||||
|
is.Equal(value.(*TestValue).Name, "hi")
|
||||||
|
|
||||||
|
testValue = &TestValue{
|
||||||
|
Name: "there",
|
||||||
|
}
|
||||||
|
err = testStore.Set(testValue)
|
||||||
|
is.NoErr(err)
|
||||||
|
testStore.resync()
|
||||||
|
value = testStore.Get()
|
||||||
|
is.Equal(value, testValue)
|
||||||
|
is.Equal(value.(*TestValue).Name, "there")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreProvider_RapidReadWrite(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
|
||||||
|
testStore := storeProvider.New("test", 1)
|
||||||
|
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
readers := 100
|
||||||
|
writers := 100
|
||||||
|
wg.Add(readers + writers)
|
||||||
|
// Setup readers
|
||||||
|
go func(testStore *Store, ctx context.Context) {
|
||||||
|
for readerCount := 0; readerCount < readers; readerCount++ {
|
||||||
|
go func(store *Store, ctx context.Context, id int) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
store.Get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(testStore, ctx, readerCount)
|
||||||
|
}
|
||||||
|
}(testStore, ctx)
|
||||||
|
|
||||||
|
// Setup writers
|
||||||
|
go func(testStore *Store, ctx context.Context) {
|
||||||
|
for writerCount := 0; writerCount < writers; writerCount++ {
|
||||||
|
go func(store *Store, ctx context.Context, id int) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
err := store.Set(rand.Int())
|
||||||
|
is.NoErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(testStore, ctx, writerCount)
|
||||||
|
}
|
||||||
|
}(testStore, ctx)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ type Window interface {
|
|||||||
Unminimise()
|
Unminimise()
|
||||||
SetTitle(title string)
|
SetTitle(title string)
|
||||||
SetSize(width int, height int)
|
SetSize(width int, height int)
|
||||||
|
SetMinSize(width int, height int)
|
||||||
|
SetMaxSize(width int, height int)
|
||||||
SetPosition(x int, y int)
|
SetPosition(x int, y int)
|
||||||
Fullscreen()
|
Fullscreen()
|
||||||
UnFullscreen()
|
UnFullscreen()
|
||||||
@@ -85,6 +87,18 @@ func (w *window) SetSize(width int, height int) {
|
|||||||
w.bus.Publish(message, "")
|
w.bus.Publish(message, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetSize sets the size of the window
|
||||||
|
func (w *window) SetMinSize(width int, height int) {
|
||||||
|
message := fmt.Sprintf("window:minsize:%d:%d", width, height)
|
||||||
|
w.bus.Publish(message, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize sets the size of the window
|
||||||
|
func (w *window) SetMaxSize(width int, height int) {
|
||||||
|
message := fmt.Sprintf("window:maxsize:%d:%d", width, height)
|
||||||
|
w.bus.Publish(message, "")
|
||||||
|
}
|
||||||
|
|
||||||
// SetPosition sets the position of the window
|
// SetPosition sets the position of the window
|
||||||
func (w *window) SetPosition(x int, y int) {
|
func (w *window) SetPosition(x int, y int) {
|
||||||
message := fmt.Sprintf("window:position:%d:%d", x, y)
|
message := fmt.Sprintf("window:position:%d:%d", x, y)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package servicebus
|
package servicebus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -12,23 +13,26 @@ import (
|
|||||||
type ServiceBus struct {
|
type ServiceBus struct {
|
||||||
listeners map[string][]chan *Message
|
listeners map[string][]chan *Message
|
||||||
messageQueue chan *Message
|
messageQueue chan *Message
|
||||||
quitChannel chan struct{}
|
|
||||||
wg sync.WaitGroup
|
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
closed bool
|
closed bool
|
||||||
debug bool
|
debug bool
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ServiceBus
|
// New creates a new ServiceBus
|
||||||
// The internal message queue is set to 100 messages
|
// The internal message queue is set to 100 messages
|
||||||
// Listener queues are set to 10
|
// Listener queues are set to 10
|
||||||
func New(logger *logger.Logger) *ServiceBus {
|
func New(logger *logger.Logger) *ServiceBus {
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &ServiceBus{
|
return &ServiceBus{
|
||||||
listeners: make(map[string][]chan *Message),
|
listeners: make(map[string][]chan *Message),
|
||||||
messageQueue: make(chan *Message, 100),
|
messageQueue: make(chan *Message, 100),
|
||||||
quitChannel: make(chan struct{}, 1),
|
|
||||||
logger: logger.CustomLogger("Service Bus"),
|
logger: logger.CustomLogger("Service Bus"),
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,23 +67,22 @@ func (s *ServiceBus) Debug() {
|
|||||||
// Start the service bus
|
// Start the service bus
|
||||||
func (s *ServiceBus) Start() error {
|
func (s *ServiceBus) Start() error {
|
||||||
|
|
||||||
s.logger.Trace("Starting")
|
|
||||||
|
|
||||||
// Prevent starting when closed
|
// Prevent starting when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return fmt.Errorf("cannot call start on closed servicebus")
|
return fmt.Errorf("cannot call start on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
// We run in a different thread
|
s.logger.Trace("Starting")
|
||||||
go func() {
|
|
||||||
|
|
||||||
quit := false
|
go func() {
|
||||||
s.wg.Add(1)
|
defer s.logger.Trace("Stopped")
|
||||||
|
|
||||||
// Loop until we get a quit message
|
// Loop until we get a quit message
|
||||||
for !quit {
|
for {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
|
||||||
// Listen for messages
|
// Listen for messages
|
||||||
case message := <-s.messageQueue:
|
case message := <-s.messageQueue:
|
||||||
@@ -90,16 +93,9 @@ func (s *ServiceBus) Start() error {
|
|||||||
}
|
}
|
||||||
// Dispatch message
|
// Dispatch message
|
||||||
s.dispatchMessage(message)
|
s.dispatchMessage(message)
|
||||||
|
|
||||||
// Listen for quit messages
|
|
||||||
case <-s.quitChannel:
|
|
||||||
quit = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicate we have shut down
|
|
||||||
s.wg.Done()
|
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -116,10 +112,7 @@ func (s *ServiceBus) Stop() error {
|
|||||||
s.closed = true
|
s.closed = true
|
||||||
|
|
||||||
// Send quit message
|
// Send quit message
|
||||||
s.quitChannel <- struct{}{}
|
s.cancel()
|
||||||
|
|
||||||
// Wait for dispatcher to stop
|
|
||||||
s.wg.Wait()
|
|
||||||
|
|
||||||
// Close down subscriber channels
|
// Close down subscriber channels
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
@@ -134,7 +127,6 @@ func (s *ServiceBus) Stop() error {
|
|||||||
// Close message queue
|
// Close message queue
|
||||||
close(s.messageQueue)
|
close(s.messageQueue)
|
||||||
|
|
||||||
s.logger.Trace("Stopped")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +163,6 @@ func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) {
|
|||||||
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +174,6 @@ func (s *ServiceBus) Publish(topic string, data interface{}) {
|
|||||||
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message := NewMessageForTarget(topic, data, target)
|
message := NewMessageForTarget(topic, data, target)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package signal
|
package signal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
gosignal "os/signal"
|
gosignal "os/signal"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -20,24 +22,29 @@ type Manager struct {
|
|||||||
// signalChannel
|
// signalChannel
|
||||||
signalchannel chan os.Signal
|
signalchannel chan os.Signal
|
||||||
|
|
||||||
// Quit channel
|
// ctx
|
||||||
quitChannel <-chan *servicebus.Message
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// The shutdown callback to notify the user's app that a shutdown
|
||||||
|
// has started
|
||||||
|
shutdownCallback func()
|
||||||
|
|
||||||
|
// Parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new signal manager
|
// NewManager creates a new signal manager
|
||||||
func NewManager(bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger, shutdownCallback func()) (*Manager, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &Manager{
|
result := &Manager{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
logger: logger.CustomLogger("Event Manager"),
|
logger: logger.CustomLogger("Event Manager"),
|
||||||
signalchannel: make(chan os.Signal, 2),
|
signalchannel: make(chan os.Signal, 2),
|
||||||
quitChannel: quitChannel,
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
shutdownCallback: shutdownCallback,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -49,20 +56,28 @@ func (m *Manager) Start() {
|
|||||||
// Hook into interrupts
|
// Hook into interrupts
|
||||||
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
// Spin off signal listener
|
m.wg.Add(1)
|
||||||
|
|
||||||
|
// Spin off signal listener and wait for either a cancellation
|
||||||
|
// or signal
|
||||||
go func() {
|
go func() {
|
||||||
running := true
|
select {
|
||||||
for running {
|
case <-m.signalchannel:
|
||||||
select {
|
println()
|
||||||
case <-m.signalchannel:
|
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||||
println()
|
m.bus.Publish("quit", "ctrl-c pressed")
|
||||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
|
||||||
m.bus.Publish("quit", "ctrl-c pressed")
|
// Shutdown app first
|
||||||
case <-m.quitChannel:
|
if m.shutdownCallback != nil {
|
||||||
running = false
|
m.shutdownCallback()
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start shutdown of Wails
|
||||||
|
m.cancel()
|
||||||
|
|
||||||
|
case <-m.ctx.Done():
|
||||||
}
|
}
|
||||||
m.logger.Trace("Shutdown")
|
m.logger.Trace("Shutdown")
|
||||||
|
m.wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
// Binding is the Binding subsystem. It manages all service bus messages
|
// Binding is the Binding subsystem. It manages all service bus messages
|
||||||
// starting with "binding".
|
// starting with "binding".
|
||||||
type Binding struct {
|
type Binding struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
bindingChannel <-chan *servicebus.Message
|
bindingChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
running bool
|
||||||
|
|
||||||
// Binding db
|
// Binding db
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
@@ -27,12 +27,6 @@ type Binding struct {
|
|||||||
// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
|
// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
|
||||||
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *runtime.Runtime) (*Binding, error) {
|
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *runtime.Runtime) (*Binding, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
bindingChannel, err := bus.Subscribe("binding")
|
bindingChannel, err := bus.Subscribe("binding")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,7 +34,6 @@ func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *bin
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Binding{
|
result := &Binding{
|
||||||
quitChannel: quitChannel,
|
|
||||||
bindingChannel: bindingChannel,
|
bindingChannel: bindingChannel,
|
||||||
logger: logger.CustomLogger("Binding Subsystem"),
|
logger: logger.CustomLogger("Binding Subsystem"),
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
@@ -61,20 +54,16 @@ func (b *Binding) Start() error {
|
|||||||
go func() {
|
go func() {
|
||||||
for b.running {
|
for b.running {
|
||||||
select {
|
select {
|
||||||
case <-b.quitChannel:
|
|
||||||
b.running = false
|
|
||||||
case bindingMessage := <-b.bindingChannel:
|
case bindingMessage := <-b.bindingChannel:
|
||||||
b.logger.Trace("Got binding message: %+v", bindingMessage)
|
b.logger.Trace("Got binding message: %+v", bindingMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
b.logger.Trace("Shutdown")
|
||||||
// Call shutdown
|
|
||||||
b.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Binding) shutdown() {
|
func (b *Binding) Close() {
|
||||||
b.logger.Trace("Shutdown")
|
b.running = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -16,9 +19,10 @@ import (
|
|||||||
// Call is the Call subsystem. It manages all service bus messages
|
// Call is the Call subsystem. It manages all service bus messages
|
||||||
// starting with "call".
|
// starting with "call".
|
||||||
type Call struct {
|
type Call struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
callChannel <-chan *servicebus.Message
|
callChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
// quit flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
// bindings DB
|
// bindings DB
|
||||||
DB *binding.DB
|
DB *binding.DB
|
||||||
@@ -31,16 +35,16 @@ type Call struct {
|
|||||||
|
|
||||||
// runtime
|
// runtime
|
||||||
runtime *runtime.Runtime
|
runtime *runtime.Runtime
|
||||||
|
|
||||||
|
// context
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCall creates a new call subsystem
|
// NewCall creates a new call subsystem
|
||||||
func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
|
func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
callChannel, err := bus.Subscribe("call:invoke")
|
callChannel, err := bus.Subscribe("call:invoke")
|
||||||
@@ -49,12 +53,13 @@ func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Call{
|
result := &Call{
|
||||||
quitChannel: quitChannel,
|
|
||||||
callChannel: callChannel,
|
callChannel: callChannel,
|
||||||
logger: logger.CustomLogger("Call Subsystem"),
|
logger: logger.CustomLogger("Call Subsystem"),
|
||||||
DB: DB,
|
DB: DB,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
|
ctx: ctx,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -63,22 +68,21 @@ func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB,
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (c *Call) Start() error {
|
func (c *Call) Start() error {
|
||||||
|
|
||||||
c.running = true
|
c.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for c.running {
|
defer c.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.quitChannel:
|
case <-c.ctx.Done():
|
||||||
c.running = false
|
c.wg.Done()
|
||||||
|
return
|
||||||
case callMessage := <-c.callChannel:
|
case callMessage := <-c.callChannel:
|
||||||
// TODO: Check if this works ok in a goroutine
|
|
||||||
c.processCall(callMessage)
|
c.processCall(callMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
c.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -190,10 +194,6 @@ func (c *Call) sendError(err error, payload *message.CallMessage, clientID strin
|
|||||||
c.bus.PublishForTarget("call:result", string(messageData), clientID)
|
c.bus.PublishForTarget("call:result", string(messageData), clientID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Call) shutdown() {
|
|
||||||
c.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallbackMessage defines a message that contains the result of a call
|
// CallbackMessage defines a message that contains the result of a call
|
||||||
type CallbackMessage struct {
|
type CallbackMessage struct {
|
||||||
Result interface{} `json:"result"`
|
Result interface{} `json:"result"`
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -22,9 +23,7 @@ type eventListener struct {
|
|||||||
// Event is the Eventing subsystem. It manages all service bus messages
|
// Event is the Eventing subsystem. It manages all service bus messages
|
||||||
// starting with "event".
|
// starting with "event".
|
||||||
type Event struct {
|
type Event struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
eventChannel <-chan *servicebus.Message
|
eventChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
|
||||||
// Event listeners
|
// Event listeners
|
||||||
listeners map[string][]*eventListener
|
listeners map[string][]*eventListener
|
||||||
@@ -32,16 +31,16 @@ type Event struct {
|
|||||||
|
|
||||||
// logger
|
// logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
|
// ctx
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvent creates a new log subsystem
|
// NewEvent creates a new log subsystem
|
||||||
func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
func NewEvent(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
eventChannel, err := bus.Subscribe("event")
|
eventChannel, err := bus.Subscribe("event")
|
||||||
@@ -50,10 +49,11 @@ func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Event{
|
result := &Event{
|
||||||
quitChannel: quitChannel,
|
|
||||||
eventChannel: eventChannel,
|
eventChannel: eventChannel,
|
||||||
logger: logger.CustomLogger("Event Subsystem"),
|
logger: logger.CustomLogger("Event Subsystem"),
|
||||||
listeners: make(map[string][]*eventListener),
|
listeners: make(map[string][]*eventListener),
|
||||||
|
ctx: ctx,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -80,15 +80,16 @@ func (e *Event) Start() error {
|
|||||||
|
|
||||||
e.logger.Trace("Starting")
|
e.logger.Trace("Starting")
|
||||||
|
|
||||||
e.running = true
|
e.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for e.running {
|
defer e.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-e.quitChannel:
|
case <-e.ctx.Done():
|
||||||
e.running = false
|
e.wg.Done()
|
||||||
break
|
return
|
||||||
case eventMessage := <-e.eventChannel:
|
case eventMessage := <-e.eventChannel:
|
||||||
splitTopic := strings.Split(eventMessage.Topic(), ":")
|
splitTopic := strings.Split(eventMessage.Topic(), ":")
|
||||||
eventType := splitTopic[1]
|
eventType := splitTopic[1]
|
||||||
@@ -128,8 +129,6 @@ func (e *Event) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
e.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -190,7 +189,3 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
|||||||
// Unlock
|
// Unlock
|
||||||
e.notifyLock.Unlock()
|
e.notifyLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Event) shutdown() {
|
|
||||||
e.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
@@ -12,15 +14,23 @@ import (
|
|||||||
// Log is the Logging subsystem. It handles messages with topics starting
|
// Log is the Logging subsystem. It handles messages with topics starting
|
||||||
// with "log:"
|
// with "log:"
|
||||||
type Log struct {
|
type Log struct {
|
||||||
logChannel <-chan *servicebus.Message
|
logChannel <-chan *servicebus.Message
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
running bool
|
// quit flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
// Logger!
|
// Logger!
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
|
|
||||||
// Loglevel store
|
// Loglevel store
|
||||||
logLevelStore *runtime.Store
|
logLevelStore *runtime.Store
|
||||||
|
|
||||||
|
// Context for shutdown
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// internal waitgroup
|
||||||
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLog creates a new log subsystem
|
// NewLog creates a new log subsystem
|
||||||
@@ -32,17 +42,14 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to quit messages
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &Log{
|
result := &Log{
|
||||||
logChannel: logChannel,
|
logChannel: logChannel,
|
||||||
quitChannel: quitChannel,
|
|
||||||
logger: logger,
|
logger: logger,
|
||||||
logLevelStore: logLevelStore,
|
logLevelStore: logLevelStore,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -51,15 +58,17 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (l *Log) Start() error {
|
func (l *Log) Start() error {
|
||||||
|
|
||||||
l.running = true
|
l.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for l.running {
|
defer l.logger.Trace("Logger Shutdown")
|
||||||
|
|
||||||
|
for l.shouldQuit == false {
|
||||||
select {
|
select {
|
||||||
case <-l.quitChannel:
|
case <-l.ctx.Done():
|
||||||
l.running = false
|
l.wg.Done()
|
||||||
break
|
return
|
||||||
case logMessage := <-l.logChannel:
|
case logMessage := <-l.logChannel:
|
||||||
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
||||||
switch logType {
|
switch logType {
|
||||||
@@ -98,8 +107,12 @@ func (l *Log) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l.logger.Trace("Logger Shutdown")
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Log) Close() {
|
||||||
|
l.cancel()
|
||||||
|
l.wg.Wait()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
@@ -13,9 +16,10 @@ import (
|
|||||||
// Menu is the subsystem that handles the operation of menus. It manages all service bus messages
|
// Menu is the subsystem that handles the operation of menus. It manages all service bus messages
|
||||||
// starting with "menu".
|
// starting with "menu".
|
||||||
type Menu struct {
|
type Menu struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
menuChannel <-chan *servicebus.Message
|
menuChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
// shutdown flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
// logger
|
// logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -25,16 +29,16 @@ type Menu struct {
|
|||||||
|
|
||||||
// Menu Manager
|
// Menu Manager
|
||||||
menuManager *menumanager.Manager
|
menuManager *menumanager.Manager
|
||||||
|
|
||||||
|
// ctx
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMenu creates a new menu subsystem
|
// NewMenu creates a new menu subsystem
|
||||||
func NewMenu(bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *menumanager.Manager) (*Menu, error) {
|
func NewMenu(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *menumanager.Manager) (*Menu, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to menu messages
|
// Subscribe to menu messages
|
||||||
menuChannel, err := bus.Subscribe("menu:")
|
menuChannel, err := bus.Subscribe("menu:")
|
||||||
@@ -43,11 +47,12 @@ func NewMenu(bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *men
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Menu{
|
result := &Menu{
|
||||||
quitChannel: quitChannel,
|
|
||||||
menuChannel: menuChannel,
|
menuChannel: menuChannel,
|
||||||
logger: logger.CustomLogger("Menu Subsystem"),
|
logger: logger.CustomLogger("Menu Subsystem"),
|
||||||
bus: bus,
|
bus: bus,
|
||||||
menuManager: menuManager,
|
menuManager: menuManager,
|
||||||
|
ctx: ctx,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -58,15 +63,16 @@ func (m *Menu) Start() error {
|
|||||||
|
|
||||||
m.logger.Trace("Starting")
|
m.logger.Trace("Starting")
|
||||||
|
|
||||||
m.running = true
|
m.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for m.running {
|
defer m.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-m.quitChannel:
|
case <-m.ctx.Done():
|
||||||
m.running = false
|
m.wg.Done()
|
||||||
break
|
return
|
||||||
case menuMessage := <-m.menuChannel:
|
case menuMessage := <-m.menuChannel:
|
||||||
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
||||||
menuMessageType := splitTopic[1]
|
menuMessageType := splitTopic[1]
|
||||||
@@ -147,14 +153,7 @@ func (m *Menu) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
m.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) shutdown() {
|
|
||||||
m.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
@@ -12,7 +14,6 @@ import (
|
|||||||
// Runtime is the Runtime subsystem. It handles messages with topics starting
|
// Runtime is the Runtime subsystem. It handles messages with topics starting
|
||||||
// with "runtime:"
|
// with "runtime:"
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
runtimeChannel <-chan *servicebus.Message
|
runtimeChannel <-chan *servicebus.Message
|
||||||
|
|
||||||
// The hooks channel allows us to hook into frontend startup
|
// The hooks channel allows us to hook into frontend startup
|
||||||
@@ -20,22 +21,23 @@ type Runtime struct {
|
|||||||
startupCallback func(*runtime.Runtime)
|
startupCallback func(*runtime.Runtime)
|
||||||
shutdownCallback func()
|
shutdownCallback func()
|
||||||
|
|
||||||
running bool
|
// quit flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
// Runtime library
|
// Runtime library
|
||||||
runtime *runtime.Runtime
|
runtime *runtime.Runtime
|
||||||
|
|
||||||
|
//ctx
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// Startup Hook
|
||||||
|
startupOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRuntime creates a new runtime subsystem
|
// NewRuntime creates a new runtime subsystem
|
||||||
func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime), shutdownCallback func()) (*Runtime, error) {
|
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime), shutdownCallback func()) (*Runtime, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to log messages
|
// Subscribe to log messages
|
||||||
runtimeChannel, err := bus.Subscribe("runtime:")
|
runtimeChannel, err := bus.Subscribe("runtime:")
|
||||||
@@ -50,13 +52,13 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallba
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Runtime{
|
result := &Runtime{
|
||||||
quitChannel: quitChannel,
|
|
||||||
runtimeChannel: runtimeChannel,
|
runtimeChannel: runtimeChannel,
|
||||||
hooksChannel: hooksChannel,
|
hooksChannel: hooksChannel,
|
||||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||||
runtime: runtime.New(bus),
|
runtime: runtime.New(bus, shutdownCallback),
|
||||||
startupCallback: startupCallback,
|
startupCallback: startupCallback,
|
||||||
shutdownCallback: shutdownCallback,
|
shutdownCallback: shutdownCallback,
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -65,15 +67,11 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallba
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (r *Runtime) Start() error {
|
func (r *Runtime) Start() error {
|
||||||
|
|
||||||
r.running = true
|
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for r.running {
|
defer r.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-r.quitChannel:
|
|
||||||
r.running = false
|
|
||||||
break
|
|
||||||
case hooksMessage := <-r.hooksChannel:
|
case hooksMessage := <-r.hooksChannel:
|
||||||
r.logger.Trace(fmt.Sprintf("Received hooksmessage: %+v", hooksMessage))
|
r.logger.Trace(fmt.Sprintf("Received hooksmessage: %+v", hooksMessage))
|
||||||
messageSlice := strings.Split(hooksMessage.Topic(), ":")
|
messageSlice := strings.Split(hooksMessage.Topic(), ":")
|
||||||
@@ -81,7 +79,9 @@ func (r *Runtime) Start() error {
|
|||||||
switch hook {
|
switch hook {
|
||||||
case "startup":
|
case "startup":
|
||||||
if r.startupCallback != nil {
|
if r.startupCallback != nil {
|
||||||
go r.startupCallback(r.runtime)
|
r.startupOnce.Do(func() {
|
||||||
|
go r.startupCallback(r.runtime)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
r.logger.Warning("no startup callback registered!")
|
r.logger.Warning("no startup callback registered!")
|
||||||
}
|
}
|
||||||
@@ -113,11 +113,10 @@ func (r *Runtime) Start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error(err.Error())
|
r.logger.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
case <-r.ctx.Done():
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
r.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -128,15 +127,6 @@ func (r *Runtime) GoRuntime() *runtime.Runtime {
|
|||||||
return r.runtime
|
return r.runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) shutdown() {
|
|
||||||
if r.shutdownCallback != nil {
|
|
||||||
go r.shutdownCallback()
|
|
||||||
} else {
|
|
||||||
r.logger.Warning("no shutdown callback registered!")
|
|
||||||
}
|
|
||||||
r.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
||||||
switch method {
|
switch method {
|
||||||
case "open":
|
case "open":
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ func Build(options *Options) (string, error) {
|
|||||||
builder = newHybridBuilder(options)
|
builder = newHybridBuilder(options)
|
||||||
case "server":
|
case "server":
|
||||||
builder = newServerBuilder(options)
|
builder = newServerBuilder(options)
|
||||||
|
case "dev":
|
||||||
|
builder = newDesktopBuilder(options)
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
|
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,27 +14,28 @@ import (
|
|||||||
|
|
||||||
// App contains options for creating the App
|
// App contains options for creating the App
|
||||||
type App struct {
|
type App struct {
|
||||||
Title string
|
Title string
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
DisableResize bool
|
DisableResize bool
|
||||||
Fullscreen bool
|
Fullscreen bool
|
||||||
MinWidth int
|
MinWidth int
|
||||||
MinHeight int
|
MinHeight int
|
||||||
MaxWidth int
|
MaxWidth int
|
||||||
MaxHeight int
|
MaxHeight int
|
||||||
StartHidden bool
|
StartHidden bool
|
||||||
DevTools bool
|
HideWindowOnClose bool
|
||||||
RGBA int
|
DevTools bool
|
||||||
ContextMenus []*menu.ContextMenu
|
RGBA int
|
||||||
TrayMenus []*menu.TrayMenu
|
ContextMenus []*menu.ContextMenu
|
||||||
Menu *menu.Menu
|
TrayMenus []*menu.TrayMenu
|
||||||
Mac *mac.Options
|
Menu *menu.Menu
|
||||||
Logger logger.Logger `json:"-"`
|
Mac *mac.Options
|
||||||
LogLevel logger.LogLevel
|
Logger logger.Logger `json:"-"`
|
||||||
Startup func(*wailsruntime.Runtime) `json:"-"`
|
LogLevel logger.LogLevel
|
||||||
Shutdown func() `json:"-"`
|
Startup func(*wailsruntime.Runtime) `json:"-"`
|
||||||
Bind []interface{}
|
Shutdown func() `json:"-"`
|
||||||
|
Bind []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeDefaults will set the minimum default values for an application
|
// MergeDefaults will set the minimum default values for an application
|
||||||
|
|||||||
Reference in New Issue
Block a user