Compare commits

...

9 Commits

Author SHA1 Message Date
Lea Anthony
2e9e6a398a chore: bump version 2019-10-24 08:53:54 +11:00
Lea Anthony
cd3865038b fix: linting 2019-10-24 08:52:43 +11:00
Lea Anthony
9e6d6763ec Merge branch 'develop' into 261-korean-fonts 2019-10-23 21:37:13 +11:00
Lea Anthony
e0856b8bfc fix: detect encoding on windows 2019-10-23 21:29:40 +11:00
Lea Anthony
96fc70df26 Merge branch 'master' into develop 2019-10-23 14:07:22 +11:00
Lea Anthony
1c8d4c902a Merge branch 'develop' of github.com:wailsapp/wails into develop 2019-10-23 14:06:19 +11:00
Lea Anthony
08fc1d53d0 chore: version bump 2019-10-23 14:06:14 +11:00
Lea Anthony
6c5d5e40f4 262 add wails shutdown method (#264)
* feat: support close in bridge mode

* feat: WailsShutdown callback

Now handles proper shutdown through:
  * runtime.Window.Close()
  * Killing the main window
  * Ctrl-C
2019-10-23 14:04:41 +11:00
Lea Anthony
d399b7580d Develop (#255)
Hotfix
2019-10-09 19:34:24 +11:00
14 changed files with 190 additions and 31 deletions

39
app.go
View File

@@ -1,6 +1,10 @@
package wails
import (
"os"
"syscall"
"github.com/syossan27/tebata"
"github.com/wailsapp/wails/cmd"
"github.com/wailsapp/wails/lib/binding"
"github.com/wailsapp/wails/lib/event"
@@ -66,6 +70,7 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
// Run the app
func (a *App) Run() error {
if BuildMode != cmd.BuildModeProd {
return a.cli.Run()
}
@@ -97,6 +102,13 @@ func (a *App) start() error {
return err
}
// Start signal handler
t := tebata.New(os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
t.Reserve(func() {
a.log.Debug("SIGNAL CAUGHT! Starting Shutdown")
a.renderer.Close()
})
// Start event manager and give it our renderer
a.eventManager.Start(a.renderer)
@@ -112,8 +124,33 @@ func (a *App) start() error {
return err
}
// Defer the shutdown
defer a.shutdown()
// Run the renderer
return a.renderer.Run()
err = a.renderer.Run()
if err != nil {
return err
}
return nil
}
// shutdown the app
func (a *App) shutdown() {
// Make sure this is only called once
a.log.Debug("Shutting down")
// Shutdown Binding Manager
a.bindingManager.Shutdown()
// Shutdown IPC Manager
a.ipc.Shutdown()
// Shutdown Event Manager
a.eventManager.Shutdown()
a.log.Debug("Cleanly Shutdown")
}
// Bind allows the user to bind the given object

View File

@@ -1,4 +1,4 @@
package cmd
// Version - Wails version
const Version = "v0.18.3"
const Version = "v0.18.7-pre"

View File

@@ -1,6 +1,9 @@
package wails
import "github.com/leaanthony/mewn"
import (
"github.com/leaanthony/mewn"
"github.com/wailsapp/wails/runtime"
)
// AppConfig is the configuration structure used when creating a Wails App object
type AppConfig struct {
@@ -65,7 +68,7 @@ func (a *AppConfig) merge(in *AppConfig) error {
a.CSS = in.CSS
}
if in.Title != "" {
a.Title = in.Title
a.Title = runtime.ProcessEncoding(in.Title)
}
if in.Colour != "" {

5
go.mod
View File

@@ -2,6 +2,7 @@ module github.com/wailsapp/wails
require (
github.com/Masterminds/semver v1.4.2
github.com/abadojack/whatlanggo v1.0.1
github.com/dchest/cssmin v0.0.0-20151210170030-fb8d9b44afdc // indirect
github.com/dchest/htmlmin v0.0.0-20150526090704-e254725e81ac
github.com/dchest/jsmin v0.0.0-20160823214000-faeced883947 // indirect
@@ -22,9 +23,11 @@ require (
github.com/pkg/errors v0.8.1 // indirect
github.com/sirupsen/logrus v1.4.1
github.com/stretchr/testify v1.3.0 // indirect
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 // indirect
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862
golang.org/x/text v0.3.0
gopkg.in/AlecAivazis/survey.v1 v1.8.4
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
)
)

5
go.sum
View File

@@ -2,6 +2,8 @@ github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITg
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/abadojack/whatlanggo v1.0.1 h1:19N6YogDnf71CTHm3Mp2qhYfkRdyvbgwWdd2EPxJRG4=
github.com/abadojack/whatlanggo v1.0.1/go.mod h1:66WiQbSbJBIlOZMsvbKe5m6pzQovxCH9B/K8tQB2uoc=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -68,6 +70,8 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba h1:2DHfQOxcpWdGf5q5IzCUFPNvRX9Icf+09RvQK2VnJq0=
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba/go.mod h1:iLnlXG2Pakcii2CU0cbY07DRCSvpWNa7nFxtevhOChk=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
@@ -83,6 +87,7 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
gopkg.in/AlecAivazis/survey.v1 v1.8.4/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=

View File

@@ -16,6 +16,7 @@ type Manager struct {
functions map[string]*boundFunction
internalMethods *internalMethods
initMethods []*boundMethod
shutdownMethods []*boundMethod
log *logger.CustomLogger
renderer interfaces.Renderer
runtime interfaces.Runtime // The runtime object to pass to bound structs
@@ -127,6 +128,9 @@ func (b *Manager) bindMethod(object interface{}) error {
if newMethod.isWailsInit {
b.log.Debugf("Detected WailsInit function: %s", fullMethodName)
b.initMethods = append(b.initMethods, newMethod)
} else if newMethod.isWailsShutdown {
b.log.Debugf("Detected WailsShutdown function: %s", fullMethodName)
b.shutdownMethods = append(b.shutdownMethods, newMethod)
} else {
// Save boundMethod
b.log.Infof("Bound Method: %s()", fullMethodName)
@@ -292,3 +296,13 @@ func (b *Manager) callWailsInitMethods() error {
}
return nil
}
// Shutdown the binding manager
func (b *Manager) Shutdown() {
b.log.Debug("Shutdown called")
for _, method := range b.shutdownMethods {
b.log.Debugf("Calling Shutdown for method: %s", method.fullName)
method.call("[]")
}
b.log.Debug("Shutdown complete")
}

View File

@@ -18,6 +18,7 @@ type boundMethod struct {
log *logger.CustomLogger
hasErrorReturnType bool // Indicates if there is an error return type
isWailsInit bool
isWailsShutdown bool
}
// Creates a new bound method based on the given method + type
@@ -39,6 +40,11 @@ func newBoundMethod(name string, fullName string, method reflect.Value, objectTy
err = result.processWailsInit()
}
// Are we a WailsShutdown method?
if result.Name == "WailsShutdown" {
err = result.processWailsShutdown()
}
return result, err
}
@@ -211,3 +217,20 @@ func (b *boundMethod) processWailsInit() error {
return nil
}
func (b *boundMethod) processWailsShutdown() error {
// We must have only 1 input, it must be *wails.Runtime
if len(b.inputs) != 0 {
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 inputs, but got %d", len(b.inputs))
}
// We must have only 1 output, it must be error
if len(b.returnTypes) != 0 {
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 return types, but got %d", len(b.returnTypes))
}
// We are indeed a wails Shutdown method
b.isWailsShutdown = true
return nil
}

View File

@@ -3,18 +3,21 @@ package event
import (
"fmt"
"sync"
"time"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/lib/interfaces"
)
// Manager handles and processes events
type Manager struct {
incomingEvents chan *messages.EventData
listeners map[string][]*eventListener
exit bool
running bool
log *logger.CustomLogger
renderer interfaces.Renderer // Messages will be dispatched to the frontend
wg sync.WaitGroup
}
// NewManager creates a new event manager with a 100 event buffer
@@ -22,7 +25,7 @@ func NewManager() interfaces.EventManager {
return &Manager{
incomingEvents: make(chan *messages.EventData, 100),
listeners: make(map[string][]*eventListener),
exit: false,
running: false,
log: logger.NewCustomLogger("Events"),
}
}
@@ -87,15 +90,14 @@ func (e *Manager) Start(renderer interfaces.Renderer) {
// Store renderer
e.renderer = renderer
// Set up waitgroup so we can wait for goroutine to start
var wg sync.WaitGroup
wg.Add(1)
// Set up waitgroup so we can wait for goroutine to quit
e.running = true
e.wg.Add(1)
// Run main loop in separate goroutine
go func() {
wg.Done()
e.log.Info("Listening")
for e.exit == false {
for e.running {
// TODO: Listen for application exit
select {
case event := <-e.incomingEvents:
@@ -139,14 +141,18 @@ func (e *Manager) Start(renderer interfaces.Renderer) {
}
}
}
default:
time.Sleep(1 * time.Millisecond)
}
}
e.wg.Done()
}()
// Wait for goroutine to start
wg.Wait()
}
func (e *Manager) stop() {
e.exit = true
// Shutdown is called when exiting the Application
func (e *Manager) Shutdown() {
e.log.Debug("Shutting Down")
e.running = false
e.log.Debug("Waiting for main loop to exit")
e.wg.Wait()
}

View File

@@ -7,4 +7,5 @@ type BindingManager interface {
Bind(object interface{})
Start(renderer Renderer, runtime Runtime) error
ProcessCall(callData *messages.CallData) (result interface{}, err error)
Shutdown()
}

View File

@@ -8,4 +8,5 @@ type EventManager interface {
Emit(eventName string, optionalData ...interface{})
On(eventName string, callback func(...interface{}))
Start(Renderer)
Shutdown()
}

View File

@@ -5,4 +5,5 @@ type IPCManager interface {
BindRenderer(Renderer)
Dispatch(message string)
Start(eventManager EventManager, bindingManager BindingManager)
Shutdown()
}

View File

@@ -2,6 +2,8 @@ package ipc
import (
"fmt"
"sync"
"time"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
@@ -12,18 +14,20 @@ import (
type Manager struct {
renderer interfaces.Renderer // The renderer
messageQueue chan *ipcMessage
// quitChannel chan struct{}
quitChannel chan struct{}
// signals chan os.Signal
log *logger.CustomLogger
eventManager interfaces.EventManager
bindingManager interfaces.BindingManager
running bool
wg sync.WaitGroup
}
// NewManager creates a new IPC Manager
func NewManager() interfaces.IPCManager {
result := &Manager{
messageQueue: make(chan *ipcMessage, 100),
// quitChannel: make(chan struct{}),
quitChannel: make(chan struct{}),
// signals: make(chan os.Signal, 1),
log: logger.NewCustomLogger("IPC"),
}
@@ -44,9 +48,12 @@ func (i *Manager) Start(eventManager interfaces.EventManager, bindingManager int
i.log.Info("Starting")
// signal.Notify(manager.signals, os.Interrupt)
i.running = true
// Keep track of this goroutine
i.wg.Add(1)
go func() {
running := true
for running {
for i.running {
select {
case incomingMessage := <-i.messageQueue:
i.log.DebugFields("Processing message", logger.Fields{
@@ -117,15 +124,12 @@ func (i *Manager) Start(eventManager interfaces.EventManager, bindingManager int
i.log.DebugFields("Finished processing message", logger.Fields{
"1D": &incomingMessage,
})
// case <-manager.quitChannel:
// Debug("[MessageQueue] Quit caught")
// running = false
// case <-manager.signals:
// Debug("[MessageQueue] Signal caught")
// running = false
default:
time.Sleep(1 * time.Millisecond)
}
}
i.log.Debug("Stopping")
i.wg.Done()
}()
}
@@ -167,3 +171,11 @@ func (i *Manager) SendResponse(response *ipcResponse) error {
// Call back to the front end
return i.renderer.Callback(data)
}
// Shutdown is called when exiting the Application
func (i *Manager) Shutdown() {
i.log.Debug("Shutdown called")
i.running = false
i.log.Debug("Waiting of main loop shutdown")
i.wg.Wait()
}

View File

@@ -156,7 +156,7 @@ func (h *Bridge) Run() error {
h.log.Info("The frontend will connect automatically.")
err := h.server.ListenAndServe()
if err != nil {
if err != nil && err != http.ErrServerClosed {
h.log.Fatal(err.Error())
}
return err
@@ -250,5 +250,9 @@ func (h *Bridge) SetTitle(title string) {
// Close is unsupported for Bridge but required
// for the Renderer interface
func (h *Bridge) Close() {
h.log.Warn("Close() unsupported in bridge mode")
h.log.Debug("Shutting down")
err := h.server.Close()
if err != nil {
h.log.Errorf(err.Error())
}
}

View File

@@ -1,6 +1,54 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
import (
"bytes"
"runtime"
"github.com/abadojack/whatlanggo"
"github.com/wailsapp/wails/lib/interfaces"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/encoding/korean"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
func detectEncoding(text string) (encoding.Encoding, string) {
// korean
var enc encoding.Encoding
info := whatlanggo.Detect(text)
//fmt.Println("Language:", info.Lang.String(), " Script:", whatlanggo.Scripts[info.Script], " Confidence: ", info.Confidence)
switch info.Lang.String() {
case "Korean":
enc = korean.EUCKR
case "Mandarin":
enc = simplifiedchinese.GBK
case "Japanese":
enc = japanese.EUCJP
}
return enc, info.Lang.String()
}
// ProcessEncoding attempts to convert CKJ strings to UTF-8
func ProcessEncoding(text string) string {
if runtime.GOOS != "windows" {
return text
}
encoding, _ := detectEncoding(text)
if encoding != nil {
var bufs bytes.Buffer
wr := transform.NewWriter(&bufs, encoding.NewEncoder())
_, err := wr.Write([]byte(text))
defer wr.Close()
if err != nil {
return ""
}
return bufs.String()
}
return text
}
// Window exposes an interface for manipulating the window
type Window struct {
@@ -31,6 +79,7 @@ func (r *Window) UnFullscreen() {
// SetTitle sets the the window title
func (r *Window) SetTitle(title string) {
title = ProcessEncoding(title)
r.renderer.SetTitle(title)
}