mirror of
https://github.com/taigrr/wails.git
synced 2026-04-17 04:05:12 -07:00
Compare commits
4 Commits
v1.0.2-pre
...
v1.0.2-pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a14cc9a4c | ||
|
|
8a7c098041 | ||
|
|
030bd629df | ||
|
|
cad65f8f3f |
2
app.go
2
app.go
@@ -97,7 +97,7 @@ func (a *App) start() error {
|
|||||||
|
|
||||||
// Check if we are to run in bridge mode
|
// Check if we are to run in bridge mode
|
||||||
if BuildMode == cmd.BuildModeBridge {
|
if BuildMode == cmd.BuildModeBridge {
|
||||||
a.renderer = &renderer.Bridge{}
|
a.renderer = renderer.NewBridge()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the renderer
|
// Initialise the renderer
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
// Version - Wails version
|
// Version - Wails version
|
||||||
const Version = "v1.0.2-pre6"
|
const Version = "v1.0.2-pre8"
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ func (b *Manager) generateTypescriptDefinitions() error {
|
|||||||
var output strings.Builder
|
var output strings.Builder
|
||||||
|
|
||||||
for structname, methodList := range b.structList {
|
for structname, methodList := range b.structList {
|
||||||
|
structname = strings.SplitN(structname, ".", 2)[1]
|
||||||
output.WriteString(fmt.Sprintf("Interface %s {\n", structname))
|
output.WriteString(fmt.Sprintf("Interface %s {\n", structname))
|
||||||
for _, method := range methodList {
|
for _, method := range methodList {
|
||||||
output.WriteString(fmt.Sprintf("\t%s: (...args : any[]) => Promise\n", method))
|
output.WriteString(fmt.Sprintf("\t%s: (...args : any[]) => Promise\n", method))
|
||||||
@@ -125,6 +126,7 @@ func (b *Manager) generateTypescriptDefinitions() error {
|
|||||||
output.WriteString("Interface Backend {\n")
|
output.WriteString("Interface Backend {\n")
|
||||||
|
|
||||||
for structname := range b.structList {
|
for structname := range b.structList {
|
||||||
|
structname = strings.SplitN(structname, ".", 2)[1]
|
||||||
output.WriteString(fmt.Sprintf("\t%[1]s: %[1]s\n", structname))
|
output.WriteString(fmt.Sprintf("\t%[1]s: %[1]s\n", structname))
|
||||||
}
|
}
|
||||||
output.WriteString("}\n")
|
output.WriteString("}\n")
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package interfaces
|
package interfaces
|
||||||
|
|
||||||
|
// CallbackFunc defines the signature of a function required to be provided to the
|
||||||
|
// Dispatch function so that the response may be returned
|
||||||
|
type CallbackFunc func(string) error
|
||||||
|
|
||||||
// IPCManager is the event manager interface
|
// IPCManager is the event manager interface
|
||||||
type IPCManager interface {
|
type IPCManager interface {
|
||||||
BindRenderer(Renderer)
|
BindRenderer(Renderer)
|
||||||
Dispatch(message string)
|
Dispatch(message string, f CallbackFunc)
|
||||||
Start(eventManager EventManager, bindingManager BindingManager)
|
Start(eventManager EventManager, bindingManager BindingManager)
|
||||||
Shutdown()
|
Shutdown()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ type Renderer interface {
|
|||||||
|
|
||||||
// Binding
|
// Binding
|
||||||
NewBinding(bindingName string) error
|
NewBinding(bindingName string) error
|
||||||
Callback(data string) error
|
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
NotifyEvent(eventData *messages.EventData) error
|
NotifyEvent(eventData *messages.EventData) error
|
||||||
|
|||||||
@@ -135,10 +135,10 @@ func (i *Manager) Start(eventManager interfaces.EventManager, bindingManager int
|
|||||||
// Dispatch receives JSON encoded messages from the renderer.
|
// Dispatch receives JSON encoded messages from the renderer.
|
||||||
// It processes the message to ensure that it is valid and places
|
// It processes the message to ensure that it is valid and places
|
||||||
// the processed message on the message queue
|
// the processed message on the message queue
|
||||||
func (i *Manager) Dispatch(message string) {
|
func (i *Manager) Dispatch(message string, cb interfaces.CallbackFunc) {
|
||||||
|
|
||||||
// Create a new IPC Message
|
// Create a new IPC Message
|
||||||
incomingMessage, err := newIPCMessage(message, i.SendResponse)
|
incomingMessage, err := newIPCMessage(message, i.SendResponse(cb))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i.log.ErrorFields("Could not understand incoming message! ", map[string]interface{}{
|
i.log.ErrorFields("Could not understand incoming message! ", map[string]interface{}{
|
||||||
"message": message,
|
"message": message,
|
||||||
@@ -158,17 +158,19 @@ func (i *Manager) Dispatch(message string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SendResponse sends the given response back to the frontend
|
// SendResponse sends the given response back to the frontend
|
||||||
func (i *Manager) SendResponse(response *ipcResponse) error {
|
// It sends the data back to the correct renderer by way of the provided callback function
|
||||||
|
func (i *Manager) SendResponse(cb interfaces.CallbackFunc) func(i *ipcResponse) error {
|
||||||
|
|
||||||
// Serialise the Message
|
return func(response *ipcResponse) error {
|
||||||
data, err := response.Serialise()
|
// Serialise the Message
|
||||||
if err != nil {
|
data, err := response.Serialise()
|
||||||
fmt.Printf(err.Error())
|
if err != nil {
|
||||||
return err
|
fmt.Printf(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cb(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call back to the front end
|
|
||||||
return i.renderer.Callback(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown is called when exiting the Application
|
// Shutdown is called when exiting the Application
|
||||||
|
|||||||
@@ -1,262 +1,10 @@
|
|||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
bridge "github.com/wailsapp/wails/lib/renderer/bridge"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/dchest/htmlmin"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/leaanthony/mewn"
|
|
||||||
"github.com/wailsapp/wails/lib/interfaces"
|
|
||||||
"github.com/wailsapp/wails/lib/logger"
|
|
||||||
"github.com/wailsapp/wails/lib/messages"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type messageType int
|
// NewBridge returns a new Bridge struct
|
||||||
|
func NewBridge() *bridge.Bridge {
|
||||||
const (
|
return &bridge.Bridge{}
|
||||||
jsMessage messageType = iota
|
|
||||||
cssMessage
|
|
||||||
htmlMessage
|
|
||||||
notifyMessage
|
|
||||||
bindingMessage
|
|
||||||
callbackMessage
|
|
||||||
wailsRuntimeMessage
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m messageType) toString() string {
|
|
||||||
return [...]string{"j", "s", "h", "n", "b", "c", "w"}[m]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bridge is a backend that opens a local web server
|
|
||||||
// and renders the files over a websocket
|
|
||||||
type Bridge struct {
|
|
||||||
// Common
|
|
||||||
log *logger.CustomLogger
|
|
||||||
ipcManager interfaces.IPCManager
|
|
||||||
appConfig interfaces.AppConfig
|
|
||||||
eventManager interfaces.EventManager
|
|
||||||
bindingCache []string
|
|
||||||
|
|
||||||
// Bridge specific
|
|
||||||
initialisationJS []string
|
|
||||||
server *http.Server
|
|
||||||
theConnection *websocket.Conn
|
|
||||||
|
|
||||||
// Mutex for writing to the socket
|
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise the Bridge Renderer
|
|
||||||
func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error {
|
|
||||||
h.ipcManager = ipcManager
|
|
||||||
h.appConfig = appConfig
|
|
||||||
h.eventManager = eventManager
|
|
||||||
ipcManager.BindRenderer(h)
|
|
||||||
h.log = logger.NewCustomLogger("Bridge")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Bridge) evalJS(js string, mtype messageType) error {
|
|
||||||
|
|
||||||
message := mtype.toString() + js
|
|
||||||
|
|
||||||
if h.theConnection == nil {
|
|
||||||
h.initialisationJS = append(h.initialisationJS, message)
|
|
||||||
} else {
|
|
||||||
// Prepend message type to message
|
|
||||||
h.sendMessage(h.theConnection, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableConsole not needed for bridge!
|
|
||||||
func (h *Bridge) EnableConsole() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Bridge) injectCSS(css string) {
|
|
||||||
// Minify css to overcome issues in the browser with carriage returns
|
|
||||||
minified, err := htmlmin.Minify([]byte(css), &htmlmin.Options{
|
|
||||||
MinifyStyles: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
h.log.Fatal("Unable to minify CSS: " + css)
|
|
||||||
}
|
|
||||||
minifiedCSS := string(minified)
|
|
||||||
minifiedCSS = strings.Replace(minifiedCSS, "\\", "\\\\", -1)
|
|
||||||
minifiedCSS = strings.Replace(minifiedCSS, "'", "\\'", -1)
|
|
||||||
minifiedCSS = strings.Replace(minifiedCSS, "\n", " ", -1)
|
|
||||||
inject := fmt.Sprintf("wails._.InjectCSS('%s')", minifiedCSS)
|
|
||||||
h.evalJS(inject, cssMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
h.theConnection = conn
|
|
||||||
h.log.Infof("Connection from frontend accepted [%p].", h.theConnection)
|
|
||||||
conn.SetCloseHandler(func(int, string) error {
|
|
||||||
h.log.Infof("Connection dropped [%p].", h.theConnection)
|
|
||||||
h.theConnection = nil
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
go h.start(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Bridge) sendMessage(conn *websocket.Conn, msg string) {
|
|
||||||
|
|
||||||
h.lock.Lock()
|
|
||||||
defer h.lock.Unlock()
|
|
||||||
|
|
||||||
if err := conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
|
|
||||||
h.log.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Bridge) start(conn *websocket.Conn) {
|
|
||||||
|
|
||||||
// set external.invoke
|
|
||||||
h.log.Infof("Connected to frontend.")
|
|
||||||
|
|
||||||
wailsRuntime := mewn.String("../../runtime/assets/wails.js")
|
|
||||||
h.evalJS(wailsRuntime, wailsRuntimeMessage)
|
|
||||||
|
|
||||||
// Inject bindings
|
|
||||||
for _, binding := range h.bindingCache {
|
|
||||||
h.evalJS(binding, bindingMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit that everything is loaded and ready
|
|
||||||
h.eventManager.Emit("wails:ready")
|
|
||||||
|
|
||||||
for {
|
|
||||||
messageType, buffer, err := conn.ReadMessage()
|
|
||||||
if messageType == -1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
h.log.Errorf("Error reading message: ", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
h.log.Debugf("Got message: %#v\n", string(buffer))
|
|
||||||
|
|
||||||
h.ipcManager.Dispatch(string(buffer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the app in Bridge mode!
|
|
||||||
func (h *Bridge) Run() error {
|
|
||||||
h.server = &http.Server{Addr: ":34115"}
|
|
||||||
http.HandleFunc("/bridge", h.wsBridgeHandler)
|
|
||||||
|
|
||||||
h.log.Info("Bridge mode started.")
|
|
||||||
h.log.Info("The frontend will connect automatically.")
|
|
||||||
|
|
||||||
err := h.server.ListenAndServe()
|
|
||||||
if err != nil && err != http.ErrServerClosed {
|
|
||||||
h.log.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBinding creates a new binding with the frontend
|
|
||||||
func (h *Bridge) NewBinding(methodName string) error {
|
|
||||||
h.bindingCache = append(h.bindingCache, methodName)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectFile is unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) SelectFile() string {
|
|
||||||
h.log.Warn("SelectFile() unsupported in bridge mode")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectDirectory is unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) SelectDirectory() string {
|
|
||||||
h.log.Warn("SelectDirectory() unsupported in bridge mode")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectSaveFile is unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) SelectSaveFile() string {
|
|
||||||
h.log.Warn("SelectSaveFile() unsupported in bridge mode")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Callback sends a callback to the frontend
|
|
||||||
func (h *Bridge) Callback(data string) error {
|
|
||||||
return h.evalJS(data, callbackMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyEvent notifies the frontend of an event
|
|
||||||
func (h *Bridge) NotifyEvent(event *messages.EventData) error {
|
|
||||||
|
|
||||||
// Look out! Nils about!
|
|
||||||
var err error
|
|
||||||
if event == nil {
|
|
||||||
err = fmt.Errorf("Sent nil event to renderer.webViewRenderer")
|
|
||||||
h.log.Error(err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default data is a blank array
|
|
||||||
data := []byte("[]")
|
|
||||||
|
|
||||||
// Process event data
|
|
||||||
if event.Data != nil {
|
|
||||||
// Marshall the data
|
|
||||||
data, err = json.Marshal(event.Data)
|
|
||||||
if err != nil {
|
|
||||||
h.log.Errorf("Cannot unmarshall JSON data in event: %s ", err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data)
|
|
||||||
return h.evalJS(message, notifyMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetColour is unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) SetColour(colour string) error {
|
|
||||||
h.log.WarnFields("SetColour ignored for Bridge more", logger.Fields{"col": colour})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fullscreen is unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) Fullscreen() {
|
|
||||||
h.log.Warn("Fullscreen() unsupported in bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnFullscreen is unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) UnFullscreen() {
|
|
||||||
h.log.Warn("UnFullscreen() unsupported in bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTitle is currently unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) SetTitle(title string) {
|
|
||||||
h.log.WarnFields("SetTitle() unsupported in bridge mode", logger.Fields{"title": title})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close is unsupported for Bridge but required
|
|
||||||
// for the Renderer interface
|
|
||||||
func (h *Bridge) Close() {
|
|
||||||
h.log.Debug("Shutting down")
|
|
||||||
err := h.server.Close()
|
|
||||||
if err != nil {
|
|
||||||
h.log.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
214
lib/renderer/bridge/bridge.go
Normal file
214
lib/renderer/bridge/bridge.go
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
package renderer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/wailsapp/wails/lib/interfaces"
|
||||||
|
"github.com/wailsapp/wails/lib/logger"
|
||||||
|
"github.com/wailsapp/wails/lib/messages"
|
||||||
|
)
|
||||||
|
|
||||||
|
type messageType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
jsMessage messageType = iota
|
||||||
|
cssMessage
|
||||||
|
htmlMessage
|
||||||
|
notifyMessage
|
||||||
|
bindingMessage
|
||||||
|
callbackMessage
|
||||||
|
wailsRuntimeMessage
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m messageType) toString() string {
|
||||||
|
return [...]string{"j", "s", "h", "n", "b", "c", "w"}[m]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bridge is a backend that opens a local web server
|
||||||
|
// and renders the files over a websocket
|
||||||
|
type Bridge struct {
|
||||||
|
// Common
|
||||||
|
log *logger.CustomLogger
|
||||||
|
ipcManager interfaces.IPCManager
|
||||||
|
appConfig interfaces.AppConfig
|
||||||
|
eventManager interfaces.EventManager
|
||||||
|
bindingCache []string
|
||||||
|
|
||||||
|
// Bridge specific
|
||||||
|
server *http.Server
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
sessions map[string]session
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise the Bridge Renderer
|
||||||
|
func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error {
|
||||||
|
h.sessions = map[string]session{}
|
||||||
|
h.ipcManager = ipcManager
|
||||||
|
h.appConfig = appConfig
|
||||||
|
h.eventManager = eventManager
|
||||||
|
ipcManager.BindRenderer(h)
|
||||||
|
h.log = logger.NewCustomLogger("Bridge")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableConsole not needed for bridge!
|
||||||
|
func (h *Bridge) EnableConsole() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
h.log.Infof("Connection from frontend accepted [%s].", conn.RemoteAddr().String())
|
||||||
|
h.startSession(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Bridge) startSession(conn *websocket.Conn) {
|
||||||
|
s := session{
|
||||||
|
conn: conn,
|
||||||
|
bindingCache: h.bindingCache,
|
||||||
|
ipc: h.ipcManager,
|
||||||
|
log: h.log,
|
||||||
|
eventManager: h.eventManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.SetCloseHandler(func(int, string) error {
|
||||||
|
h.log.Infof("Connection dropped [%s].", s.Identifier())
|
||||||
|
h.eventManager.Emit("wails:bridge:session:closed", s.Identifier())
|
||||||
|
h.lock.Lock()
|
||||||
|
defer h.lock.Unlock()
|
||||||
|
delete(h.sessions, s.Identifier())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
h.lock.Lock()
|
||||||
|
defer h.lock.Unlock()
|
||||||
|
go s.start(len(h.sessions) == 0)
|
||||||
|
h.sessions[s.Identifier()] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the app in Bridge mode!
|
||||||
|
func (h *Bridge) Run() error {
|
||||||
|
h.server = &http.Server{Addr: ":34115"}
|
||||||
|
http.HandleFunc("/bridge", h.wsBridgeHandler)
|
||||||
|
|
||||||
|
h.log.Info("Bridge mode started.")
|
||||||
|
h.log.Info("The frontend will connect automatically.")
|
||||||
|
|
||||||
|
err := h.server.ListenAndServe()
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
h.log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBinding creates a new binding with the frontend
|
||||||
|
func (h *Bridge) NewBinding(methodName string) error {
|
||||||
|
h.bindingCache = append(h.bindingCache, methodName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectFile is unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) SelectFile() string {
|
||||||
|
h.log.Warn("SelectFile() unsupported in bridge mode")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectDirectory is unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) SelectDirectory() string {
|
||||||
|
h.log.Warn("SelectDirectory() unsupported in bridge mode")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectSaveFile is unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) SelectSaveFile() string {
|
||||||
|
h.log.Warn("SelectSaveFile() unsupported in bridge mode")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyEvent notifies the frontend of an event
|
||||||
|
func (h *Bridge) NotifyEvent(event *messages.EventData) error {
|
||||||
|
|
||||||
|
// Look out! Nils about!
|
||||||
|
var err error
|
||||||
|
if event == nil {
|
||||||
|
err = fmt.Errorf("Sent nil event to renderer.webViewRenderer")
|
||||||
|
h.log.Error(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default data is a blank array
|
||||||
|
data := []byte("[]")
|
||||||
|
|
||||||
|
// Process event data
|
||||||
|
if event.Data != nil {
|
||||||
|
// Marshall the data
|
||||||
|
data, err = json.Marshal(event.Data)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Errorf("Cannot unmarshall JSON data in event: %s ", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data)
|
||||||
|
dead := []session{}
|
||||||
|
for _, session := range h.sessions {
|
||||||
|
err := session.evalJS(message, notifyMessage)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Debugf("Failed to send message to %s - Removing listener : %v", session.Identifier(), err)
|
||||||
|
h.log.Infof("Connection from [%v] unresponsive - dropping", session.Identifier())
|
||||||
|
dead = append(dead, session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.lock.Lock()
|
||||||
|
defer h.lock.Unlock()
|
||||||
|
for _, session := range dead {
|
||||||
|
delete(h.sessions, session.Identifier())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColour is unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) SetColour(colour string) error {
|
||||||
|
h.log.WarnFields("SetColour ignored for Bridge more", logger.Fields{"col": colour})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fullscreen is unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) Fullscreen() {
|
||||||
|
h.log.Warn("Fullscreen() unsupported in bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnFullscreen is unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) UnFullscreen() {
|
||||||
|
h.log.Warn("UnFullscreen() unsupported in bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle is currently unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) SetTitle(title string) {
|
||||||
|
h.log.WarnFields("SetTitle() unsupported in bridge mode", logger.Fields{"title": title})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is unsupported for Bridge but required
|
||||||
|
// for the Renderer interface
|
||||||
|
func (h *Bridge) Close() {
|
||||||
|
h.log.Debug("Shutting down")
|
||||||
|
err := h.server.Close()
|
||||||
|
if err != nil {
|
||||||
|
h.log.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
90
lib/renderer/bridge/session.go
Normal file
90
lib/renderer/bridge/session.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package renderer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/leaanthony/mewn"
|
||||||
|
"github.com/wailsapp/wails/lib/interfaces"
|
||||||
|
"github.com/wailsapp/wails/lib/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO Move this back into bridge.go
|
||||||
|
|
||||||
|
// session represents a single websocket session
|
||||||
|
type session struct {
|
||||||
|
bindingCache []string
|
||||||
|
conn *websocket.Conn
|
||||||
|
eventManager interfaces.EventManager
|
||||||
|
log *logger.CustomLogger
|
||||||
|
ipc interfaces.IPCManager
|
||||||
|
|
||||||
|
// Mutex for writing to the socket
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
s.lock.Lock()
|
||||||
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
|
if err := s.conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
|
||||||
|
s.log.Debug(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) start(firstSession bool) {
|
||||||
|
s.log.Infof("Connected to frontend.")
|
||||||
|
|
||||||
|
wailsRuntime := mewn.String("../../runtime/assets/wails.js")
|
||||||
|
s.evalJS(wailsRuntime, wailsRuntimeMessage)
|
||||||
|
|
||||||
|
// Inject bindings
|
||||||
|
for _, binding := range s.bindingCache {
|
||||||
|
s.evalJS(binding, bindingMessage)
|
||||||
|
}
|
||||||
|
s.eventManager.Emit("wails:bridge:session:started", s.Identifier())
|
||||||
|
|
||||||
|
// Emit that everything is loaded and ready
|
||||||
|
if firstSession {
|
||||||
|
s.eventManager.Emit("wails:ready")
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
messageType, buffer, err := s.conn.ReadMessage()
|
||||||
|
if messageType == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorf("Error reading message: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.Debugf("Got message: %#v\n", string(buffer))
|
||||||
|
|
||||||
|
s.ipc.Dispatch(string(buffer), s.Callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback sends a callback to the frontend
|
||||||
|
func (s *session) Callback(data string) error {
|
||||||
|
return s.evalJS(data, callbackMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) evalJS(js string, mtype messageType) error {
|
||||||
|
|
||||||
|
// Prepend message type to message
|
||||||
|
return s.sendMessage(mtype.toString() + js)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -58,7 +58,7 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana
|
|||||||
URL: config.GetDefaultHTML(),
|
URL: config.GetDefaultHTML(),
|
||||||
Debug: !config.GetDisableInspector(),
|
Debug: !config.GetDisableInspector(),
|
||||||
ExternalInvokeCallback: func(_ wv.WebView, message string) {
|
ExternalInvokeCallback: func(_ wv.WebView, message string) {
|
||||||
w.ipc.Dispatch(message)
|
w.ipc.Dispatch(message, w.callback)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -299,8 +299,8 @@ func (w *WebView) SelectSaveFile() string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback sends a callback to the frontend
|
// callback sends a callback to the frontend
|
||||||
func (w *WebView) Callback(data string) error {
|
func (w *WebView) callback(data string) error {
|
||||||
callbackCMD := fmt.Sprintf("window.wails._.Callback('%s');", data)
|
callbackCMD := fmt.Sprintf("window.wails._.Callback('%s');", data)
|
||||||
return w.evalJS(callbackCMD)
|
return w.evalJS(callbackCMD)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user