mirror of
https://github.com/taigrr/wails.git
synced 2026-04-03 05:38:56 -07:00
Compare commits
1 Commits
v2-linux
...
419-better
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d442947f3 |
6
.github/workflows/latest-pre.yml
vendored
6
.github/workflows/latest-pre.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.12
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.12
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -27,6 +27,6 @@ jobs:
|
||||
go get -v -d ./...
|
||||
- name: Build
|
||||
run: go build -v ./cmd/wails
|
||||
|
||||
|
||||
- name: Test
|
||||
run: ./wails version
|
||||
|
||||
6
.github/workflows/pr.yml
vendored
6
.github/workflows/pr.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.12
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.12
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -27,6 +27,6 @@ jobs:
|
||||
go get -v -d ./...
|
||||
- name: Build
|
||||
run: go build -v ./cmd/wails
|
||||
|
||||
|
||||
- name: Test
|
||||
run: ./wails version
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '!**pre**'
|
||||
- '!**pre**'
|
||||
jobs:
|
||||
|
||||
build:
|
||||
@@ -15,10 +15,10 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.13
|
||||
- name: Set up Go 1.12
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.12
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -29,6 +29,6 @@ jobs:
|
||||
go get -v -d ./...
|
||||
- name: Build
|
||||
run: go build -v ./cmd/wails
|
||||
|
||||
|
||||
- name: Test
|
||||
run: ./wails version
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -16,13 +16,4 @@ examples/**/example*
|
||||
cmd/wails/wails
|
||||
.DS_Store
|
||||
tmp
|
||||
node_modules/
|
||||
package.json.md5
|
||||
v2/test/**/frontend/dist
|
||||
v2/test/**/build/
|
||||
v2/test/frameless/icon.png
|
||||
v2/test/hidden/icon.png
|
||||
v2/internal/ffenestri/runtime.c
|
||||
v2/internal/runtime/assets/desktop.js
|
||||
v2/test/kitchensink/frontend/public/bundle.*
|
||||
v2/pkg/parser/testproject/frontend/wails
|
||||
node_modules/
|
||||
@@ -15,6 +15,7 @@ Wails is what it is because of the time and effort given by these great people.
|
||||
* [admin_3.exe](https://github.com/bh90210)
|
||||
* [iceleo-com](https://github.com/iceleo-com)
|
||||
* [fallendusk](https://github.com/fallendusk)
|
||||
* [Florian Didran](https://github.com/fdidron)
|
||||
* [Nikolai Zimmermann](https://github.com/Chronophylos)
|
||||
* [Toyam Cox](https://github.com/Vaelatern)
|
||||
* [Robin Eklind](https://github.com/mewmew)
|
||||
@@ -25,10 +26,4 @@ Wails is what it is because of the time and effort given by these great people.
|
||||
* [Reuben Thomas-Davis](https://github.com/Rested)
|
||||
* [Jarek](https://github.com/Jarek-SRT)
|
||||
* [Konez2k](https://github.com/konez2k)
|
||||
* [msms](https://github.com/sayuthisobri)
|
||||
* [dedo1911](https://github.com/dedo1911)
|
||||
* [Florian Didron](https://github.com/fdidron)
|
||||
* [Christopher Murphy](https://github.com/Splode)
|
||||
* [Zámbó, Levente](https://github.com/Lyimmi)
|
||||
* [artem](https://github.com/Unix4ever)
|
||||
* [Tim Kipp](https://github.com/timkippdev)
|
||||
* [msms](https://github.com/sayuthisobri)
|
||||
@@ -36,7 +36,7 @@ The official docs can be found at [https://wails.app](https://wails.app).
|
||||
|
||||
Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as an installation of Go. The basic requirements are:
|
||||
|
||||
- Go 1.13
|
||||
- Go 1.12
|
||||
- npm
|
||||
|
||||
### MacOS
|
||||
@@ -55,7 +55,7 @@ _Debian: 8, 9, 10_
|
||||
|
||||
_Ubuntu: 16.04, 18.04, 19.04_
|
||||
|
||||
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!_OS
|
||||
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_
|
||||
|
||||
#### Arch Linux / ArchLabs
|
||||
|
||||
|
||||
@@ -293,11 +293,9 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
// Removed by popular demand
|
||||
// TODO: Potentially add a flag to cleanup
|
||||
// if projectOptions.Platform == "windows" {
|
||||
// helper.CleanWindows(projectOptions)
|
||||
// }
|
||||
if projectOptions.Platform == "windows" {
|
||||
helper.CleanWindows(projectOptions)
|
||||
}
|
||||
}()
|
||||
|
||||
if projectOptions.CrossCompile {
|
||||
@@ -563,11 +561,6 @@ func ldFlags(po *ProjectOptions, buildMode string) string {
|
||||
|
||||
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
|
||||
|
||||
// Add additional ldflags passed in via the `ldflags` cli flag
|
||||
if len(po.LdFlags) > 0 {
|
||||
ldflags += " " + po.LdFlags
|
||||
}
|
||||
|
||||
// If we wish to generate typescript
|
||||
if po.typescriptDefsFilename != "" {
|
||||
cwd, err := os.Getwd()
|
||||
|
||||
27
cmd/linux.go
27
cmd/linux.go
@@ -59,10 +59,6 @@ const (
|
||||
Leap
|
||||
// ArchLabs distribution
|
||||
ArchLabs
|
||||
// PopOS distribution
|
||||
PopOS
|
||||
// Solus distribution
|
||||
Solus
|
||||
)
|
||||
|
||||
// DistroInfo contains all the information relating to a linux distribution
|
||||
@@ -111,7 +107,7 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
}
|
||||
switch splitLine[0] {
|
||||
case "ID":
|
||||
osID = strings.ToLower(strings.Trim(splitLine[1], "\""))
|
||||
osID = strings.Trim(splitLine[1], "\"")
|
||||
case "NAME":
|
||||
osNAME = strings.Trim(splitLine[1], "\"")
|
||||
case "VERSION_ID":
|
||||
@@ -120,7 +116,7 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
}
|
||||
|
||||
// Check distro name against list of distros
|
||||
switch osID {
|
||||
switch strings.ToLower(osID) {
|
||||
case "fedora":
|
||||
result.Distribution = Fedora
|
||||
case "centos":
|
||||
@@ -163,10 +159,6 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
result.Distribution = Tumbleweed
|
||||
case "opensuse-leap":
|
||||
result.Distribution = Leap
|
||||
case "pop":
|
||||
result.Distribution = PopOS
|
||||
case "solus":
|
||||
result.Distribution = Solus
|
||||
default:
|
||||
result.Distribution = Unknown
|
||||
}
|
||||
@@ -203,17 +195,6 @@ func DpkgInstalled(packageName string) (bool, error) {
|
||||
return exitCode == 0, nil
|
||||
}
|
||||
|
||||
// EOpkgInstalled uses dpkg to see if a package is installed
|
||||
func EOpkgInstalled(packageName string) (bool, error) {
|
||||
program := NewProgramHelper()
|
||||
eopkg := program.FindProgram("eopkg")
|
||||
if eopkg == nil {
|
||||
return false, fmt.Errorf("cannot check dependencies: eopkg not found")
|
||||
}
|
||||
stdout, _, _, _ := eopkg.Run("info", packageName)
|
||||
return strings.HasPrefix(stdout, "Installed"), nil
|
||||
}
|
||||
|
||||
// PacmanInstalled uses pacman to see if a package is installed.
|
||||
func PacmanInstalled(packageName string) (bool, error) {
|
||||
program := NewProgramHelper()
|
||||
@@ -285,9 +266,5 @@ func RequestSupportForDistribution(distroInfo *DistroInfo) error {
|
||||
|
||||
fmt.Println("Opening browser to file request.")
|
||||
browser.OpenURL(fullURL + url.PathEscape(params))
|
||||
result = Prompt("We have a guide for adding support for your distribution. Would you like to view it?", "yes")
|
||||
if strings.ToLower(result) == "yes" {
|
||||
browser.OpenURL("https://wails.app/guides/distro/")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,15 +28,6 @@ distributions:
|
||||
gccversioncommand: &gccdumpfullversion -dumpfullversion
|
||||
programs: *debiandefaultprograms
|
||||
libraries: *debiandefaultlibraries
|
||||
pop:
|
||||
id: pop
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: Pop!_OS
|
||||
gccversioncommand: &gccdumpfullversion -dumpfullversion
|
||||
programs: *debiandefaultprograms
|
||||
libraries: *debiandefaultlibraries
|
||||
kali:
|
||||
id: kali
|
||||
releases:
|
||||
@@ -241,25 +232,6 @@ distributions:
|
||||
gccversioncommand: *gccdumpfullversion
|
||||
programs: *debiandefaultprograms
|
||||
libraries: *debiandefaultlibraries
|
||||
solus:
|
||||
id: solus
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: Solus
|
||||
gccversioncommand: *gccdumpfullversion
|
||||
programs: &solusdefaultprograms
|
||||
- name: gcc
|
||||
help: Please install with `sudo eopkg it -c system.devel` and try again
|
||||
- name: pkg-config
|
||||
help: Please install with `sudo eopkg it -c system.devel` and try again
|
||||
- name: npm
|
||||
help: Please install with `sudo eopkg it nodejs` and try again
|
||||
libraries: &solusdefaultlibraries
|
||||
- name: libgtk-3-devel
|
||||
help: Please install with `sudo eopkg it libgtk-3-devel` and try again
|
||||
- name: libwebkit-gtk-devel
|
||||
help: Please install with `sudo eopkg it libwebkit-gtk-devel` and try again
|
||||
|
||||
opensuse-tumbleweed:
|
||||
id: opensuse-tumbleweed
|
||||
|
||||
187
cmd/package.go
187
cmd/package.go
@@ -1,12 +1,9 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
@@ -17,7 +14,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jackmordaunt/icns"
|
||||
"golang.org/x/image/draw"
|
||||
)
|
||||
|
||||
// PackageHelper helps with the 'wails package' command
|
||||
@@ -59,111 +55,6 @@ func newPlistData(title, exe, packageID, version, author string) *plistData {
|
||||
}
|
||||
}
|
||||
|
||||
type windowsIcoHeader struct {
|
||||
_ uint16
|
||||
imageType uint16
|
||||
imageCount uint16
|
||||
}
|
||||
|
||||
type windowsIcoDescriptor struct {
|
||||
width uint8
|
||||
height uint8
|
||||
colours uint8
|
||||
_ uint8
|
||||
planes uint16
|
||||
bpp uint16
|
||||
size uint32
|
||||
offset uint32
|
||||
}
|
||||
|
||||
type windowsIcoContainer struct {
|
||||
Header windowsIcoDescriptor
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func generateWindowsIcon(pngFilename string, iconfile string) error {
|
||||
sizes := []int{256, 128, 64, 48, 32, 16}
|
||||
|
||||
pngfile, err := os.Open(pngFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer pngfile.Close()
|
||||
|
||||
pngdata, err := png.Decode(pngfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
icons := []windowsIcoContainer{}
|
||||
|
||||
for _, size := range sizes {
|
||||
rect := image.Rect(0, 0, int(size), int(size))
|
||||
rawdata := image.NewRGBA(rect)
|
||||
scale := draw.CatmullRom
|
||||
scale.Scale(rawdata, rect, pngdata, pngdata.Bounds(), draw.Over, nil)
|
||||
|
||||
icondata := new(bytes.Buffer)
|
||||
writer := bufio.NewWriter(icondata)
|
||||
err = png.Encode(writer, rawdata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writer.Flush()
|
||||
|
||||
imgSize := size
|
||||
if imgSize >= 256 {
|
||||
imgSize = 0
|
||||
}
|
||||
|
||||
data := icondata.Bytes()
|
||||
|
||||
icn := windowsIcoContainer{
|
||||
Header: windowsIcoDescriptor{
|
||||
width: uint8(imgSize),
|
||||
height: uint8(imgSize),
|
||||
planes: 1,
|
||||
bpp: 32,
|
||||
size: uint32(len(data)),
|
||||
},
|
||||
Data: data,
|
||||
}
|
||||
icons = append(icons, icn)
|
||||
}
|
||||
|
||||
outfile, err := os.Create(iconfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outfile.Close()
|
||||
|
||||
ico := windowsIcoHeader{
|
||||
imageType: 1,
|
||||
imageCount: uint16(len(sizes)),
|
||||
}
|
||||
err = binary.Write(outfile, binary.LittleEndian, ico)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offset := uint32(6 + 16*len(sizes))
|
||||
for _, icon := range icons {
|
||||
icon.Header.offset = offset
|
||||
err = binary.Write(outfile, binary.LittleEndian, icon.Header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset += icon.Header.size
|
||||
}
|
||||
for _, icon := range icons {
|
||||
_, err = outfile.Write(icon.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultString(val string, defaultVal string) string {
|
||||
if val != "" {
|
||||
return val
|
||||
@@ -212,8 +103,6 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
|
||||
packageID := strings.Join([]string{"wails", name, version}, ".")
|
||||
plistData := newPlistData(name, exe, packageID, version, author)
|
||||
appname := po.Name + ".app"
|
||||
plistFilename := path.Join(build, appname, "Contents", "Info.plist")
|
||||
customPlist := path.Join(b.fs.Cwd(), "info.plist")
|
||||
|
||||
// Check binary exists
|
||||
source := path.Join(build, exe)
|
||||
@@ -232,48 +121,28 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
|
||||
// Remove the existing package
|
||||
os.RemoveAll(appname)
|
||||
|
||||
// Create directories
|
||||
exeDir := path.Join(build, appname, "/Contents/MacOS")
|
||||
b.fs.MkDirs(exeDir, 0755)
|
||||
resourceDir := path.Join(build, appname, "/Contents/Resources")
|
||||
b.fs.MkDirs(resourceDir, 0755)
|
||||
tmpl := template.New("infoPlist")
|
||||
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
|
||||
infoPlist, err := ioutil.ReadFile(plistFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl.Parse(string(infoPlist))
|
||||
|
||||
// Do we have a custom plist in the project directory?
|
||||
if !fs.FileExists(customPlist) {
|
||||
|
||||
// No - create a new plist from our defaults
|
||||
tmpl := template.New("infoPlist")
|
||||
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
|
||||
infoPlist, err := ioutil.ReadFile(plistFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl.Parse(string(infoPlist))
|
||||
|
||||
// Write the template to a buffer
|
||||
var tpl bytes.Buffer
|
||||
err = tmpl.Execute(&tpl, plistData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save to the package
|
||||
err = ioutil.WriteFile(plistFilename, tpl.Bytes(), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Also write to project directory for customisation
|
||||
err = ioutil.WriteFile(customPlist, tpl.Bytes(), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Yes - we have a plist. Copy it to the package verbatim
|
||||
err = fs.CopyFile(customPlist, plistFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Write the template to a buffer
|
||||
var tpl bytes.Buffer
|
||||
err = tmpl.Execute(&tpl, plistData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filename := path.Join(build, appname, "Contents", "Info.plist")
|
||||
err = ioutil.WriteFile(filename, tpl.Bytes(), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy executable
|
||||
@@ -308,16 +177,14 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
|
||||
outputDir := b.fs.Cwd()
|
||||
basename := strings.TrimSuffix(po.BinaryName, ".exe")
|
||||
|
||||
// Copy default icon if needed
|
||||
icon, err := b.copyIcon()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate icon from PNG
|
||||
err = generateWindowsIcon(icon, basename+".ico")
|
||||
if err != nil {
|
||||
return err
|
||||
// Copy icon
|
||||
tgtIconFile := filepath.Join(outputDir, basename+".ico")
|
||||
if !b.fs.FileExists(tgtIconFile) {
|
||||
srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
|
||||
err := b.fs.CopyFile(srcIconfile, tgtIconFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy manifest
|
||||
@@ -376,7 +243,7 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *PackageHelper) copyIcon() (string, error) {
|
||||
func (b *PackageHelper) copyIcon(resourceDir string) (string, error) {
|
||||
|
||||
// TODO: Read this from project.json
|
||||
const appIconFilename = "appicon.png"
|
||||
@@ -401,7 +268,7 @@ func (b *PackageHelper) copyIcon() (string, error) {
|
||||
|
||||
func (b *PackageHelper) packageIconOSX(resourceDir string) error {
|
||||
|
||||
srcIcon, err := b.copyIcon()
|
||||
srcIcon, err := b.copyIcon(resourceDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
100 ICON "$NAME$.ico"
|
||||
110 24 "$NAME$.exe.manifest"
|
||||
100 24 "$NAME$.exe.manifest"
|
||||
@@ -161,7 +161,6 @@ type ProjectOptions struct {
|
||||
CrossCompile bool
|
||||
Platform string
|
||||
Architecture string
|
||||
LdFlags string
|
||||
}
|
||||
|
||||
// Defaults sets the default project template
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// SystemHelper - Defines everything related to the system
|
||||
@@ -37,11 +38,10 @@ func NewSystemHelper() *SystemHelper {
|
||||
// setSystemDirs calculates the system directories it is interested in
|
||||
func (s *SystemHelper) setSystemDirs() {
|
||||
var err error
|
||||
s.homeDir, err = os.UserHomeDir()
|
||||
s.homeDir, err = homedir.Dir()
|
||||
if err != nil {
|
||||
log.Fatal("Cannot find home directory! Please file a bug report!")
|
||||
}
|
||||
|
||||
// TODO: A better config system
|
||||
s.wailsSystemDir = filepath.Join(s.homeDir, ".wails")
|
||||
s.wailsSystemConfig = filepath.Join(s.wailsSystemDir, s.configFilename)
|
||||
@@ -132,7 +132,7 @@ func (s *SystemHelper) setup() error {
|
||||
}
|
||||
|
||||
const introText = `
|
||||
Wails is a lightweight framework for creating web-like desktop apps in Go.
|
||||
Wails is a lightweight framework for creating web-like desktop apps in Go.
|
||||
I'll need to ask you a few questions so I can fill in your project templates and then I will try and see if you have the correct dependencies installed. If you don't have the right tools installed, I'll try and suggest how to install them.
|
||||
`
|
||||
|
||||
@@ -274,7 +274,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
distroInfo := GetLinuxDistroInfo()
|
||||
|
||||
switch distroInfo.Distribution {
|
||||
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
|
||||
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian:
|
||||
libraryChecker = DpkgInstalled
|
||||
case Arch, ArcoLinux, ArchLabs, Manjaro, ManjaroARM:
|
||||
libraryChecker = PacmanInstalled
|
||||
@@ -284,8 +284,6 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
libraryChecker = EqueryInstalled
|
||||
case VoidLinux:
|
||||
libraryChecker = XbpsInstalled
|
||||
case Solus:
|
||||
libraryChecker = EOpkgInstalled
|
||||
default:
|
||||
return false, RequestSupportForDistribution(distroInfo)
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"core-js": "^3.6.4",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0",
|
||||
"wails-react-scripts": "3.0.1-2",
|
||||
"react-modal": "3.11.2",
|
||||
"@wailsapp/runtime": "^1.0.10"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -1,13 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Web site created using create-react-app" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
@@ -23,9 +20,8 @@
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="app"></div>
|
||||
<!--
|
||||
@@ -38,6 +34,5 @@
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 176 KiB |
@@ -6,16 +6,6 @@
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -3,16 +3,11 @@
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
|
||||
@@ -1,35 +1,48 @@
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
function HelloWorld() {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [result, setResult] = useState(null);
|
||||
class HelloWorld extends React.Component {
|
||||
constructor(props, context) {
|
||||
super();
|
||||
this.state = {
|
||||
showModal: false
|
||||
};
|
||||
|
||||
const handleOpenModal = () => {
|
||||
setShowModal(true);
|
||||
this.handleOpenModal = this.handleOpenModal.bind(this);
|
||||
this.handleCloseModal = this.handleCloseModal.bind(this);
|
||||
}
|
||||
|
||||
window.backend.basic().then((result) => setResult(result));
|
||||
};
|
||||
handleOpenModal () {
|
||||
this.setState({ showModal: true });
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setShowModal(false);
|
||||
};
|
||||
window.backend.basic().then(result =>
|
||||
this.setState({
|
||||
result
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<button onClick={() => handleOpenModal()} type="button">
|
||||
Hello
|
||||
</button>
|
||||
<Modal
|
||||
appElement={document.getElementById("app")}
|
||||
isOpen={showModal}
|
||||
contentLabel="Minimal Modal Example"
|
||||
>
|
||||
<p>{result}</p>
|
||||
<button onClick={() => handleCloseModal()}>Close Modal</button>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
handleCloseModal () {
|
||||
this.setState({ showModal: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { result } = this.state;
|
||||
return (
|
||||
<div className="App">
|
||||
<button onClick={this.handleOpenModal} type="button">
|
||||
Hello
|
||||
</button>
|
||||
<Modal
|
||||
isOpen={this.state.showModal}
|
||||
contentLabel="Minimal Modal Example"
|
||||
>
|
||||
<p>{result}</p>
|
||||
<button onClick={this.handleCloseModal}>Close Modal</button>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HelloWorld;
|
||||
|
||||
@@ -3,20 +3,9 @@ import ReactDOM from 'react-dom';
|
||||
import 'core-js/stable';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
import * as Wails from '@wailsapp/runtime';
|
||||
|
||||
Wails.Init(() => {
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById("app")
|
||||
);
|
||||
ReactDOM.render(<App />, document.getElementById('app'));
|
||||
});
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "React JS",
|
||||
"version": "1.0.0",
|
||||
"shortdescription": "Create React App v4 template",
|
||||
"description": "Create React App v4 standard tooling",
|
||||
"shortdescription": "Create React App v3 template",
|
||||
"description": "Create React App v3 standar tooling",
|
||||
"install": "npm install",
|
||||
"build": "npm run build",
|
||||
"author": "bh90210 <ktc@pm.me>",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# README
|
||||
|
||||
This is an experimental template for vanilla HTML/JS/CSS.
|
||||
|
||||
The webpack rules may need to be adjusted to correctly embed all assets. Babel may also need to be setup correctly.
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"name": "vanilla",
|
||||
"author": "Lea<l>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "webpack-dev-server",
|
||||
"build": "npx webpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.6.4",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"@wailsapp/runtime": "^1.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^10.1.0",
|
||||
"copy-webpack-plugin": "^6.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"webpack-hot-middleware": "^2.25.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,30 +0,0 @@
|
||||
import 'core-js/stable';
|
||||
const runtime = require('@wailsapp/runtime');
|
||||
|
||||
// Main entry point
|
||||
function start() {
|
||||
|
||||
// Ensure the default app div is 100% wide/high
|
||||
var app = document.getElementById('app');
|
||||
app.style.width = '100%';
|
||||
app.style.height = '100%';
|
||||
|
||||
// Inject html
|
||||
app.innerHTML = `
|
||||
<div class='logo'></div>
|
||||
<div class='container'>
|
||||
<button id='button'>Click Me!</button>
|
||||
<div id='result'/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Connect button to Go method
|
||||
document.getElementById('button').onclick = function() {
|
||||
window.backend.basic().then( function(result) {
|
||||
document.getElementById('result').innerText = result;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// We provide our entrypoint as a callback for runtime.Init
|
||||
runtime.Init(start);
|
||||
@@ -1,56 +0,0 @@
|
||||
const path = require('path');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
let imageSizeLimit = 9007199254740991; // Number.MAX_SAFE_INTEGER
|
||||
let sourceDir = path.resolve(__dirname, 'src');
|
||||
let buildDir = path.resolve(__dirname, 'build');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
index: path.resolve(sourceDir, 'main.js')
|
||||
},
|
||||
output: {
|
||||
path: buildDir,
|
||||
filename: 'main.js'
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: false
|
||||
},
|
||||
devServer: {
|
||||
disableHostCheck: true,
|
||||
contentBase: path.join(__dirname, 'src'),
|
||||
compress: true,
|
||||
open: true,
|
||||
port: 8090
|
||||
},
|
||||
mode: 'production',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(png|gif|jpg|woff2?|eot|ttf|otf|svg)(\?.*)?$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: imageSizeLimit
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.resolve(sourceDir, 'main.css'),
|
||||
to: path.resolve(buildDir, 'main.css')
|
||||
},
|
||||
{
|
||||
from: path.resolve(sourceDir, 'index.html'),
|
||||
to: path.resolve(buildDir, 'index.html')
|
||||
},
|
||||
]
|
||||
})
|
||||
]
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
module {{.BinaryName}}
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails {{.WailsVersion}}
|
||||
)
|
||||
@@ -1,27 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/mewn"
|
||||
"github.com/wailsapp/wails"
|
||||
)
|
||||
|
||||
func basic() string {
|
||||
return "Hello World!"
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
js := mewn.String("./frontend/build/main.js")
|
||||
css := mewn.String("./frontend/build/main.css")
|
||||
|
||||
app := wails.CreateApp(&wails.AppConfig{
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
Title: "{{.Name}}",
|
||||
JS: js,
|
||||
CSS: css,
|
||||
Colour: "#131313",
|
||||
})
|
||||
app.Bind(basic)
|
||||
app.Run()
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "Vanilla",
|
||||
"shortdescription": "A Vanilla HTML/JS template",
|
||||
"description": "A basic template using plain html/js and bundled using Webpack 4",
|
||||
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
||||
"created": "2020-06-14",
|
||||
"frontenddir": "frontend",
|
||||
"install": "npm install",
|
||||
"build": "npm run build",
|
||||
"serve": "npm run serve",
|
||||
"bridge": "src"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
|
||||
// Version - Wails version
|
||||
const Version = "v1.7.2-pre3"
|
||||
const Version = "v1.6.0-pre2"
|
||||
|
||||
@@ -29,7 +29,6 @@ func init() {
|
||||
var typescriptFilename = ""
|
||||
var verbose = false
|
||||
var platform = ""
|
||||
var ldflags = ""
|
||||
|
||||
buildSpinner := spinner.NewSpinner()
|
||||
buildSpinner.SetSpinSpeed(50)
|
||||
@@ -41,8 +40,7 @@ func init() {
|
||||
BoolFlag("f", "Force rebuild of application components", &forceRebuild).
|
||||
BoolFlag("d", "Build in Debug mode", &debugMode).
|
||||
BoolFlag("verbose", "Verbose output", &verbose).
|
||||
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename).
|
||||
StringFlag("ldflags", "Extra options for -ldflags", &ldflags)
|
||||
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename)
|
||||
|
||||
var b strings.Builder
|
||||
for _, plat := range getSupportedPlatforms() {
|
||||
@@ -86,7 +84,7 @@ func init() {
|
||||
}
|
||||
}
|
||||
if !supported {
|
||||
return fmt.Errorf("unsupported platform '%s' specified.\nPlease run `wails build -h` to see the supported platform/architecture options", platform)
|
||||
return fmt.Errorf("Unsupported platform '%s' specified.\nPlease run `wails build -h` to see the supported platform/architecture options.", platform)
|
||||
}
|
||||
|
||||
projectOptions.CrossCompile = true
|
||||
@@ -95,9 +93,6 @@ func init() {
|
||||
projectOptions.Architecture = plat[1]
|
||||
}
|
||||
|
||||
// Add ldflags
|
||||
projectOptions.LdFlags = ldflags
|
||||
|
||||
// Validate config
|
||||
// Check if we have a frontend
|
||||
err = cmd.ValidateFrontendConfig(projectOptions)
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/leaanthony/spinner"
|
||||
"github.com/wailsapp/wails/cmd"
|
||||
@@ -35,6 +34,7 @@ func init() {
|
||||
|
||||
// Project options
|
||||
projectOptions := &cmd.ProjectOptions{}
|
||||
projectOptions.Verbose = verbose
|
||||
|
||||
// Check we are in project directory
|
||||
// Check project.json loads correctly
|
||||
@@ -44,10 +44,6 @@ func init() {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set project options
|
||||
projectOptions.Verbose = verbose
|
||||
projectOptions.Platform = runtime.GOOS
|
||||
|
||||
// Save project directory
|
||||
projectDir := fs.Cwd()
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/leaanthony/spinner"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/wailsapp/wails/cmd"
|
||||
)
|
||||
|
||||
@@ -146,7 +146,7 @@ func updateToVersion(targetVersion *cmd.SemanticVersion, force bool) error {
|
||||
updateSpinner.Start("Installing Wails " + desiredVersion)
|
||||
|
||||
// Run command in non module directory
|
||||
homeDir, err := os.UserHomeDir()
|
||||
homeDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
log.Fatal("Cannot find home directory! Please file a bug report!")
|
||||
}
|
||||
|
||||
9
go.mod
9
go.mod
@@ -14,18 +14,19 @@ require (
|
||||
github.com/leaanthony/spinner v0.5.3
|
||||
github.com/mattn/go-colorable v0.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.7 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
|
||||
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/image v0.0.0-20200430140353-33d19683fad8
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
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
|
||||
)
|
||||
|
||||
go 1.13
|
||||
go 1.12
|
||||
|
||||
18
go.sum
18
go.sum
@@ -46,6 +46,8 @@ github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
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/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98=
|
||||
@@ -66,23 +68,19 @@ github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba h1:2DHfQOxcpWdGf5
|
||||
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-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak=
|
||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/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-20190222072716-a9d3bda3a223/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-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-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-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=
|
||||
|
||||
@@ -17,9 +17,9 @@ type Renderer interface {
|
||||
NotifyEvent(eventData *messages.EventData) error
|
||||
|
||||
// Dialog Runtime
|
||||
SelectFile(title string, filter string) string
|
||||
SelectFile() string
|
||||
SelectDirectory() string
|
||||
SelectSaveFile(title string, filter string) string
|
||||
SelectSaveFile() string
|
||||
|
||||
// Window Runtime
|
||||
SetColour(string) error
|
||||
|
||||
@@ -114,7 +114,7 @@ func (h *Bridge) NewBinding(methodName string) error {
|
||||
|
||||
// SelectFile is unsupported for Bridge but required
|
||||
// for the Renderer interface
|
||||
func (h *Bridge) SelectFile(title string, filter string) string {
|
||||
func (h *Bridge) SelectFile() string {
|
||||
h.log.Warn("SelectFile() unsupported in bridge mode")
|
||||
return ""
|
||||
}
|
||||
@@ -128,7 +128,7 @@ func (h *Bridge) SelectDirectory() string {
|
||||
|
||||
// SelectSaveFile is unsupported for Bridge but required
|
||||
// for the Renderer interface
|
||||
func (h *Bridge) SelectSaveFile(title string, filter string) string {
|
||||
func (h *Bridge) SelectSaveFile() string {
|
||||
h.log.Warn("SelectSaveFile() unsupported in bridge mode")
|
||||
return ""
|
||||
}
|
||||
@@ -152,19 +152,12 @@ func (h *Bridge) NotifyEvent(event *messages.EventData) error {
|
||||
// Marshall the data
|
||||
data, err = json.Marshal(event.Data)
|
||||
if err != nil {
|
||||
h.log.Errorf("Cannot marshal JSON data in event: %s ", err.Error())
|
||||
h.log.Errorf("Cannot unmarshall JSON data in event: %s ", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Double encode data to ensure everything is escaped correctly.
|
||||
data, err = json.Marshal(string(data))
|
||||
if err != nil {
|
||||
h.log.Errorf("Cannot marshal JSON data in event: %s ", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
message := "window.wails._.Notify('" + event.Name + "'," + string(data) + ")"
|
||||
message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data)
|
||||
dead := []*session{}
|
||||
for _, session := range h.sessions {
|
||||
err := session.evalJS(message, notifyMessage)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package renderer
|
||||
|
||||
// Autogenerated by Mewn - Do not alter
|
||||
|
||||
import "github.com/leaanthony/mewn"
|
||||
|
||||
func init() {
|
||||
mewn.AddAsset(".", "../../runtime/assets/wails.js", "1f8b08000000000000ff94586d8f1bb711fe2b2ba255481f8f91dc14a977c318cee582a838df053ebbfda00a01b53b92e85b915b2e758a20f1bf17b3efba97a2050c7bc519728633cf3c33f468b533a9d7d650c38e8fca45208f216917234f1d3bea1585b95b30077ee74c84df02fe28acf365825bacc42579d4b1e3793c9af246181f43489a4d0637a52acfa96df772cbfb6fcfb815b91c4dfab5e0c5561aee452a817b91c9de550edcb1a317163fd9e974b7fc0aa91719acb481df9c2dc0f943a57604b3db8253cb1ce2d184afc1c72eb0c0bd70727875b233f5ee8c8ca43f146057d1fd61bbb4f9785cff2bbcbdf74e9bf567b51e8f5fb3f85c971f1f55be83987cb4d92e0712187f6d33f9fd77281bb576db6852bbebcfae5f25653a86f1981ae9a9618cff6d0c6d864ca257f43b94125b9922b2bd93198ff18fe82df59b30974e36cea50e94076a7679cef0382f1c75afb9ee38c960a576b9274f235edfc204c6df560e95555cfa201bb6b28e56308ab4890cf322a38e5bde5d17d8b103112c82586a93557e71cb588b2f873132f2399a9fdcf67da7d19f2a1adf43fc82b04330fa059c28c281714073f6494a1ac5264485b3dee225c54695777bd306abae02dc8067149210eea917a59cb040e76718f788cb12228c59ea49823900c61b4f66467bc2e9739f6781b12699c7903489abc34a7e86e56efde2ae5d609dd6ccacec8b4af940e99fca194ce54b7aab81deb573d6bda8950db47e515ee52f6a95ed756c7b1d5befb29cdc1560be7cba7971dfa13dbdd1fb456359bda0b86c0d68395ff4dca7da1c34a1c45cc68663fa962a7d98fd1c7b5ea8436e55164348ce9854afe85e9bcceec55ee9bc5c3a9dade1fdf325b1876569d307f0a20484358b1b25f8c383332a17da3cda07a086712d72306bbff971d2d50cc849023fb482042e2e989ec3821a16e8dfefef6e455d6e7a75a08eb1d0dd2dad11ab28c9ed9af0630e8f90c7866fa12cd51a620803e51dde28a524abb16306a2bc11e90a3043c9aa91ec5b940c855923841a1a4351d98856351e0c0b78cf3d2fcefad2639b9bb64c77793e927e3c6e3e4e27eae5847103fbe83767b7ba04da67de715b675527993d6a692ec825b9d85316f61b9d032de67a5193de8f13867aa92cc17fd65bb03b4f8708b2b4423725572acf236f2372612e48e4f516b2c8eebc883ec1bf7750fa68f6731c910bcd5860dcb3044dc8a3af8ffc55992c8738e50e903a62cb1d94367f84d885c4bb03e6096147f811c94f9bf5adda221633e555fc24cfc82b9a8554f97453c5d29ad2e620aa582330c21008eb33ea7aa4a446a720171543757a073caad15a53f293b3fb129ce8ea6f98c2e5ebba750d1a16f6b2c179ea0e85b7435ac6801b8999fba28dffcbdb0fcea9039d76547fb651acc17f5226b3db7f60a341f4cc278b9798fcdd64f2fdf4ddbbb77ffdeefbef26efde4ddf7c547e235cb597b25011c0b663f4c8d00a5fa3675d77444da44de99549b17f01637ee3ec3e42873f1f0ae800618cf511a62d52519aabb28c5419a9a83d90b040fd4697dc30ee11b197538ebfc555433067cdac9dbd842a8afc50b73fc3f8e5742411f6132925f59772ca42e09bb352b96b4b6533370b897f9d4ef345d31fd0e96d75d30425a2d8951bea06b9bcee3be9139ccd17a2cc75da7aa4dc7ab705e34b562fd329b2af3c9a015221248a127804e309f77565df9f39fb80e610f16d5dc33efaa50d03417d7261181f4d7a7cd79aa369e89dbeeab24eafda794695a55e9bd36918d49e45a709fcd0dd60c8a6d5ddbdec64735824ed3687038b67ff53bff7dc311cd67050f6384c876ec0604d042b30f4610c0dce110a6032790c7cd83fe4f0c7e9d44b7bfd2ac75fe5f116f63fd5b4113f9f8fe60b915a932a4f8d288b5c7b4a0461d56797c4f3939118a16d45d3ae15393949dc0fade0729ab80b39adadd40f04dc377aa096b141729b6a21ad139693489711968e8a1e55aeb3e8ab7a5465ea74e1239d81f17aa5c109c258e2e776814c3fb70b790c5519cded22d4ad1c44610bca1aabfabf5ad5ff9f55bd904ff80afbf0e0d1d4c2a6ab910a037d6e938e6f0df7c8b3dd8cd9f799b3f20769705a5cbf20edb921701f280bbc2590e7d94e8ccc20b5197cf934bbb2dbc21a3098780745ae52a0dffeabbcf876cd0961fdd27c72f94e5dae16c7b701457ffed3b80a823b1c1b4a28942b713e694a12dacb9399a943895a51a1ca12326c91ed0015475dfc413483072722fad8cc2011611d34595293ec8efabaa9d7196c38c4c93ac278aaceb895450bb7b6cf4bd20625faa6b3ea38f9a6cab983b52e3d64a3d188b496cedba61e5ac5ee9a8372ed4060c5591f673c831c3c44e8056f4e786f45dddc69b3c0625ca9ba3c0581f4c802bfb55eaf0ef1b39e83dcdcd395af58bca159c6ebc2ab961ae21a561eaea31fd56c8b95cb30757a983a18a42ea3e76943c7a2128cc7cc99ca3d115d238347c8ec91ac0839d8ae6b51cdc663dff287e35316aac6e343e01fb2ecbe2aa92737ac2f95d9b42a8fe6e9799d03fea2a4ae424c8cf0f0879786779a4b9b1d903dc164571b9d67880e188fafab21686630de57f7f72fb0deabb6fc2107c212c042fce0bdd3cb9d074a90d7092768ffdbb42c09e3202addfb0d807f3ffc21d2b2fc5cf919c3996f4f6ca2ceadcda07abcf7c20da8ec74ea7eaec137ce953f1d3eab6af0a30495080e3aeccc427d6bede319867af6dbd50dc2da803b8b80aebb3c4e82fc561e6fec3a76bc19d262cbabe496f1f1ce3cc9d25df368bd331f77b9d7450ef11dbfde6a1f5ff35f4139bf04759ed9fef184f34a728f3818f056554433e3c13daabc7aa3227b399cb5bbd5e1b08db34860950f1fd20763f739646b889fbcbb466866309365dd2ca6fa4dd1cee0b78936ade3035e309c7c831c8707518cd2eff1d7c18c32434386b27075f6c6e3b7ac6dc1d654257efe1f569e57cf8ee1167163d7a2e9456fdebc79135da9dd7ae3a32f6653314956134e8432c2f86b5b7bc2c4d9e835ad2f9f6e50035ed7b8d106a25b8b5afe75ad2b9befb6a6d173afeb5521401dcb02bfc687a0cecb185fcb901116162cf94f000000ffff04075a367c140000")
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -245,7 +245,7 @@ func (w *WebView) NewBinding(methodName string) error {
|
||||
}
|
||||
|
||||
// SelectFile opens a dialog that allows the user to select a file
|
||||
func (w *WebView) SelectFile(title string, filter string) string {
|
||||
func (w *WebView) SelectFile() string {
|
||||
var result string
|
||||
|
||||
// We need to run this on the main thread, however Dispatch is
|
||||
@@ -255,7 +255,7 @@ func (w *WebView) SelectFile(title string, filter string) string {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
w.window.Dispatch(func() {
|
||||
result = w.window.Dialog(wv.DialogTypeOpen, 0, title, "", filter)
|
||||
result = w.window.Dialog(wv.DialogTypeOpen, 0, "Select File", "")
|
||||
wg.Done()
|
||||
})
|
||||
}()
|
||||
@@ -273,7 +273,7 @@ func (w *WebView) SelectDirectory() string {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
w.window.Dispatch(func() {
|
||||
result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "", "")
|
||||
result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "")
|
||||
wg.Done()
|
||||
})
|
||||
}()
|
||||
@@ -282,7 +282,7 @@ func (w *WebView) SelectDirectory() string {
|
||||
}
|
||||
|
||||
// SelectSaveFile opens a dialog that allows the user to select a file to save
|
||||
func (w *WebView) SelectSaveFile(title string, filter string) string {
|
||||
func (w *WebView) SelectSaveFile() string {
|
||||
var result string
|
||||
// We need to run this on the main thread, however Dispatch is
|
||||
// non-blocking so we launch this in a goroutine and wait for
|
||||
@@ -291,7 +291,7 @@ func (w *WebView) SelectSaveFile(title string, filter string) string {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
w.window.Dispatch(func() {
|
||||
result = w.window.Dialog(wv.DialogTypeSave, 0, title, "", filter)
|
||||
result = w.window.Dialog(wv.DialogTypeSave, 0, "Save file", "")
|
||||
wg.Done()
|
||||
})
|
||||
}()
|
||||
@@ -329,14 +329,7 @@ func (w *WebView) NotifyEvent(event *messages.EventData) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Double encode data to ensure everything is escaped correctly.
|
||||
data, err = json.Marshal(string(data))
|
||||
if err != nil {
|
||||
w.log.Errorf("Cannot marshal JSON data in event: %s ", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
message := "window.wails._.Notify('" + event.Name + "'," + string(data) + ")"
|
||||
message := fmt.Sprintf("wails._.Notify('%s','%s')", event.Name, data)
|
||||
return w.evalJS(message)
|
||||
}
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@ static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b,
|
||||
}
|
||||
|
||||
static inline void CgoDialog(void *w, int dlgtype, int flags,
|
||||
char *title, char *arg, char *res, size_t ressz, char *filter) {
|
||||
char *title, char *arg, char *res, size_t ressz) {
|
||||
webview_dialog(w, dlgtype, flags,
|
||||
(const char*)title, (const char*) arg, res, ressz, filter);
|
||||
(const char*)title, (const char*) arg, res, ressz);
|
||||
}
|
||||
|
||||
static inline int CgoWebViewEval(void *w, char *js) {
|
||||
@@ -186,7 +186,7 @@ type WebView interface {
|
||||
// Dialog() opens a system dialog of the given type and title. String
|
||||
// argument can be provided for certain dialogs, such as alert boxes. For
|
||||
// alert boxes argument is a message inside the dialog box.
|
||||
Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string
|
||||
Dialog(dlgType DialogType, flags int, title string, arg string) string
|
||||
// Terminate() breaks the main UI loop. This method must be called from the main thread
|
||||
// only. See Dispatch() for more details.
|
||||
Terminate()
|
||||
@@ -311,7 +311,7 @@ func (w *webview) SetFullscreen(fullscreen bool) {
|
||||
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
|
||||
}
|
||||
|
||||
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string, filter string) string {
|
||||
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string) string {
|
||||
const maxPath = 4096
|
||||
titlePtr := C.CString(title)
|
||||
defer C.free(unsafe.Pointer(titlePtr))
|
||||
@@ -319,10 +319,8 @@ func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string
|
||||
defer C.free(unsafe.Pointer(argPtr))
|
||||
resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath)))
|
||||
defer C.free(unsafe.Pointer(resultPtr))
|
||||
filterPtr := C.CString(filter)
|
||||
defer C.free(unsafe.Pointer(filterPtr))
|
||||
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
|
||||
argPtr, resultPtr, C.size_t(maxPath), filterPtr)
|
||||
argPtr, resultPtr, C.size_t(maxPath))
|
||||
return C.GoString(resultPtr)
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ struct webview_priv
|
||||
WEBVIEW_API void webview_dialog(struct webview *w,
|
||||
enum webview_dialog_type dlgtype, int flags,
|
||||
const char *title, const char *arg,
|
||||
char *result, size_t resultsz, char *filter);
|
||||
char *result, size_t resultsz);
|
||||
WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
|
||||
void *arg);
|
||||
WEBVIEW_API void webview_terminate(struct webview *w);
|
||||
@@ -418,7 +418,7 @@ struct webview_priv
|
||||
WEBVIEW_API void webview_dialog(struct webview *w,
|
||||
enum webview_dialog_type dlgtype, int flags,
|
||||
const char *title, const char *arg,
|
||||
char *result, size_t resultsz, char *filter)
|
||||
char *result, size_t resultsz)
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
if (result != NULL)
|
||||
@@ -438,17 +438,6 @@ struct webview_priv
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
(dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ? "_Open" : "_Save"),
|
||||
GTK_RESPONSE_ACCEPT, NULL);
|
||||
if (filter[0] != '\0') {
|
||||
GtkFileFilter *file_filter = gtk_file_filter_new();
|
||||
gchar **filters = g_strsplit(filter, ",", -1);
|
||||
gint i;
|
||||
for(i = 0; filters && filters[i]; i++) {
|
||||
gtk_file_filter_add_pattern(file_filter, filters[i]);
|
||||
}
|
||||
gtk_file_filter_set_name(file_filter, filter);
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dlg), file_filter);
|
||||
g_strfreev(filters);
|
||||
}
|
||||
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
|
||||
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
|
||||
@@ -1421,8 +1410,6 @@ struct webview_priv
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpfnWndProc = wndproc;
|
||||
wc.lpszClassName = classname;
|
||||
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100));
|
||||
wc.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(100));
|
||||
RegisterClassEx(&wc);
|
||||
|
||||
style = WS_OVERLAPPEDWINDOW;
|
||||
@@ -1793,7 +1780,7 @@ struct webview_priv
|
||||
WEBVIEW_API void webview_dialog(struct webview *w,
|
||||
enum webview_dialog_type dlgtype, int flags,
|
||||
const char *title, const char *arg,
|
||||
char *result, size_t resultsz, char *filter)
|
||||
char *result, size_t resultsz)
|
||||
{
|
||||
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
|
||||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
|
||||
@@ -1833,32 +1820,6 @@ struct webview_priv
|
||||
FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS |
|
||||
FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE;
|
||||
}
|
||||
if (filter[0] != '\0')
|
||||
{
|
||||
int count;
|
||||
int i=0;
|
||||
char* token;
|
||||
char* filter_dup = strdup(filter);
|
||||
for (count=1; filter[count]; filter[count]==',' ? count++ : *filter++);
|
||||
COMDLG_FILTERSPEC rgSpec[count];
|
||||
char* filters[count];
|
||||
token = strtok(filter_dup, ",");
|
||||
while(token != NULL)
|
||||
{
|
||||
filters[i] = token;
|
||||
token = strtok(NULL, ",");
|
||||
i++;
|
||||
}
|
||||
for (int i=0; i < count; i++) {
|
||||
wchar_t *wFilter = (wchar_t *)malloc(4096);
|
||||
MultiByteToWideChar(CP_ACP, 0, filters[i], -1, wFilter, 4096);
|
||||
rgSpec[i].pszName = wFilter;
|
||||
rgSpec[i].pszSpec = wFilter;
|
||||
}
|
||||
if (dlg->lpVtbl->SetFileTypes(dlg, count, rgSpec) != S_OK) {
|
||||
goto error_dlg;
|
||||
}
|
||||
}
|
||||
if (dlg->lpVtbl->GetOptions(dlg, &opts) != S_OK)
|
||||
{
|
||||
goto error_dlg;
|
||||
@@ -1977,7 +1938,7 @@ struct webview_priv
|
||||
(struct webview *)objc_getAssociatedObject(self, "webview");
|
||||
|
||||
webview_dialog(w, WEBVIEW_DIALOG_TYPE_OPEN, WEBVIEW_DIALOG_FLAG_FILE, "", "",
|
||||
filename, 255, "");
|
||||
filename, 255);
|
||||
filename[255] = '\0';
|
||||
if (strlen(filename) > 0)
|
||||
{
|
||||
@@ -2240,16 +2201,12 @@ struct webview_priv
|
||||
WEBVIEW_API void webview_dialog(struct webview *w,
|
||||
enum webview_dialog_type dlgtype, int flags,
|
||||
const char *title, const char *arg,
|
||||
char *result, size_t resultsz, char *filter)
|
||||
char *result, size_t resultsz)
|
||||
{
|
||||
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
|
||||
dlgtype == WEBVIEW_DIALOG_TYPE_SAVE)
|
||||
{
|
||||
NSSavePanel *panel;
|
||||
NSString *filter_str = [NSString stringWithUTF8String:filter];
|
||||
filter_str = [filter_str stringByReplacingOccurrencesOfString:@"*."
|
||||
withString:@""];
|
||||
NSArray *fileTypes = [filter_str componentsSeparatedByString:@","];
|
||||
if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN)
|
||||
{
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
@@ -2262,10 +2219,6 @@ struct webview_priv
|
||||
{
|
||||
[openPanel setCanChooseFiles:YES];
|
||||
[openPanel setCanChooseDirectories:NO];
|
||||
if(filter[0] != NULL)
|
||||
{
|
||||
[openPanel setAllowedFileTypes:fileTypes];
|
||||
}
|
||||
}
|
||||
[openPanel setResolvesAliases:NO];
|
||||
[openPanel setAllowsMultipleSelection:NO];
|
||||
@@ -2279,10 +2232,6 @@ struct webview_priv
|
||||
[panel setShowsHiddenFiles:YES];
|
||||
[panel setExtensionHidden:NO];
|
||||
[panel setCanSelectHiddenExtension:NO];
|
||||
if(filter[0] != NULL)
|
||||
{
|
||||
[panel setAllowedFileTypes:fileTypes];
|
||||
}
|
||||
[panel setTreatsFilePackagesAsDirectories:YES];
|
||||
[panel beginSheetModalForWindow:w->priv.window
|
||||
completionHandler:^(NSInteger result) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import "github.com/wailsapp/wails/lib/interfaces"
|
||||
import "strings"
|
||||
|
||||
// Dialog exposes an interface to native dialogs
|
||||
type Dialog struct {
|
||||
@@ -16,16 +15,8 @@ func NewDialog(renderer interfaces.Renderer) *Dialog {
|
||||
}
|
||||
|
||||
// SelectFile prompts the user to select a file
|
||||
func (r *Dialog) SelectFile(params ...string) string {
|
||||
title := "Select File"
|
||||
filter := ""
|
||||
if len(params) > 0 {
|
||||
title = params[0]
|
||||
}
|
||||
if len(params) > 1 {
|
||||
filter = strings.Replace(params[1], " ", "", -1)
|
||||
}
|
||||
return r.renderer.SelectFile(title, filter)
|
||||
func (r *Dialog) SelectFile() string {
|
||||
return r.renderer.SelectFile()
|
||||
}
|
||||
|
||||
// SelectDirectory prompts the user to select a directory
|
||||
@@ -34,14 +25,6 @@ func (r *Dialog) SelectDirectory() string {
|
||||
}
|
||||
|
||||
// SelectSaveFile prompts the user to select a file for saving
|
||||
func (r *Dialog) SelectSaveFile(params ...string) string {
|
||||
title := "Select Save"
|
||||
filter := ""
|
||||
if len(params) > 0 {
|
||||
title = params[0]
|
||||
}
|
||||
if len(params) > 1 {
|
||||
filter = strings.Replace(params[1], " ", "", -1)
|
||||
}
|
||||
return r.renderer.SelectSaveFile(title, filter)
|
||||
func (r *Dialog) SelectSaveFile() string {
|
||||
return r.renderer.SelectSaveFile()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package runtime
|
||||
|
||||
import "os"
|
||||
import homedir "github.com/mitchellh/go-homedir"
|
||||
|
||||
// FileSystem exposes file system utilities to the runtime
|
||||
type FileSystem struct{}
|
||||
type FileSystem struct {}
|
||||
|
||||
// NewFileSystem creates a new FileSystem struct
|
||||
func NewFileSystem() *FileSystem {
|
||||
@@ -12,5 +12,5 @@ func NewFileSystem() *FileSystem {
|
||||
|
||||
// HomeDir returns the user's home directory
|
||||
func (r *FileSystem) HomeDir() (string, error) {
|
||||
return os.UserHomeDir()
|
||||
return homedir.Dir()
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ export function Notify(eventName, data) {
|
||||
try {
|
||||
parsedData = JSON.parse(data);
|
||||
} catch (e) {
|
||||
Error('Invalid JSON data sent to notify. Event name = ' + eventName);
|
||||
e.message =
|
||||
Error('Invalid JSON data sent to notify [Event name = ' + eventName + '] ' + e.toString());
|
||||
}
|
||||
}
|
||||
// Do the callback
|
||||
|
||||
7078
runtime/js/package-lock.json
generated
7078
runtime/js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -29,15 +29,15 @@
|
||||
},
|
||||
"homepage": "https://github.com/wailsapp/runtime#readme",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.11.5",
|
||||
"@babel/core": "^7.11.5",
|
||||
"@babel/plugin-transform-object-assign": "^7.10.4",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-preset-minify": "^0.5.1",
|
||||
"core-js": "^3.6.5",
|
||||
"eslint": "^7.8.1",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12"
|
||||
"@babel/cli": "^7.5.0",
|
||||
"@babel/core": "^7.5.4",
|
||||
"@babel/plugin-transform-object-assign": "^7.2.0",
|
||||
"@babel/preset-env": "^7.5.4",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-preset-minify": "^0.5.0",
|
||||
"core-js": "^3.1.4",
|
||||
"eslint": "^6.5.1",
|
||||
"webpack": "^4.35.3",
|
||||
"webpack-cli": "^3.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
13
v2/.vscode/settings.json
vendored
13
v2/.vscode/settings.json
vendored
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"ios": "c",
|
||||
"typeinfo": "c",
|
||||
"sstream": "c",
|
||||
"__functional_03": "c",
|
||||
"functional": "c",
|
||||
"__locale": "c",
|
||||
"locale": "c",
|
||||
"chrono": "c",
|
||||
"system_error": "c"
|
||||
}
|
||||
}
|
||||
40
v2/NOTES.md
40
v2/NOTES.md
@@ -1,40 +0,0 @@
|
||||
|
||||
# Packing linux
|
||||
|
||||
* create app, app.desktop, app.png (512x512)
|
||||
* chmod +x app!
|
||||
* ./linuxdeploy-x86_64.AppImage --appdir AppDir -i react.png -d react.desktop -e react --output appimage
|
||||
|
||||
|
||||
# Wails Doctor
|
||||
|
||||
Tested on:
|
||||
|
||||
* Debian 8
|
||||
* Ubuntu 20.04
|
||||
* Ubuntu 19.10
|
||||
* Solus 4.1
|
||||
* Centos 8
|
||||
* Gentoo
|
||||
* OpenSUSE/leap
|
||||
* Fedora 31
|
||||
|
||||
### Development
|
||||
|
||||
Add a new package manager processor here: `v2/internal/system/packagemanager/`. IsAvailable should work even if the package is installed.
|
||||
Add your new package manager to the list of package managers in `v2/internal/system/packagemanager/packagemanager.go`:
|
||||
|
||||
```
|
||||
var db = map[string]PackageManager{
|
||||
"eopkg": NewEopkg(),
|
||||
"apt": NewApt(),
|
||||
"yum": NewYum(),
|
||||
"pacman": NewPacman(),
|
||||
"emerge": NewEmerge(),
|
||||
"zypper": NewZypper(),
|
||||
}
|
||||
```
|
||||
|
||||
## Gentoo
|
||||
|
||||
* Setup docker image using: emerge-webrsync -x -v
|
||||
@@ -1,5 +0,0 @@
|
||||
# Wails v2 ALPHA
|
||||
|
||||
This branch contains WORK IN PROGRESS! There are no guarantees. Use at your peril!
|
||||
|
||||
This document will be updated as progress is made.
|
||||
@@ -1,113 +0,0 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
// AddBuildSubcommand adds the `build` command for the Wails application
|
||||
func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
outputType := "desktop"
|
||||
|
||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
||||
|
||||
command := app.NewSubCommand("build", "Builds the application")
|
||||
|
||||
// Setup target type flag
|
||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||
command.StringFlag("t", description, &outputType)
|
||||
|
||||
// Setup production flag
|
||||
production := false
|
||||
command.BoolFlag("production", "Build in production mode", &production)
|
||||
|
||||
// Setup pack flag
|
||||
pack := false
|
||||
command.BoolFlag("pack", "Create a platform specific package", &pack)
|
||||
|
||||
compilerCommand := "go"
|
||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||
|
||||
// Setup Platform flag
|
||||
platform := runtime.GOOS
|
||||
command.StringFlag("platform", "Platform to target", &platform)
|
||||
|
||||
// Quiet Build
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
// ldflags to pass to `go`
|
||||
ldflags := ""
|
||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||
|
||||
// Log to file
|
||||
logFile := ""
|
||||
command.StringFlag("l", "Log to file", &logFile)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// Validate output type
|
||||
if !validTargetTypes.Contains(outputType) {
|
||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Setup mode
|
||||
mode := build.Debug
|
||||
if production {
|
||||
mode = build.Production
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Mode: mode,
|
||||
Pack: pack,
|
||||
Platform: platform,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
}
|
||||
|
||||
return doBuild(buildOptions)
|
||||
})
|
||||
}
|
||||
|
||||
// doBuild is our main build command
|
||||
func doBuild(buildOptions *build.Options) error {
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
outputFilename, err := build.Build(buildOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Output stats
|
||||
elapsed := time.Since(start)
|
||||
buildOptions.Logger.Println("")
|
||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
||||
buildOptions.Logger.Println("")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
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
|
||||
ldflags := ""
|
||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||
|
||||
// compiler command
|
||||
compilerCommand := "go"
|
||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||
|
||||
// extensions to trigger rebuilds
|
||||
extensions := "go"
|
||||
command.StringFlag("m", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Validate inputs
|
||||
if !validTargetTypes.Contains(outputType) {
|
||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||
}
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
app.PrintBanner()
|
||||
|
||||
// TODO: Check you are in a project directory
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
var debugBinaryProcess *process.Process = nil
|
||||
var buildFrontend bool = true
|
||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||
|
||||
// Setup signal handler
|
||||
quitChannel := make(chan os.Signal, 1)
|
||||
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
|
||||
debounceQuit := make(chan bool, 1)
|
||||
|
||||
// Do initial build
|
||||
logger.Println("Building application for development...")
|
||||
debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||
|
||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||
// logger.Println("event: %+v", event)
|
||||
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
// If this is a folder, add it to our watch list
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
watcher.Add(event.Name)
|
||||
logger.Println("Watching directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
// logger.Println("modified file: %s", event.Name)
|
||||
var rebuild bool = false
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range extensionsThatTriggerARebuild {
|
||||
rebuild = strings.HasSuffix(event.Name, pattern)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
if rebuild {
|
||||
// Only build frontend when the file isn't a Go file
|
||||
buildFrontend = !strings.HasSuffix(event.Name, "go")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !rebuild {
|
||||
logger.Println("Filename change: %s did not match extension list %s", event.Name, extensions)
|
||||
return
|
||||
}
|
||||
|
||||
if buildFrontend {
|
||||
logger.Println("Full rebuild triggered: %s updated", event.Name)
|
||||
} else {
|
||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
||||
}
|
||||
|
||||
// Do a rebuild
|
||||
|
||||
// Try and build the app
|
||||
newBinaryProcess := restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||
|
||||
// If we have a new process, save it
|
||||
if newBinaryProcess != nil {
|
||||
debugBinaryProcess = newBinaryProcess
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Get project dir
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
logger.Println("Watching directory: %s", dir)
|
||||
err = watcher.Add(dir)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// Wait until we get a quit signal
|
||||
quit := false
|
||||
for quit == false {
|
||||
select {
|
||||
case <-quitChannel:
|
||||
println()
|
||||
// Notify debouncer to quit
|
||||
debounceQuit <- true
|
||||
quit = true
|
||||
}
|
||||
}
|
||||
|
||||
// Kill the current program if running
|
||||
if debugBinaryProcess != nil {
|
||||
debugBinaryProcess.Kill()
|
||||
}
|
||||
|
||||
logger.Println("Development mode exited")
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Credit: https://drailing.net/2018/01/debounce-function-for-golang/
|
||||
func debounce(interval time.Duration, input chan fsnotify.Event, quitChannel chan bool, cb func(arg fsnotify.Event)) {
|
||||
var item fsnotify.Event
|
||||
timer := time.NewTimer(interval)
|
||||
exit:
|
||||
for {
|
||||
select {
|
||||
case item = <-input:
|
||||
timer.Reset(interval)
|
||||
case <-timer.C:
|
||||
if item.Name != "" {
|
||||
cb(item)
|
||||
}
|
||||
case <-quitChannel:
|
||||
break exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
||||
|
||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
|
||||
println()
|
||||
if err != nil {
|
||||
logger.Println("[ERROR] Build Failed: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
logger.Println("Build new binary: %s", appBinary)
|
||||
|
||||
// Kill existing binary if need be
|
||||
if debugBinaryProcess != nil {
|
||||
killError := debugBinaryProcess.Kill()
|
||||
|
||||
if killError != nil {
|
||||
logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
||||
}
|
||||
|
||||
debugBinaryProcess = nil
|
||||
}
|
||||
|
||||
// TODO: Generate `backend.js`
|
||||
|
||||
// Start up new binary
|
||||
newProcess := process.NewProcess(logger, appBinary)
|
||||
err = newProcess.Start()
|
||||
if err != nil {
|
||||
// Remove binary
|
||||
fs.DeleteFile(appBinary)
|
||||
logger.Fatal("Unable to start application: %s", err.Error())
|
||||
}
|
||||
|
||||
return newProcess
|
||||
}
|
||||
|
||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
||||
|
||||
// Create random output file
|
||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Mode: build.Debug,
|
||||
Pack: false,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
OutputFile: outputFile,
|
||||
IgnoreFrontend: !buildFrontend,
|
||||
}
|
||||
|
||||
return build.Build(buildOptions)
|
||||
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package doctor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/system"
|
||||
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `doctor` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
logger := clilogger.New(w)
|
||||
|
||||
app.PrintBanner()
|
||||
logger.Print("Scanning system - please wait...")
|
||||
|
||||
// Get system info
|
||||
info, err := system.GetInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Println("Done.")
|
||||
|
||||
// Start a new tabwriter
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
|
||||
// Write out the system information
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "System\n")
|
||||
fmt.Fprintf(w, "------\n")
|
||||
fmt.Fprintf(w, "%s\t%s\n", "OS:", info.OS.Name)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Version: ", info.OS.Version)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "ID:", info.OS.ID)
|
||||
|
||||
// Exit early if PM not found
|
||||
if info.PM == nil {
|
||||
fmt.Fprintf(w, "\n%s\t%s", "Package Manager:", "Not Found")
|
||||
w.Flush()
|
||||
println()
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
||||
|
||||
// Output Go Information
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Go Version:", runtime.Version())
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Platform:", runtime.GOOS)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Architecture:", runtime.GOARCH)
|
||||
|
||||
// Output Dependencies Status
|
||||
var dependenciesMissing = []string{}
|
||||
var externalPackages = []*packagemanager.Dependancy{}
|
||||
var dependenciesAvailableRequired = 0
|
||||
var dependenciesAvailableOptional = 0
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "Dependency\tPackage Name\tStatus\tVersion\n")
|
||||
fmt.Fprintf(w, "----------\t------------\t------\t-------\n")
|
||||
|
||||
// Loop over dependencies
|
||||
for _, dependency := range info.Dependencies {
|
||||
|
||||
name := dependency.Name
|
||||
if dependency.Optional {
|
||||
name += "*"
|
||||
}
|
||||
packageName := "Unknown"
|
||||
status := "Not Found"
|
||||
|
||||
// If we found the package
|
||||
if dependency.PackageName != "" {
|
||||
|
||||
packageName = dependency.PackageName
|
||||
|
||||
// If it's installed, update the status
|
||||
if dependency.Installed {
|
||||
status = "Installed"
|
||||
} else {
|
||||
// Generate meaningful status text
|
||||
status = "Available"
|
||||
|
||||
if dependency.Optional {
|
||||
dependenciesAvailableOptional++
|
||||
} else {
|
||||
dependenciesAvailableRequired++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !dependency.Optional {
|
||||
dependenciesMissing = append(dependenciesMissing, dependency.Name)
|
||||
}
|
||||
|
||||
if dependency.External {
|
||||
externalPackages = append(externalPackages, dependency)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, packageName, status, dependency.Version)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
||||
w.Flush()
|
||||
logger.Println("")
|
||||
logger.Println("Diagnosis")
|
||||
logger.Println("---------\n")
|
||||
|
||||
// Generate an appropriate diagnosis
|
||||
|
||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
||||
logger.Println("Your system is ready for Wails development!")
|
||||
}
|
||||
|
||||
if dependenciesAvailableRequired != 0 {
|
||||
log.Println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
||||
}
|
||||
|
||||
if dependenciesAvailableOptional != 0 {
|
||||
log.Println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
||||
}
|
||||
|
||||
if len(externalPackages) > 0 {
|
||||
for _, p := range externalPackages {
|
||||
if p.Optional {
|
||||
print("[Optional] ")
|
||||
}
|
||||
log.Println("Install " + p.Name + ": " + p.InstallCommand)
|
||||
}
|
||||
}
|
||||
|
||||
if len(dependenciesMissing) != 0 {
|
||||
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
||||
log.Println("Fatal:")
|
||||
log.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||
log.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
||||
}
|
||||
|
||||
log.Println("")
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("generate", "Code Generation Tools")
|
||||
|
||||
// Backend API
|
||||
backendAPI := command.NewSubCommand("module", "Generates a JS module for the frontend to interface with the backend")
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
backendAPI.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
backendAPI.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
app.PrintBanner()
|
||||
|
||||
logger.Print("Generating Javascript module for Go code...")
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
p, err := parser.GenerateWailsFrontendPackage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Println("done.")
|
||||
logger.Println("")
|
||||
|
||||
elapsed := time.Since(start)
|
||||
packages := p.Packages
|
||||
|
||||
// Print report
|
||||
for _, pkg := range p.Packages {
|
||||
if pkg.ShouldGenerate() {
|
||||
logPackage(pkg, logger)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.Println("%d packages parsed in %s.", len(packages), elapsed)
|
||||
|
||||
return nil
|
||||
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func logPackage(pkg *parser.Package, logger *clilogger.CLILogger) {
|
||||
|
||||
logger.Println("Processed Go package '" + pkg.Gopackage.Name + "' as '" + pkg.Name + "'")
|
||||
for _, strct := range pkg.Structs() {
|
||||
logger.Println("")
|
||||
logger.Println(" Processed struct '" + strct.Name + "'")
|
||||
if strct.IsBound {
|
||||
for _, method := range strct.Methods {
|
||||
logger.Println(" Bound method '" + method.Name + "'")
|
||||
}
|
||||
}
|
||||
if strct.IsUsedAsData {
|
||||
for _, field := range strct.Fields {
|
||||
if !field.Ignored {
|
||||
logger.Print(" Processed ")
|
||||
if field.IsOptional {
|
||||
logger.Print("optional ")
|
||||
}
|
||||
logger.Println("field '" + field.Name + "' as '" + field.JSName() + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Println("")
|
||||
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/templates"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `init` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
// Load the template shortnames
|
||||
validShortNames, err := templates.TemplateShortNames()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command := app.NewSubCommand("init", "Initialise a new Wails project")
|
||||
|
||||
// Setup template name flag
|
||||
templateName := "vanilla"
|
||||
description := "Name of template to use. Valid tempates: " + validShortNames.Join(" ")
|
||||
command.StringFlag("t", description, &templateName)
|
||||
|
||||
// Setup project name
|
||||
projectName := ""
|
||||
command.StringFlag("n", "Name of project", &projectName)
|
||||
|
||||
// Setup project directory
|
||||
projectDirectory := ""
|
||||
command.StringFlag("d", "Project directory", &projectDirectory)
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
// VSCode project files
|
||||
vscode := false
|
||||
command.BoolFlag("vscode", "Generate VSCode project files", &vscode)
|
||||
|
||||
// List templates
|
||||
list := false
|
||||
command.BoolFlag("l", "List templates", &list)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// Are we listing templates?
|
||||
if list {
|
||||
app.PrintBanner()
|
||||
err := templates.OutputList(logger)
|
||||
logger.Println("")
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate output type
|
||||
if !validShortNames.Contains(templateName) {
|
||||
logger.Print(fmt.Sprintf("[ERROR] Template '%s' is not valid", templateName))
|
||||
logger.Println("")
|
||||
command.PrintHelp()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate name
|
||||
if len(projectName) == 0 {
|
||||
logger.Println("ERROR: Project name required")
|
||||
logger.Println("")
|
||||
command.PrintHelp()
|
||||
return nil
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Create Template Options
|
||||
options := &templates.Options{
|
||||
ProjectName: projectName,
|
||||
TargetDir: projectDirectory,
|
||||
TemplateName: templateName,
|
||||
Logger: logger,
|
||||
GenerateVSCode: vscode,
|
||||
}
|
||||
|
||||
return initProject(options)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// initProject is our main init command
|
||||
func initProject(options *templates.Options) error {
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
// Install the template
|
||||
err := templates.Install(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Output stats
|
||||
elapsed := time.Since(start)
|
||||
options.Logger.Println("")
|
||||
options.Logger.Println("Project Name: " + options.ProjectName)
|
||||
options.Logger.Println("Project Directory: " + options.TargetDir)
|
||||
options.Logger.Println("Project Template: " + options.TemplateName)
|
||||
options.Logger.Println("")
|
||||
if options.GenerateVSCode {
|
||||
options.Logger.Println("VSCode config files generated.")
|
||||
}
|
||||
options.Logger.Println("")
|
||||
options.Logger.Println(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
||||
options.Logger.Println("")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
|
||||
)
|
||||
|
||||
func fatal(message string) {
|
||||
println(message)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var err error
|
||||
version := "v2.0.0-alpha"
|
||||
|
||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||
|
||||
build.AddBuildSubcommand(app, os.Stdout)
|
||||
err = initialise.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
err = doctor.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = dev.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = generate.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = app.Run()
|
||||
if err != nil {
|
||||
println("\n\nERROR: " + err.Error())
|
||||
}
|
||||
}
|
||||
26
v2/go.mod
26
v2/go.mod
@@ -1,26 +0,0 @@
|
||||
module github.com/wailsapp/wails/v2
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/gosod v0.0.4
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.6 // indirect
|
||||
github.com/xyproto/xpm v1.2.1
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
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
|
||||
)
|
||||
130
v2/go.sum
130
v2/go.sum
@@ -1,130 +0,0 @@
|
||||
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=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
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-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
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/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/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
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/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/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
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/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
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/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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
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/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
|
||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
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-20200625203802-6e8e738ad208/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-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-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-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
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.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/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-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/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-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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
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 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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
@@ -1,9 +0,0 @@
|
||||
// +build debug
|
||||
|
||||
package app
|
||||
|
||||
// Init initialises the application for a debug environment
|
||||
func (a *App) Init() error {
|
||||
a.debug = true
|
||||
return nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// +build !desktop,!hybrid,!server
|
||||
|
||||
package app
|
||||
|
||||
// This is the default application that will get run if the user compiles using `go build`.
|
||||
// The reason we want to prevent that is that the `wails build` command does a lot of behind
|
||||
// the scenes work such as asset compilation. If we allow `go build`, the state of these assets
|
||||
// will be unknown and the application will not work as expected.
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
type App struct {
|
||||
Title string
|
||||
Width int
|
||||
Height int
|
||||
Resizable bool
|
||||
|
||||
// Indicates if the app is running in debug mode
|
||||
debug bool
|
||||
}
|
||||
|
||||
// CreateApp returns a null application
|
||||
func CreateApp(options *options.App) *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
// Run the application
|
||||
func (a *App) Run() error {
|
||||
println(`FATAL: This application was built using "go build". This is unsupported. Please compile using "wails build".`)
|
||||
os.Exit(1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bind the dummy interface
|
||||
func (a *App) Bind(dummy interface{}) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
// +build desktop,!server
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/internal/signal"
|
||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
type App struct {
|
||||
window *ffenestri.Application
|
||||
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
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
|
||||
// Indicates if the app is in debug mode
|
||||
debug bool
|
||||
|
||||
// This is our binding DB
|
||||
bindings *binding.Bindings
|
||||
|
||||
// LogLevel Store
|
||||
loglevelStore *runtime.Store
|
||||
}
|
||||
|
||||
// Create App
|
||||
func CreateApp(options *options.App) *App {
|
||||
|
||||
// Merge default options
|
||||
options.MergeDefaults()
|
||||
|
||||
// Set up logger
|
||||
myLogger := logger.New(options.Logger)
|
||||
myLogger.SetLogLevel(options.LogLevel)
|
||||
|
||||
window := ffenestri.NewApplicationWithConfig(options, myLogger)
|
||||
|
||||
result := &App{
|
||||
window: window,
|
||||
servicebus: servicebus.New(myLogger),
|
||||
logger: myLogger,
|
||||
bindings: binding.NewBindings(myLogger),
|
||||
}
|
||||
|
||||
result.options = options
|
||||
|
||||
// Initialise the app
|
||||
result.Init()
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
// Run the application
|
||||
func (a *App) Run() error {
|
||||
|
||||
// Setup signal handler
|
||||
signal, err := signal.NewManager(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.signal = signal
|
||||
a.signal.Start()
|
||||
|
||||
// Start the service bus
|
||||
a.servicebus.Debug()
|
||||
a.servicebus.Start()
|
||||
|
||||
// Start the runtime
|
||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.runtime = runtime
|
||||
a.runtime.Start()
|
||||
|
||||
// Application Stores
|
||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("loglevel", a.options.LogLevel)
|
||||
|
||||
// Start the binding subsystem
|
||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.binding = binding
|
||||
a.binding.Start()
|
||||
|
||||
// Start the logging subsystem
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.log = log
|
||||
a.log.Start()
|
||||
|
||||
// create the dispatcher
|
||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.dispatcher = dispatcher
|
||||
dispatcher.Start()
|
||||
|
||||
// Start the eventing subsystem
|
||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.event = event
|
||||
a.event.Start()
|
||||
|
||||
// Start the call subsystem
|
||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.call = call
|
||||
a.call.Start()
|
||||
|
||||
// Dump bindings as a debug
|
||||
bindingDump, err := a.bindings.ToJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := a.window.Run(dispatcher, bindingDump)
|
||||
a.logger.Trace("Ffenestri.Run() exited")
|
||||
a.servicebus.Stop()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Bind a struct to the application by passing in
|
||||
// a pointer to it
|
||||
func (a *App) Bind(structPtr interface{}) {
|
||||
|
||||
// Add the struct to the bindings
|
||||
err := a.bindings.Add(structPtr)
|
||||
if err != nil {
|
||||
a.logger.Fatal("Error during binding: " + err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
// +build !server,!desktop,hybrid
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
||||
)
|
||||
|
||||
// Config defines the Application's configuration
|
||||
type Config struct {
|
||||
Title string // Title is the value to be displayed in the title bar
|
||||
Width int // Width is the desired window width
|
||||
Height int // Height is the desired window height
|
||||
DevTools bool // DevTools enables or disables the browser development tools
|
||||
Resizable bool // Resizable when False prevents window resizing
|
||||
ServerEnabled bool // ServerEnabled when True allows remote connections
|
||||
}
|
||||
|
||||
// App defines a Wails application structure
|
||||
type App struct {
|
||||
config Config
|
||||
window *ffenestri.Application
|
||||
webserver *webserver.WebServer
|
||||
binding *subsystem.Binding
|
||||
call *subsystem.Call
|
||||
event *subsystem.Event
|
||||
log *subsystem.Log
|
||||
runtime *subsystem.Runtime
|
||||
|
||||
bindings *binding.Bindings
|
||||
logger *logger.Logger
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
servicebus *servicebus.ServiceBus
|
||||
|
||||
debug bool
|
||||
}
|
||||
|
||||
// Create App
|
||||
func CreateApp(options *Options) *App {
|
||||
|
||||
// Merge default options
|
||||
options.mergeDefaults()
|
||||
|
||||
// Set up logger
|
||||
myLogger := logger.New(os.Stdout)
|
||||
myLogger.SetLogLevel(logger.INFO)
|
||||
|
||||
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
||||
Title: options.Title,
|
||||
Width: options.Width,
|
||||
Height: options.Height,
|
||||
MinWidth: options.MinWidth,
|
||||
MinHeight: options.MinHeight,
|
||||
MaxWidth: options.MaxWidth,
|
||||
MaxHeight: options.MaxHeight,
|
||||
StartHidden: options.StartHidden,
|
||||
|
||||
// This should be controlled by the compile time flags...
|
||||
DevTools: true,
|
||||
|
||||
Resizable: !options.DisableResize,
|
||||
Fullscreen: options.Fullscreen,
|
||||
}, myLogger)
|
||||
|
||||
app := &App{
|
||||
window: window,
|
||||
webserver: webserver.NewWebServer(myLogger),
|
||||
servicebus: servicebus.New(myLogger),
|
||||
logger: myLogger,
|
||||
bindings: binding.NewBindings(myLogger),
|
||||
}
|
||||
|
||||
// Initialise the app
|
||||
app.Init()
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// Run the application
|
||||
func (a *App) Run() error {
|
||||
|
||||
// Default app options
|
||||
var port = 8080
|
||||
var ip = "localhost"
|
||||
var suppressLogging = false
|
||||
|
||||
// Create CLI
|
||||
cli := clir.NewCli(filepath.Base(os.Args[0]), "Desktop/Server Build", "")
|
||||
|
||||
// Setup flags
|
||||
cli.IntFlag("p", "Port to serve on", &port)
|
||||
cli.StringFlag("i", "IP to serve on", &ip)
|
||||
cli.BoolFlag("q", "Suppress logging", &suppressLogging)
|
||||
|
||||
// Setup main action
|
||||
cli.Action(func() error {
|
||||
|
||||
// Set IP + Port
|
||||
a.webserver.SetPort(port)
|
||||
a.webserver.SetIP(ip)
|
||||
a.webserver.SetBindings(a.bindings)
|
||||
// Log information (if we aren't suppressing it)
|
||||
if !suppressLogging {
|
||||
cli.PrintBanner()
|
||||
a.logger.Info("Running server at %s", a.webserver.URL())
|
||||
}
|
||||
|
||||
a.servicebus.Start()
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.log = log
|
||||
a.log.Start()
|
||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.dispatcher = dispatcher
|
||||
a.dispatcher.Start()
|
||||
|
||||
// Start the runtime
|
||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.runtime = runtime
|
||||
a.runtime.Start()
|
||||
|
||||
// Start the binding subsystem
|
||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.binding = binding
|
||||
a.binding.Start()
|
||||
|
||||
// Start the eventing subsystem
|
||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.event = event
|
||||
a.event.Start()
|
||||
|
||||
// Start the call subsystem
|
||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.call = call
|
||||
a.call.Start()
|
||||
|
||||
// Required so that the WailsInit functions are fired!
|
||||
runtime.GoRuntime().Events.Emit("wails:loaded")
|
||||
|
||||
// Set IP + Port
|
||||
a.webserver.SetPort(port)
|
||||
a.webserver.SetIP(ip)
|
||||
|
||||
// Log information (if we aren't suppressing it)
|
||||
if !suppressLogging {
|
||||
cli.PrintBanner()
|
||||
println("Running server at " + a.webserver.URL())
|
||||
}
|
||||
|
||||
// Dump bindings as a debug
|
||||
bindingDump, err := a.bindings.ToJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := a.webserver.Start(dispatcher, event); err != nil {
|
||||
a.logger.Error("Webserver failed to start %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
result := a.window.Run(dispatcher, bindingDump)
|
||||
a.servicebus.Stop()
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
return cli.Run()
|
||||
}
|
||||
|
||||
// Bind a struct to the application by passing in
|
||||
// a pointer to it
|
||||
func (a *App) Bind(structPtr interface{}) {
|
||||
|
||||
// Add the struct to the bindings
|
||||
err := a.bindings.Add(structPtr)
|
||||
if err != nil {
|
||||
a.logger.Fatal("Error during binding: " + err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// +build !debug
|
||||
|
||||
package app
|
||||
|
||||
// Init initialises the application for a production environment
|
||||
func (a *App) Init() error {
|
||||
println("Processing production cli options")
|
||||
return nil
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
// +build server,!desktop
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"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/servicebus"
|
||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
type App struct {
|
||||
binding *subsystem.Binding
|
||||
call *subsystem.Call
|
||||
event *subsystem.Event
|
||||
log *subsystem.Log
|
||||
runtime *subsystem.Runtime
|
||||
|
||||
bindings *binding.Bindings
|
||||
logger *logger.Logger
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
servicebus *servicebus.ServiceBus
|
||||
webserver *webserver.WebServer
|
||||
|
||||
debug bool
|
||||
}
|
||||
|
||||
// Create App
|
||||
func CreateApp(options *Options) *App {
|
||||
options.mergeDefaults()
|
||||
// We ignore the inputs (for now)
|
||||
|
||||
// TODO: Allow logger output override on CLI
|
||||
myLogger := logger.New(os.Stdout)
|
||||
myLogger.SetLogLevel(logger.TRACE)
|
||||
|
||||
result := &App{
|
||||
bindings: binding.NewBindings(myLogger),
|
||||
logger: myLogger,
|
||||
servicebus: servicebus.New(myLogger),
|
||||
webserver: webserver.NewWebServer(myLogger),
|
||||
}
|
||||
|
||||
// Initialise app
|
||||
result.Init()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Run the application
|
||||
func (a *App) Run() error {
|
||||
|
||||
// Default app options
|
||||
var port = 8080
|
||||
var ip = "localhost"
|
||||
var supressLogging = false
|
||||
var debugMode = false
|
||||
|
||||
// Create CLI
|
||||
cli := clir.NewCli(filepath.Base(os.Args[0]), "Server Build", "")
|
||||
|
||||
// Setup flags
|
||||
cli.IntFlag("p", "Port to serve on", &port)
|
||||
cli.StringFlag("i", "IP to serve on", &ip)
|
||||
cli.BoolFlag("d", "Debug mode", &debugMode)
|
||||
cli.BoolFlag("q", "Supress logging", &supressLogging)
|
||||
|
||||
// Setup main action
|
||||
cli.Action(func() error {
|
||||
|
||||
// Set IP + Port
|
||||
a.webserver.SetPort(port)
|
||||
a.webserver.SetIP(ip)
|
||||
a.webserver.SetBindings(a.bindings)
|
||||
// Log information (if we aren't supressing it)
|
||||
if !supressLogging {
|
||||
cli.PrintBanner()
|
||||
a.logger.Info("Running server at %s", a.webserver.URL())
|
||||
}
|
||||
|
||||
if debugMode {
|
||||
a.servicebus.Debug()
|
||||
}
|
||||
a.servicebus.Start()
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.log = log
|
||||
a.log.Start()
|
||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.dispatcher = dispatcher
|
||||
a.dispatcher.Start()
|
||||
|
||||
// Start the runtime
|
||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.runtime = runtime
|
||||
a.runtime.Start()
|
||||
|
||||
// Start the binding subsystem
|
||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.binding = binding
|
||||
a.binding.Start()
|
||||
|
||||
// Start the eventing subsystem
|
||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.event = event
|
||||
a.event.Start()
|
||||
|
||||
// Start the call subsystem
|
||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.call = call
|
||||
a.call.Start()
|
||||
|
||||
// Required so that the WailsInit functions are fired!
|
||||
runtime.GoRuntime().Events.Emit("wails:loaded")
|
||||
|
||||
if err := a.webserver.Start(dispatcher, event); err != nil {
|
||||
a.logger.Error("Webserver failed to start %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return cli.Run()
|
||||
}
|
||||
|
||||
// Bind a struct to the application by passing in
|
||||
// a pointer to it
|
||||
func (a *App) Bind(structPtr interface{}) {
|
||||
|
||||
// Add the struct to the bindings
|
||||
err := a.bindings.Add(structPtr)
|
||||
if err != nil {
|
||||
a.logger.Fatal("Error during binding: " + err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package assetdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// AssetDB is a database for assets encoded as byte slices
|
||||
type AssetDB struct {
|
||||
db map[string][]byte
|
||||
}
|
||||
|
||||
// NewAssetDB creates a new AssetDB and initialises a blank db
|
||||
func NewAssetDB() *AssetDB {
|
||||
return &AssetDB{
|
||||
db: make(map[string][]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// AddAsset saves the given byte slice under the given name
|
||||
func (a *AssetDB) AddAsset(name string, data []byte) {
|
||||
a.db[name] = data
|
||||
}
|
||||
|
||||
// Remove removes the named asset
|
||||
func (a *AssetDB) Remove(name string) {
|
||||
delete(a.db, name)
|
||||
}
|
||||
|
||||
// Asset retrieves the byte slice for the given name
|
||||
func (a *AssetDB) Read(name string) ([]byte, error) {
|
||||
result := a.db[name]
|
||||
if result == nil {
|
||||
return nil, fmt.Errorf("asset '%s' not found", name)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// AssetAsString returns the asset as a string.
|
||||
// It also returns a boolean indicating whether the asset existed or not.
|
||||
func (a *AssetDB) String(name string) (string, error) {
|
||||
asset, err := a.Read(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&asset)), nil
|
||||
}
|
||||
|
||||
func (a *AssetDB) Dump() {
|
||||
fmt.Printf("Assets:\n")
|
||||
for k, _ := range a.db {
|
||||
fmt.Println(k)
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize converts the entire database to a file that when compiled will
|
||||
// reconstruct the AssetDB during init()
|
||||
// name: name of the asset.AssetDB instance
|
||||
// pkg: package name placed at the top of the file
|
||||
func (a *AssetDB) Serialize(name, pkg string) string {
|
||||
var cdata strings.Builder
|
||||
// Set buffer size to 4k
|
||||
cdata.Grow(4096)
|
||||
|
||||
// Write header
|
||||
header := `// DO NOT EDIT - Generated automatically
|
||||
package %s
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/assetdb"
|
||||
|
||||
var (
|
||||
%s *assetdb.AssetDB = assetdb.NewAssetDB()
|
||||
)
|
||||
|
||||
// AssetsDB is a clean interface to the assetdb.AssetDB struct
|
||||
type AssetsDB interface {
|
||||
Read(string) ([]byte, error)
|
||||
String(string) (string, error)
|
||||
}
|
||||
|
||||
// Assets returns the asset database
|
||||
func Assets() AssetsDB {
|
||||
return %s
|
||||
}
|
||||
|
||||
func init() {
|
||||
`
|
||||
cdata.WriteString(fmt.Sprintf(header, pkg, name, name))
|
||||
|
||||
for aname, bytes := range a.db {
|
||||
cdata.WriteString(fmt.Sprintf("\t%s.AddAsset(\"%s\", []byte{",
|
||||
name,
|
||||
aname))
|
||||
|
||||
l := len(bytes)
|
||||
if l == 0 {
|
||||
cdata.WriteString("0x00})\n")
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert each byte to hex
|
||||
for _, b := range bytes[:l-1] {
|
||||
cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
||||
}
|
||||
cdata.WriteString(fmt.Sprintf("0x%x})\n", bytes[l-1]))
|
||||
}
|
||||
|
||||
cdata.WriteString(`}`)
|
||||
|
||||
return cdata.String()
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package assetdb
|
||||
|
||||
import "testing"
|
||||
import "github.com/matryer/is"
|
||||
|
||||
func TestExistsAsBytes(t *testing.T) {
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("hello", helloworld)
|
||||
|
||||
result, err := db.Read("hello")
|
||||
|
||||
is.True(err == nil)
|
||||
is.Equal(result, helloworld)
|
||||
}
|
||||
|
||||
func TestNotExistsAsBytes(t *testing.T) {
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("hello4", helloworld)
|
||||
|
||||
result, err := db.Read("hello")
|
||||
|
||||
is.True(err != nil)
|
||||
is.True(result == nil)
|
||||
}
|
||||
|
||||
func TestExistsAsString(t *testing.T) {
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("hello", helloworld)
|
||||
|
||||
result, err := db.String("hello")
|
||||
|
||||
// Ensure it exists
|
||||
is.True(err == nil)
|
||||
|
||||
// Ensure the string is the same as the byte slice
|
||||
is.Equal(result, "Hello, World!")
|
||||
}
|
||||
|
||||
func TestNotExistsAsString(t *testing.T) {
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("hello", helloworld)
|
||||
|
||||
result, err := db.String("help")
|
||||
|
||||
// Ensure it doesn't exist
|
||||
is.True(err != nil)
|
||||
|
||||
// Ensure the string is blank
|
||||
is.Equal(result, "")
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
// +build !desktop
|
||||
package assetdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errWhence = errors.New("Seek: invalid whence")
|
||||
var errOffset = errors.New("Seek: invalid offset")
|
||||
|
||||
// Open implements the http.FileSystem interface for the AssetDB
|
||||
func (a *AssetDB) Open(name string) (http.File, error) {
|
||||
if name == "/" || name == "" {
|
||||
return &Entry{name: "/", dir: true, db: a}, nil
|
||||
}
|
||||
|
||||
if data, ok := a.db[name]; ok {
|
||||
return &Entry{name: name, data: data, size: len(data)}, nil
|
||||
} else {
|
||||
for n, _ := range a.db {
|
||||
if strings.HasPrefix(n, name+"/") {
|
||||
return &Entry{name: name, db: a, dir: true}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return &Entry{}, os.ErrNotExist
|
||||
}
|
||||
|
||||
// readdir returns the directory entries for the requested directory
|
||||
func (a *AssetDB) readdir(name string) ([]os.FileInfo, error) {
|
||||
dir := name
|
||||
var ents []string
|
||||
|
||||
fim := make(map[string]os.FileInfo)
|
||||
for fn, data := range a.db {
|
||||
if strings.HasPrefix(fn, dir) {
|
||||
pieces := strings.Split(fn[len(dir)+1:], "/")
|
||||
if len(pieces) == 1 {
|
||||
fim[pieces[0]] = FI{name: pieces[0], dir: false, size: len(data)}
|
||||
ents = append(ents, pieces[0])
|
||||
} else {
|
||||
fim[pieces[0]] = FI{name: pieces[0], dir: true, size: -1}
|
||||
ents = append(ents, pieces[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(ents) == 0 {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
|
||||
sort.Strings(ents)
|
||||
var list []os.FileInfo
|
||||
for _, dir := range ents {
|
||||
list = append(list, fim[dir])
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Entry implements the http.File interface to allow for
|
||||
// use in the http.FileSystem implementation of AssetDB
|
||||
type Entry struct {
|
||||
name string
|
||||
data []byte
|
||||
dir bool
|
||||
size int
|
||||
db *AssetDB
|
||||
off int
|
||||
}
|
||||
|
||||
// Close is a noop
|
||||
func (e Entry) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read fills the slice provided returning how many bytes were written
|
||||
// and any errors encountered
|
||||
func (e *Entry) Read(p []byte) (n int, err error) {
|
||||
if e.off >= e.size {
|
||||
return 0, io.EOF
|
||||
}
|
||||
numout := len(p)
|
||||
if numout > e.size {
|
||||
numout = e.size
|
||||
}
|
||||
for i := 0; i < numout; i++ {
|
||||
p[i] = e.data[e.off+i]
|
||||
}
|
||||
e.off += numout
|
||||
n = int(numout)
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// Seek seeks to the specified offset from the location specified by whence
|
||||
func (e *Entry) Seek(offset int64, whence int) (int64, error) {
|
||||
switch whence {
|
||||
default:
|
||||
return 0, errWhence
|
||||
case io.SeekStart:
|
||||
offset += 0
|
||||
case io.SeekCurrent:
|
||||
offset += int64(e.off)
|
||||
case io.SeekEnd:
|
||||
offset += int64(e.size)
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
return 0, errOffset
|
||||
}
|
||||
e.off = int(offset)
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// Readdir returns the directory entries inside this entry if it is a directory
|
||||
func (e Entry) Readdir(count int) ([]os.FileInfo, error) {
|
||||
ents := []os.FileInfo{}
|
||||
if !e.dir {
|
||||
return ents, errors.New("Not a directory")
|
||||
}
|
||||
return e.db.readdir(e.name)
|
||||
}
|
||||
|
||||
// Stat returns information about this directory entry
|
||||
func (e Entry) Stat() (os.FileInfo, error) {
|
||||
return FI{e.name, e.size, e.dir}, nil
|
||||
}
|
||||
|
||||
// FI is the AssetDB implementation of os.FileInfo.
|
||||
type FI struct {
|
||||
name string
|
||||
size int
|
||||
dir bool
|
||||
}
|
||||
|
||||
// IsDir returns true if this is a directory
|
||||
func (fi FI) IsDir() bool {
|
||||
return fi.dir
|
||||
}
|
||||
|
||||
// ModTime always returns now
|
||||
func (fi FI) ModTime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Mode returns the file as readonly and directories
|
||||
// as world writeable and executable
|
||||
func (fi FI) Mode() os.FileMode {
|
||||
if fi.IsDir() {
|
||||
return 0755 | os.ModeDir
|
||||
}
|
||||
return 0444
|
||||
}
|
||||
|
||||
// Name returns the name of this object without
|
||||
// any leading slashes
|
||||
func (fi FI) Name() string {
|
||||
return path.Base(fi.name)
|
||||
}
|
||||
|
||||
// Size returns the size of this item
|
||||
func (fi FI) Size() int64 {
|
||||
return int64(fi.size)
|
||||
}
|
||||
|
||||
// Sys returns nil
|
||||
func (fi FI) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package assetdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestOpenLeadingSlash(t *testing.T) {
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("/hello", helloworld)
|
||||
|
||||
file, err := db.Open("/hello")
|
||||
// Ensure it does exist
|
||||
is.True(err == nil)
|
||||
|
||||
buff := make([]byte, len(helloworld))
|
||||
n, err := file.Read(buff)
|
||||
fmt.Printf("Error %v\n", err)
|
||||
is.True(err == nil)
|
||||
is.Equal(n, len(helloworld))
|
||||
result := string(buff)
|
||||
|
||||
// Ensure the string is blank
|
||||
is.Equal(result, string(helloworld))
|
||||
}
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("/hello", helloworld)
|
||||
|
||||
file, err := db.Open("/hello")
|
||||
|
||||
// Ensure it does exist
|
||||
is.True(err == nil)
|
||||
|
||||
buff := make([]byte, len(helloworld))
|
||||
n, err := file.Read(buff)
|
||||
is.True(err == nil)
|
||||
is.Equal(n, len(helloworld))
|
||||
result := string(buff)
|
||||
|
||||
// Ensure the string is blank
|
||||
is.Equal(result, string(helloworld))
|
||||
}
|
||||
|
||||
func TestReaddir(t *testing.T) {
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("/hello", helloworld)
|
||||
db.AddAsset("/directory/hello", helloworld)
|
||||
db.AddAsset("/directory/subdirectory/hello", helloworld)
|
||||
|
||||
dir, err := db.Open("/doesntexist")
|
||||
is.True(err == os.ErrNotExist)
|
||||
ents, err := dir.Readdir(-1)
|
||||
is.Equal([]os.FileInfo{}, ents)
|
||||
|
||||
dir, err = db.Open("/")
|
||||
is.True(dir != nil)
|
||||
is.True(err == nil)
|
||||
ents, err = dir.Readdir(-1)
|
||||
is.True(err == nil)
|
||||
is.Equal(3, len(ents))
|
||||
}
|
||||
|
||||
func TestReaddirSubdirectory(t *testing.T) {
|
||||
is := is.New(t)
|
||||
|
||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
||||
|
||||
db := NewAssetDB()
|
||||
db.AddAsset("/hello", helloworld)
|
||||
db.AddAsset("/directory/hello", helloworld)
|
||||
db.AddAsset("/directory/subdirectory/hello", helloworld)
|
||||
|
||||
expected := []os.FileInfo{
|
||||
FI{name: "hello", dir: false, size: len(helloworld)},
|
||||
FI{name: "subdirectory", dir: true, size: -1},
|
||||
}
|
||||
|
||||
dir, err := db.Open("/directory")
|
||||
is.True(dir != nil)
|
||||
is.True(err == nil)
|
||||
ents, err := dir.Readdir(-1)
|
||||
is.Equal(expected, ents)
|
||||
|
||||
// Check sub-subdirectory
|
||||
dir, err = db.Open("/directory/subdirectory")
|
||||
is.True(dir != nil)
|
||||
is.True(err == nil)
|
||||
ents, err = dir.Readdir(-1)
|
||||
is.True(err == nil)
|
||||
is.Equal([]os.FileInfo{FI{name: "hello", size: len(helloworld)}}, ents)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package bind
|
||||
|
||||
func IsStructPointer(value interface{}) bool {
|
||||
switch t := value.(type) {
|
||||
default:
|
||||
println(t)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
type Bindings struct {
|
||||
db *DB
|
||||
logger logger.CustomLogger
|
||||
}
|
||||
|
||||
// NewBindings returns a new Bindings object
|
||||
func NewBindings(logger *logger.Logger) *Bindings {
|
||||
return &Bindings{
|
||||
db: newDB(),
|
||||
logger: logger.CustomLogger("Bindings"),
|
||||
}
|
||||
}
|
||||
|
||||
// Add the given struct methods to the Bindings
|
||||
func (b *Bindings) Add(structPtr interface{}) error {
|
||||
|
||||
methods, err := getMethods(structPtr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannout bind value to app: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, method := range methods {
|
||||
splitName := strings.Split(method.Name, ".")
|
||||
packageName := splitName[0]
|
||||
structName := splitName[1]
|
||||
methodName := splitName[2]
|
||||
|
||||
// Is this WailsInit?
|
||||
if method.IsWailsInit() {
|
||||
err := b.db.AddWailsInit(method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.logger.Trace("Registered WailsInit method: %s", method.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Is this WailsShutdown?
|
||||
if method.IsWailsShutdown() {
|
||||
err := b.db.AddWailsShutdown(method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.logger.Trace("Registered WailsShutdown method: %s", method.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add it as a regular method
|
||||
b.db.AddMethod(packageName, structName, methodName, method)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bindings) DB() *DB {
|
||||
return b.db
|
||||
}
|
||||
|
||||
func (b *Bindings) ToJSON() (string, error) {
|
||||
return b.db.ToJSON()
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BoundMethod defines all the data related to a Go method that is
|
||||
// bound to the Wails application
|
||||
type BoundMethod struct {
|
||||
Name string `json:"name"`
|
||||
Inputs []*Parameter `json:"inputs,omitempty"`
|
||||
Outputs []*Parameter `json:"outputs,omitempty"`
|
||||
Comments string `json:"comments,omitempty"`
|
||||
Method reflect.Value `json:"-"`
|
||||
}
|
||||
|
||||
// IsWailsInit returns true if the method name is "WailsInit"
|
||||
func (b *BoundMethod) IsWailsInit() bool {
|
||||
return strings.HasSuffix(b.Name, "WailsInit")
|
||||
}
|
||||
|
||||
// IsWailsShutdown returns true if the method name is "WailsShutdown"
|
||||
func (b *BoundMethod) IsWailsShutdown() bool {
|
||||
return strings.HasSuffix(b.Name, "WailsShutdown")
|
||||
}
|
||||
|
||||
// VerifyWailsInit checks if the WailsInit signature is correct
|
||||
func (b *BoundMethod) VerifyWailsInit() error {
|
||||
// Must only have 1 input
|
||||
if b.InputCount() != 1 {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Check input type
|
||||
if !b.Inputs[0].IsType("*runtime.Runtime") {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Must only have 1 output
|
||||
if b.OutputCount() != 1 {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Check output type
|
||||
if !b.Outputs[0].IsError() {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Input must be of type Runtime
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyWailsShutdown checks if the WailsShutdown signature is correct
|
||||
func (b *BoundMethod) VerifyWailsShutdown() error {
|
||||
// Must have no inputs
|
||||
if b.InputCount() != 0 {
|
||||
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||
}
|
||||
|
||||
// Must have no outputs
|
||||
if b.OutputCount() != 0 {
|
||||
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||
}
|
||||
|
||||
// Input must be of type Runtime
|
||||
return nil
|
||||
}
|
||||
|
||||
// InputCount returns the number of inputs this bound method has
|
||||
func (b *BoundMethod) InputCount() int {
|
||||
return len(b.Inputs)
|
||||
}
|
||||
|
||||
// OutputCount returns the number of outputs this bound method has
|
||||
func (b *BoundMethod) OutputCount() int {
|
||||
return len(b.Outputs)
|
||||
}
|
||||
|
||||
// ParseArgs method converts the input json into the types expected by the method
|
||||
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
||||
|
||||
result := make([]interface{}, b.InputCount())
|
||||
for index, arg := range args {
|
||||
typ := b.Inputs[index].reflectType
|
||||
inputValue := reflect.New(typ).Interface()
|
||||
err := json.Unmarshal(arg, inputValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if inputValue == nil {
|
||||
result[index] = reflect.Zero(typ).Interface()
|
||||
} else {
|
||||
result[index] = reflect.ValueOf(inputValue).Elem().Interface()
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Call will attempt to call this bound method with the given args
|
||||
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
||||
// Check inputs
|
||||
expectedInputLength := len(b.Inputs)
|
||||
actualInputLength := len(args)
|
||||
if expectedInputLength != actualInputLength {
|
||||
return nil, fmt.Errorf("%s takes %d inputs. Received %d", b.Name, expectedInputLength, actualInputLength)
|
||||
}
|
||||
|
||||
/** Convert inputs to reflect values **/
|
||||
|
||||
// Create slice for the input arguments to the method call
|
||||
callArgs := make([]reflect.Value, expectedInputLength)
|
||||
|
||||
// Iterate over given arguments
|
||||
for index, arg := range args {
|
||||
// Save the converted argument
|
||||
callArgs[index] = reflect.ValueOf(arg)
|
||||
}
|
||||
|
||||
// Do the call
|
||||
callResults := b.Method.Call(callArgs)
|
||||
|
||||
//** Check results **//
|
||||
var returnValue interface{}
|
||||
var err error
|
||||
|
||||
switch b.OutputCount() {
|
||||
case 1:
|
||||
// Loop over results and determine if the result
|
||||
// is an error or not
|
||||
for _, result := range callResults {
|
||||
interfac := result.Interface()
|
||||
temp, ok := interfac.(error)
|
||||
if ok {
|
||||
err = temp
|
||||
} else {
|
||||
returnValue = interfac
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
returnValue = callResults[0].Interface()
|
||||
if temp, ok := callResults[1].Interface().(error); ok {
|
||||
err = temp
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue, err
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DB is our database of method bindings
|
||||
type DB struct {
|
||||
// map[packagename] -> map[structname] -> map[methodname]*method
|
||||
store map[string]map[string]map[string]*BoundMethod
|
||||
|
||||
// This uses fully qualified method names as a shortcut for store traversal.
|
||||
// It used for performance gains at runtime
|
||||
methodMap map[string]*BoundMethod
|
||||
|
||||
// These are slices of methods registered using WailsInit and WailsShutdown
|
||||
wailsInitMethods []*BoundMethod
|
||||
wailsShutdownMethods []*BoundMethod
|
||||
|
||||
// Lock to ensure sync access to the data
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func newDB() *DB {
|
||||
return &DB{
|
||||
store: make(map[string]map[string]map[string]*BoundMethod),
|
||||
methodMap: make(map[string]*BoundMethod),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMethodFromStore returns the method for the given package/struct/method names
|
||||
// nil is returned if any one of those does not exist
|
||||
func (d *DB) GetMethodFromStore(packageName string, structName string, methodName string) *BoundMethod {
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
structMap, exists := d.store[packageName]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
methodMap, exists := structMap[structName]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
return methodMap[methodName]
|
||||
}
|
||||
|
||||
// GetMethod returns the method for the given qualified method name
|
||||
// qualifiedMethodName is "packagename.structname.methodname"
|
||||
func (d *DB) GetMethod(qualifiedMethodName string) *BoundMethod {
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
return d.methodMap[qualifiedMethodName]
|
||||
}
|
||||
|
||||
// AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName
|
||||
func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
|
||||
|
||||
// TODO: Validate inputs?
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
// Get the map associated with the package name
|
||||
structMap, exists := d.store[packageName]
|
||||
if !exists {
|
||||
// Create a new map for this packagename
|
||||
d.store[packageName] = make(map[string]map[string]*BoundMethod)
|
||||
structMap = d.store[packageName]
|
||||
}
|
||||
|
||||
// Get the map associated with the struct name
|
||||
methodMap, exists := structMap[structName]
|
||||
if !exists {
|
||||
// Create a new map for this packagename
|
||||
structMap[structName] = make(map[string]*BoundMethod)
|
||||
methodMap = structMap[structName]
|
||||
}
|
||||
|
||||
// Store the method definition
|
||||
methodMap[methodName] = methodDefinition
|
||||
|
||||
// Store in the methodMap
|
||||
key := packageName + "." + structName + "." + methodName
|
||||
d.methodMap[key] = methodDefinition
|
||||
|
||||
}
|
||||
|
||||
// AddWailsInit checks the given method is a WailsInit method and if it
|
||||
// is, adds it to the list of WailsInit methods
|
||||
func (d *DB) AddWailsInit(method *BoundMethod) error {
|
||||
err := method.VerifyWailsInit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
d.wailsInitMethods = append(d.wailsInitMethods, method)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddWailsShutdown checks the given method is a WailsInit method and if it
|
||||
// is, adds it to the list of WailsShutdown methods
|
||||
func (d *DB) AddWailsShutdown(method *BoundMethod) error {
|
||||
err := method.VerifyWailsShutdown()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
d.wailsShutdownMethods = append(d.wailsShutdownMethods, method)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToJSON converts the method map to JSON
|
||||
func (d *DB) ToJSON() (string, error) {
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.RLock()
|
||||
defer d.lock.RUnlock()
|
||||
|
||||
bytes, err := json.Marshal(&d.store)
|
||||
|
||||
// Return zero copy string as this string will be read only
|
||||
return *(*string)(unsafe.Pointer(&bytes)), err
|
||||
}
|
||||
|
||||
// WailsInitMethods returns the list of registered WailsInit methods
|
||||
func (d *DB) WailsInitMethods() []*BoundMethod {
|
||||
return d.wailsInitMethods
|
||||
}
|
||||
|
||||
// WailsShutdownMethods returns the list of registered WailsInit methods
|
||||
func (d *DB) WailsShutdownMethods() []*BoundMethod {
|
||||
return d.wailsShutdownMethods
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package binding
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Parameter defines a Go method parameter
|
||||
type Parameter struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
TypeName string `json:"type"`
|
||||
reflectType reflect.Type
|
||||
}
|
||||
|
||||
func newParameter(Name string, Type reflect.Type) *Parameter {
|
||||
return &Parameter{
|
||||
Name: Name,
|
||||
TypeName: Type.String(),
|
||||
reflectType: Type,
|
||||
}
|
||||
}
|
||||
|
||||
// IsType returns true if the given
|
||||
func (p *Parameter) IsType(typename string) bool {
|
||||
return p.TypeName == typename
|
||||
}
|
||||
|
||||
// IsError returns true if the parameter type is an error
|
||||
func (p *Parameter) IsError() bool {
|
||||
return p.IsType("error")
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// isStructPtr returns true if the value given is a
|
||||
// pointer to a struct
|
||||
func isStructPtr(value interface{}) bool {
|
||||
return reflect.ValueOf(value).Kind() == reflect.Ptr &&
|
||||
reflect.ValueOf(value).Elem().Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
// isStructPtr returns true if the value given is a struct
|
||||
func isStruct(value interface{}) bool {
|
||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||
|
||||
// Create result placeholder
|
||||
var result []*BoundMethod
|
||||
|
||||
// Check type
|
||||
if !isStructPtr(value) {
|
||||
|
||||
if isStruct(value) {
|
||||
name := reflect.ValueOf(value).Type().Name()
|
||||
return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not a pointer to a struct")
|
||||
}
|
||||
|
||||
// Process Struct
|
||||
structType := reflect.TypeOf(value)
|
||||
structValue := reflect.ValueOf(value)
|
||||
baseName := structType.String()[1:]
|
||||
|
||||
// Process Methods
|
||||
for i := 0; i < structType.NumMethod(); i++ {
|
||||
methodDef := structType.Method(i)
|
||||
methodName := methodDef.Name
|
||||
fullMethodName := baseName + "." + methodName
|
||||
method := structValue.MethodByName(methodName)
|
||||
|
||||
// Create new method
|
||||
boundMethod := &BoundMethod{
|
||||
Name: fullMethodName,
|
||||
Inputs: nil,
|
||||
Outputs: nil,
|
||||
Comments: "",
|
||||
Method: method,
|
||||
}
|
||||
|
||||
// Iterate inputs
|
||||
methodType := method.Type()
|
||||
inputParamCount := methodType.NumIn()
|
||||
var inputs []*Parameter
|
||||
for inputIndex := 0; inputIndex < inputParamCount; inputIndex++ {
|
||||
input := methodType.In(inputIndex)
|
||||
thisParam := newParameter("", input)
|
||||
inputs = append(inputs, thisParam)
|
||||
}
|
||||
|
||||
boundMethod.Inputs = inputs
|
||||
|
||||
// Iterate outputs
|
||||
// TODO: Determine what to do about limiting return types
|
||||
// especially around errors.
|
||||
outputParamCount := methodType.NumOut()
|
||||
var outputs []*Parameter
|
||||
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
||||
output := methodType.Out(outputIndex)
|
||||
thisParam := newParameter("", output)
|
||||
outputs = append(outputs, thisParam)
|
||||
}
|
||||
boundMethod.Outputs = outputs
|
||||
|
||||
// Save method in result
|
||||
result = append(result, boundMethod)
|
||||
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// convertArgToValue
|
||||
func convertArgToValue(input json.RawMessage, target *Parameter) (result reflect.Value, err error) {
|
||||
|
||||
// Catch type conversion panics thrown by convert
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Modify error
|
||||
err = fmt.Errorf("%s", r.(string)[23:])
|
||||
}
|
||||
}()
|
||||
|
||||
// Do the conversion
|
||||
|
||||
// Handle nil values
|
||||
if input == nil {
|
||||
switch target.reflectType.Kind() {
|
||||
case reflect.Chan,
|
||||
reflect.Func,
|
||||
reflect.Interface,
|
||||
reflect.Map,
|
||||
reflect.Ptr,
|
||||
reflect.Slice:
|
||||
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||
default:
|
||||
return reflect.Zero(target.reflectType), fmt.Errorf("Unable to use null value")
|
||||
}
|
||||
} else {
|
||||
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||
}
|
||||
|
||||
// We don't like doing this but it's the only way to
|
||||
// handle recover() correctly
|
||||
return
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// RandomID returns a random ID as a string
|
||||
func RandomID() string {
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return fmt.Sprintf("%x", b)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,181 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ffenestri.h"
|
||||
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// DEBUG is the global Ffenestri debug flag.
|
||||
// TODO: move to compile time.
|
||||
var DEBUG bool = true
|
||||
|
||||
// Application is our main application object
|
||||
type Application struct {
|
||||
config *options.App
|
||||
memory []unsafe.Pointer
|
||||
|
||||
// This is the main app pointer
|
||||
app unsafe.Pointer
|
||||
|
||||
// Logger
|
||||
logger logger.CustomLogger
|
||||
}
|
||||
|
||||
func (a *Application) saveMemoryReference(mem unsafe.Pointer) {
|
||||
a.memory = append(a.memory, mem)
|
||||
}
|
||||
|
||||
func (a *Application) string2CString(str string) *C.char {
|
||||
result := C.CString(str)
|
||||
a.saveMemoryReference(unsafe.Pointer(result))
|
||||
return result
|
||||
}
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
// NewApplicationWithConfig creates a new application based on the given config
|
||||
func NewApplicationWithConfig(config *options.App, logger *logger.Logger) *Application {
|
||||
return &Application{
|
||||
config: config,
|
||||
logger: logger.CustomLogger("Ffenestri"),
|
||||
}
|
||||
}
|
||||
|
||||
// NewApplication creates a new Application with the default config
|
||||
func NewApplication(logger *logger.Logger) *Application {
|
||||
return &Application{
|
||||
config: options.Default,
|
||||
logger: logger.CustomLogger("Ffenestri"),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Application) freeMemory() {
|
||||
for _, mem := range a.memory {
|
||||
// fmt.Printf("Freeing memory: %+v\n", mem)
|
||||
C.free(mem)
|
||||
}
|
||||
}
|
||||
|
||||
// bool2Cint converts a Go boolean to a C integer
|
||||
func (a *Application) bool2Cint(value bool) C.int {
|
||||
if value {
|
||||
return C.int(1)
|
||||
}
|
||||
return C.int(0)
|
||||
}
|
||||
|
||||
// dispatcher is the interface to send messages to
|
||||
var dispatcher *messagedispatcher.DispatchClient
|
||||
|
||||
// Dispatcher is what we register out client with
|
||||
type Dispatcher interface {
|
||||
RegisterClient(client messagedispatcher.Client) *messagedispatcher.DispatchClient
|
||||
}
|
||||
|
||||
// DispatchClient is the means for passing messages to the backend
|
||||
type DispatchClient interface {
|
||||
SendMessage(string)
|
||||
}
|
||||
|
||||
func intToColour(colour int) (C.int, C.int, C.int, C.int) {
|
||||
var alpha = C.int(colour & 0xFF)
|
||||
var blue = C.int((colour >> 8) & 0xFF)
|
||||
var green = C.int((colour >> 16) & 0xFF)
|
||||
var red = C.int((colour >> 24) & 0xFF)
|
||||
return red, green, blue, alpha
|
||||
}
|
||||
|
||||
// Run the application
|
||||
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string) error {
|
||||
title := a.string2CString(a.config.Title)
|
||||
width := C.int(a.config.Width)
|
||||
height := C.int(a.config.Height)
|
||||
resizable := a.bool2Cint(!a.config.DisableResize)
|
||||
devtools := a.bool2Cint(a.config.DevTools)
|
||||
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
||||
logLevel := C.int(a.config.LogLevel)
|
||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel)
|
||||
|
||||
// Save app reference
|
||||
a.app = unsafe.Pointer(app)
|
||||
|
||||
// Set Min Window Size
|
||||
minWidth := C.int(a.config.MinWidth)
|
||||
minHeight := C.int(a.config.MinHeight)
|
||||
C.SetMinWindowSize(a.app, minWidth, minHeight)
|
||||
|
||||
// Set Max Window Size
|
||||
maxWidth := C.int(a.config.MaxWidth)
|
||||
maxHeight := C.int(a.config.MaxHeight)
|
||||
C.SetMaxWindowSize(a.app, maxWidth, maxHeight)
|
||||
|
||||
// Set debug if needed
|
||||
C.SetDebug(app, a.bool2Cint(DEBUG))
|
||||
|
||||
// TODO: Move frameless to Linux options
|
||||
// if a.config.Frameless {
|
||||
// C.DisableFrame(a.app)
|
||||
// }
|
||||
|
||||
if a.config.RGBA != 0 {
|
||||
r, g, b, alpha := intToColour(a.config.RGBA)
|
||||
C.SetColour(a.app, r, g, b, alpha)
|
||||
}
|
||||
|
||||
// Escape bindings so C doesn't freak out
|
||||
bindings = strings.ReplaceAll(bindings, `"`, `\"`)
|
||||
|
||||
// Set bindings
|
||||
C.SetBindings(app, a.string2CString(bindings))
|
||||
|
||||
// save the dispatcher in a package variable so that the C callbacks
|
||||
// can access it
|
||||
dispatcher = incomingDispatcher.RegisterClient(newClient(a))
|
||||
|
||||
// Process platform settings
|
||||
a.processPlatformSettings()
|
||||
|
||||
// Check we could initialise the application
|
||||
if app != nil {
|
||||
// Yes - Save memory reference and run app, cleaning up afterwards
|
||||
a.saveMemoryReference(unsafe.Pointer(app))
|
||||
C.Run(app, 0, nil)
|
||||
} else {
|
||||
// Oh no! We couldn't initialise the application
|
||||
a.logger.Fatal("Cannot initialise Application.")
|
||||
}
|
||||
|
||||
a.freeMemory()
|
||||
return nil
|
||||
}
|
||||
|
||||
// messageFromWindowCallback is called by any messages sent in
|
||||
// webkit to window.external.invoke. It relays the message on to
|
||||
// the dispatcher.
|
||||
//export messageFromWindowCallback
|
||||
func messageFromWindowCallback(data *C.char) {
|
||||
dispatcher.DispatchMessage(C.GoString(data))
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef __FFENESTRI_H__
|
||||
#define __FFENESTRI_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel);
|
||||
extern void SetMinWindowSize(void *app, int minWidth, int minHeight);
|
||||
extern void SetMaxWindowSize(void *app, int maxWidth, int maxHeight);
|
||||
extern void Run(void *app, int argc, char **argv);
|
||||
extern void DestroyApplication(void *app);
|
||||
extern void SetDebug(void *app, int flag);
|
||||
extern void SetBindings(void *app, const char *bindings);
|
||||
extern void ExecJS(void *app, const char *script);
|
||||
extern void Hide(void *app);
|
||||
extern void Show(void *app);
|
||||
extern void Center(void *app);
|
||||
extern void Maximise(void *app);
|
||||
extern void Unmaximise(void *app);
|
||||
extern void ToggleMaximise(void *app);
|
||||
extern void Minimise(void *app);
|
||||
extern void Unminimise(void *app);
|
||||
extern void ToggleMinimise(void *app);
|
||||
extern void SetColour(void *app, int red, int green, int blue, int alpha);
|
||||
extern void SetSize(void *app, int width, int height);
|
||||
extern void SetPosition(void *app, int x, int y);
|
||||
extern void Quit(void *app);
|
||||
extern void SetTitle(void *app, const char *title);
|
||||
extern void Fullscreen(void *app);
|
||||
extern void UnFullscreen(void *app);
|
||||
extern void ToggleFullscreen(void *app);
|
||||
extern void DisableFrame(void *app);
|
||||
extern void OpenDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolveAliases, int treatPackagesAsDirectories);
|
||||
extern void SaveDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories);
|
||||
extern void DarkModeEnabled(void *appPointer, char *callbackID);
|
||||
#endif
|
||||
@@ -1,156 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
|
||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ffenestri.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Client is our implentation of messageDispatcher.Client
|
||||
type Client struct {
|
||||
app *Application
|
||||
logger logger.CustomLogger
|
||||
}
|
||||
|
||||
func newClient(app *Application) *Client {
|
||||
return &Client{
|
||||
app: app,
|
||||
logger: app.logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Quit the application
|
||||
func (c *Client) Quit() {
|
||||
c.app.logger.Trace("Got shutdown message")
|
||||
C.Quit(c.app.app)
|
||||
}
|
||||
|
||||
// NotifyEvent will pass on the event message to the frontend
|
||||
func (c *Client) NotifyEvent(message string) {
|
||||
eventMessage := `window.wails._.Notify(` + strconv.Quote(message) + `);`
|
||||
c.app.logger.Trace("eventMessage = %+v", eventMessage)
|
||||
C.ExecJS(c.app.app, c.app.string2CString(eventMessage))
|
||||
}
|
||||
|
||||
// CallResult contains the result of the call from JS
|
||||
func (c *Client) CallResult(message string) {
|
||||
callbackMessage := `window.wails._.Callback(` + strconv.Quote(message) + `);`
|
||||
c.app.logger.Trace("callbackMessage = %+v", callbackMessage)
|
||||
C.ExecJS(c.app.app, c.app.string2CString(callbackMessage))
|
||||
}
|
||||
|
||||
// WindowSetTitle sets the window title to the given string
|
||||
func (c *Client) WindowSetTitle(title string) {
|
||||
C.SetTitle(c.app.app, c.app.string2CString(title))
|
||||
}
|
||||
|
||||
// WindowFullscreen will set the window to be fullscreen
|
||||
func (c *Client) WindowFullscreen() {
|
||||
C.Fullscreen(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnFullscreen will unfullscreen the window
|
||||
func (c *Client) WindowUnFullscreen() {
|
||||
C.UnFullscreen(c.app.app)
|
||||
}
|
||||
|
||||
// WindowShow will show the window
|
||||
func (c *Client) WindowShow() {
|
||||
C.Show(c.app.app)
|
||||
}
|
||||
|
||||
// WindowHide will hide the window
|
||||
func (c *Client) WindowHide() {
|
||||
C.Hide(c.app.app)
|
||||
}
|
||||
|
||||
// WindowCenter will hide the window
|
||||
func (c *Client) WindowCenter() {
|
||||
C.Center(c.app.app)
|
||||
}
|
||||
|
||||
// WindowMaximise will maximise the window
|
||||
func (c *Client) WindowMaximise() {
|
||||
C.Maximise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowMinimise will minimise the window
|
||||
func (c *Client) WindowMinimise() {
|
||||
C.Minimise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnmaximise will unmaximise the window
|
||||
func (c *Client) WindowUnmaximise() {
|
||||
C.Unmaximise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnminimise will unminimise the window
|
||||
func (c *Client) WindowUnminimise() {
|
||||
C.Unminimise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowPosition will position the window to x,y on the
|
||||
// monitor that the window is mostly on
|
||||
func (c *Client) WindowPosition(x int, y int) {
|
||||
C.SetPosition(c.app.app, C.int(x), C.int(y))
|
||||
}
|
||||
|
||||
// WindowSize will resize the window to the given
|
||||
// width and height
|
||||
func (c *Client) WindowSize(width int, height int) {
|
||||
C.SetSize(c.app.app, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
// WindowSetColour sets the window colour
|
||||
func (c *Client) WindowSetColour(colour int) {
|
||||
r, g, b, a := intToColour(colour)
|
||||
C.SetColour(c.app.app, r, g, b, a)
|
||||
}
|
||||
|
||||
// OpenDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenDialog(dialogOptions *options.OpenDialog, callbackID string) {
|
||||
C.OpenDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(dialogOptions.Filters),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(dialogOptions.AllowFiles),
|
||||
c.app.bool2Cint(dialogOptions.AllowDirectories),
|
||||
c.app.bool2Cint(dialogOptions.AllowMultiple),
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
||||
c.app.bool2Cint(dialogOptions.ResolveAliases),
|
||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
||||
)
|
||||
}
|
||||
|
||||
// SaveDialog will open a dialog with the given title and filter
|
||||
func (c *Client) SaveDialog(dialogOptions *options.SaveDialog, callbackID string) {
|
||||
C.SaveDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(dialogOptions.Filters),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||
|
||||
extern void TitlebarAppearsTransparent(void *);
|
||||
extern void HideTitle(void *);
|
||||
extern void HideTitleBar(void *);
|
||||
extern void FullSizeContent(void *);
|
||||
extern void UseToolbar(void *);
|
||||
extern void HideToolbarSeparator(void *);
|
||||
extern void DisableFrame(void *);
|
||||
extern void SetAppearance(void *, const char *);
|
||||
extern void WebviewIsTransparent(void *);
|
||||
extern void SetWindowBackgroundIsTranslucent(void *);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (a *Application) processPlatformSettings() {
|
||||
|
||||
mac := a.config.Mac
|
||||
titlebar := mac.TitleBar
|
||||
|
||||
// HideTitle
|
||||
if titlebar.HideTitle {
|
||||
C.HideTitle(a.app)
|
||||
}
|
||||
|
||||
// HideTitleBar
|
||||
if titlebar.HideTitleBar {
|
||||
C.HideTitleBar(a.app)
|
||||
}
|
||||
|
||||
// Full Size Content
|
||||
if titlebar.FullSizeContent {
|
||||
C.FullSizeContent(a.app)
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
if titlebar.UseToolbar {
|
||||
C.UseToolbar(a.app)
|
||||
}
|
||||
|
||||
if titlebar.HideToolbarSeparator {
|
||||
C.HideToolbarSeparator(a.app)
|
||||
}
|
||||
|
||||
if titlebar.TitlebarAppearsTransparent {
|
||||
C.TitlebarAppearsTransparent(a.app)
|
||||
}
|
||||
|
||||
// Process window Appearance
|
||||
if mac.Appearance != "" {
|
||||
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
||||
}
|
||||
|
||||
// Check if the webview should be transparent
|
||||
if mac.WebviewIsTransparent {
|
||||
C.WebviewIsTransparent(a.app)
|
||||
}
|
||||
|
||||
// Check if window should be translucent
|
||||
if mac.WindowBackgroundIsTranslucent {
|
||||
C.SetWindowBackgroundIsTranslucent(a.app)
|
||||
}
|
||||
}
|
||||
@@ -1,984 +0,0 @@
|
||||
|
||||
#ifndef __FFENESTRI_LINUX_H__
|
||||
#define __FFENESTRI_LINUX_H__
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// References to assets
|
||||
extern const unsigned char *assets[];
|
||||
extern const unsigned char runtime;
|
||||
extern const char *icon[];
|
||||
|
||||
// Constants
|
||||
#define PRIMARY_MOUSE_BUTTON 1
|
||||
#define MIDDLE_MOUSE_BUTTON 2
|
||||
#define SECONDARY_MOUSE_BUTTON 3
|
||||
|
||||
// MAIN DEBUG FLAG
|
||||
int debug;
|
||||
|
||||
// Credit: https://stackoverflow.com/a/8465083
|
||||
char *concat(const char *s1, const char *s2)
|
||||
{
|
||||
const size_t len1 = strlen(s1);
|
||||
const size_t len2 = strlen(s2);
|
||||
char *result = malloc(len1 + len2 + 1);
|
||||
memcpy(result, s1, len1);
|
||||
memcpy(result + len1, s2, len2 + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Debug works like sprintf but mutes if the global debug flag is true
|
||||
// Credit: https://stackoverflow.com/a/20639708
|
||||
void Debug(char *message, ...)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
char *temp = concat("TRACE | Ffenestri (C) | ", message);
|
||||
message = concat(temp, "\n");
|
||||
free(temp);
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vprintf(message, args);
|
||||
free(message);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
extern void messageFromWindowCallback(const char *);
|
||||
typedef void (*ffenestriCallback)(const char *);
|
||||
|
||||
struct Application
|
||||
{
|
||||
|
||||
// Gtk Data
|
||||
GtkApplication *application;
|
||||
GtkWindow *mainWindow;
|
||||
GtkWidget *webView;
|
||||
int signalInvoke;
|
||||
int signalWindowDrag;
|
||||
int signalButtonPressed;
|
||||
int signalButtonReleased;
|
||||
int signalLoadChanged;
|
||||
|
||||
// Saves the events for the drag mouse button
|
||||
GdkEventButton *dragButtonEvent;
|
||||
|
||||
// The number of the default drag button
|
||||
int dragButton;
|
||||
|
||||
// Window Data
|
||||
const char *title;
|
||||
char *id;
|
||||
int width;
|
||||
int height;
|
||||
int resizable;
|
||||
int devtools;
|
||||
int startHidden;
|
||||
int fullscreen;
|
||||
int minWidth;
|
||||
int minHeight;
|
||||
int maxWidth;
|
||||
int maxHeight;
|
||||
int frame;
|
||||
|
||||
// User Data
|
||||
char *HTML;
|
||||
|
||||
// Callback
|
||||
ffenestriCallback sendMessageToBackend;
|
||||
|
||||
// Bindings
|
||||
const char *bindings;
|
||||
|
||||
// Lock - used for sync operations (Should we be using g_mutex?)
|
||||
int lock;
|
||||
};
|
||||
|
||||
void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden)
|
||||
{
|
||||
// Setup main application struct
|
||||
struct Application *result = malloc(sizeof(struct Application));
|
||||
result->title = title;
|
||||
result->width = width;
|
||||
result->height = height;
|
||||
result->resizable = resizable;
|
||||
result->devtools = devtools;
|
||||
result->fullscreen = fullscreen;
|
||||
result->minWidth = 0;
|
||||
result->minHeight = 0;
|
||||
result->maxWidth = 0;
|
||||
result->maxHeight = 0;
|
||||
result->frame = 1;
|
||||
result->startHidden = startHidden;
|
||||
|
||||
// Default drag button is PRIMARY
|
||||
result->dragButton = PRIMARY_MOUSE_BUTTON;
|
||||
|
||||
result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback;
|
||||
|
||||
// Create a unique ID based on the current unix timestamp
|
||||
char temp[11];
|
||||
sprintf(&temp[0], "%d", (int)time(NULL));
|
||||
result->id = concat("wails.app", &temp[0]);
|
||||
|
||||
// Create the main GTK application
|
||||
GApplicationFlags flags = G_APPLICATION_FLAGS_NONE;
|
||||
result->application = gtk_application_new(result->id, flags);
|
||||
|
||||
// Return the application struct
|
||||
return (void *)result;
|
||||
}
|
||||
|
||||
void DestroyApplication(struct Application *app)
|
||||
{
|
||||
Debug("Destroying Application");
|
||||
|
||||
g_application_quit(G_APPLICATION(app->application));
|
||||
|
||||
// Release the GTK ID string
|
||||
if (app->id != NULL)
|
||||
{
|
||||
free(app->id);
|
||||
app->id = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug("Almost a double free for app->id");
|
||||
}
|
||||
|
||||
// Free the bindings
|
||||
if (app->bindings != NULL)
|
||||
{
|
||||
free((void *)app->bindings);
|
||||
app->bindings = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug("Almost a double free for app->bindings");
|
||||
}
|
||||
|
||||
// Disconnect signal handlers
|
||||
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView);
|
||||
g_signal_handler_disconnect(manager, app->signalInvoke);
|
||||
if( app->frame == 0) {
|
||||
g_signal_handler_disconnect(manager, app->signalWindowDrag);
|
||||
g_signal_handler_disconnect(app->webView, app->signalButtonPressed);
|
||||
g_signal_handler_disconnect(app->webView, app->signalButtonReleased);
|
||||
}
|
||||
g_signal_handler_disconnect(app->webView, app->signalLoadChanged);
|
||||
|
||||
// Release the main GTK Application
|
||||
if (app->application != NULL)
|
||||
{
|
||||
g_object_unref(app->application);
|
||||
app->application = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug("Almost a double free for app->application");
|
||||
}
|
||||
Debug("Finished Destroying Application");
|
||||
}
|
||||
|
||||
// Quit will stop the gtk application and free up all the memory
|
||||
// used by the application
|
||||
void Quit(struct Application *app)
|
||||
{
|
||||
Debug("Quit Called");
|
||||
gtk_window_close((GtkWindow *)app->mainWindow);
|
||||
g_application_quit((GApplication *)app->application);
|
||||
DestroyApplication(app);
|
||||
}
|
||||
|
||||
// SetTitle sets the main window title to the given string
|
||||
void SetTitle(struct Application *app, const char *title)
|
||||
{
|
||||
gtk_window_set_title(app->mainWindow, title);
|
||||
}
|
||||
|
||||
// Fullscreen sets the main window to be fullscreen
|
||||
void Fullscreen(struct Application *app)
|
||||
{
|
||||
gtk_window_fullscreen(app->mainWindow);
|
||||
}
|
||||
|
||||
// UnFullscreen resets the main window after a fullscreen
|
||||
void UnFullscreen(struct Application *app)
|
||||
{
|
||||
gtk_window_unfullscreen(app->mainWindow);
|
||||
}
|
||||
|
||||
void setMinMaxSize(struct Application *app)
|
||||
{
|
||||
GdkGeometry size;
|
||||
size.min_width = size.min_height = size.max_width = size.max_height = 0;
|
||||
int flags = 0;
|
||||
if (app->maxHeight > 0 && app->maxWidth > 0)
|
||||
{
|
||||
size.max_height = app->maxHeight;
|
||||
size.max_width = app->maxWidth;
|
||||
flags |= GDK_HINT_MAX_SIZE;
|
||||
}
|
||||
if (app->minHeight > 0 && app->minWidth > 0)
|
||||
{
|
||||
size.min_height = app->minHeight;
|
||||
size.min_width = app->minWidth;
|
||||
flags |= GDK_HINT_MIN_SIZE;
|
||||
}
|
||||
gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags);
|
||||
}
|
||||
|
||||
char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAction, char **args) {
|
||||
GtkFileChooserNative *native;
|
||||
GtkFileChooserAction action = chooserAction;
|
||||
gint res;
|
||||
char *filename;
|
||||
|
||||
char *title = args[0];
|
||||
char *filter = args[1];
|
||||
|
||||
native = gtk_file_chooser_native_new(title,
|
||||
app->mainWindow,
|
||||
action,
|
||||
"_Open",
|
||||
"_Cancel");
|
||||
|
||||
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||
|
||||
// If we have filters, process them
|
||||
if (filter[0] != '\0') {
|
||||
GtkFileFilter *file_filter = gtk_file_filter_new();
|
||||
gchar **filters = g_strsplit(filter, ",", -1);
|
||||
gint i;
|
||||
for(i = 0; filters && filters[i]; i++) {
|
||||
gtk_file_filter_add_pattern(file_filter, filters[i]);
|
||||
// Debug("Adding filter pattern: %s\n", filters[i]);
|
||||
}
|
||||
gtk_file_filter_set_name(file_filter, filter);
|
||||
gtk_file_chooser_add_filter(chooser, file_filter);
|
||||
g_strfreev(filters);
|
||||
}
|
||||
|
||||
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||
if (res == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
filename = gtk_file_chooser_get_filename(chooser);
|
||||
}
|
||||
|
||||
g_object_unref(native);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
// openFileDialogInternal opens a dialog to select a file
|
||||
// NOTE: The result is a string that will need to be freed!
|
||||
char *openFileDialogInternal(struct Application *app, char **args)
|
||||
{
|
||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_OPEN, args);
|
||||
}
|
||||
|
||||
// saveFileDialogInternal opens a dialog to select a file
|
||||
// NOTE: The result is a string that will need to be freed!
|
||||
char *saveFileDialogInternal(struct Application *app, char **args)
|
||||
{
|
||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SAVE, args);
|
||||
}
|
||||
|
||||
|
||||
// openDirectoryDialogInternal opens a dialog to select a directory
|
||||
// NOTE: The result is a string that will need to be freed!
|
||||
char *openDirectoryDialogInternal(struct Application *app, char **args)
|
||||
{
|
||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, args);
|
||||
}
|
||||
|
||||
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
||||
{
|
||||
app->minWidth = minWidth;
|
||||
app->minHeight = minHeight;
|
||||
}
|
||||
|
||||
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
||||
{
|
||||
app->maxWidth = maxWidth;
|
||||
app->maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
// SetColour sets the colour of the webview to the given colour string
|
||||
int SetColour(struct Application *app, const char *colourString)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
gboolean result = gdk_rgba_parse(&rgba, colourString);
|
||||
if (result == FALSE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// Debug("Setting webview colour to: %s", colourString);
|
||||
webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DisableFrame disables the window frame
|
||||
void DisableFrame(struct Application *app)
|
||||
{
|
||||
app->frame = 0;
|
||||
}
|
||||
|
||||
void syncCallback(GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
void *data)
|
||||
{
|
||||
struct Application *app = (struct Application *)data;
|
||||
app->lock = 0;
|
||||
}
|
||||
|
||||
void syncEval(struct Application *app, const gchar *script)
|
||||
{
|
||||
|
||||
WebKitWebView *webView = (WebKitWebView *)(app->webView);
|
||||
|
||||
// wait for lock to free
|
||||
while (app->lock == 1)
|
||||
{
|
||||
g_main_context_iteration(0, true);
|
||||
}
|
||||
// Set lock
|
||||
app->lock = 1;
|
||||
|
||||
webkit_web_view_run_javascript(
|
||||
webView,
|
||||
script,
|
||||
NULL, syncCallback, (void*)app);
|
||||
|
||||
while (app->lock == 1)
|
||||
{
|
||||
g_main_context_iteration(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
void asyncEval(WebKitWebView *webView, const gchar *script)
|
||||
{
|
||||
webkit_web_view_run_javascript(
|
||||
webView,
|
||||
script,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
typedef void (*dispatchMethod)(struct Application *app, void *);
|
||||
|
||||
struct dispatchData
|
||||
{
|
||||
struct Application *app;
|
||||
dispatchMethod method;
|
||||
void *args;
|
||||
};
|
||||
|
||||
gboolean executeMethod(gpointer data)
|
||||
{
|
||||
struct dispatchData *d = (struct dispatchData *)data;
|
||||
(d->method)(d->app, d->args);
|
||||
g_free(d);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ExecJS(struct Application *app, char *js)
|
||||
{
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)syncEval;
|
||||
data->args = js;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
typedef char *(*dialogMethod)(struct Application *app, void *);
|
||||
|
||||
struct dialogCall
|
||||
{
|
||||
struct Application *app;
|
||||
dialogMethod method;
|
||||
void *args;
|
||||
void *filter;
|
||||
char *result;
|
||||
int done;
|
||||
};
|
||||
|
||||
gboolean executeMethodWithReturn(gpointer data)
|
||||
{
|
||||
struct dialogCall *d = (struct dialogCall *)data;
|
||||
|
||||
d->result = (d->method)(d->app, d->args);
|
||||
d->done = 1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char *OpenFileDialog(struct Application *app, char *title, char *filter)
|
||||
{
|
||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
||||
data->result = NULL;
|
||||
data->done = 0;
|
||||
data->method = (dialogMethod)openFileDialogInternal;
|
||||
const char* dialogArgs[]={ title, filter };
|
||||
data->args = dialogArgs;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||
|
||||
while (data->done == 0)
|
||||
{
|
||||
usleep(100000);
|
||||
}
|
||||
g_free(data);
|
||||
return data->result;
|
||||
}
|
||||
|
||||
char *SaveFileDialog(struct Application *app, char *title, char *filter)
|
||||
{
|
||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
||||
data->result = NULL;
|
||||
data->done = 0;
|
||||
data->method = (dialogMethod)saveFileDialogInternal;
|
||||
const char* dialogArgs[]={ title, filter };
|
||||
data->args = dialogArgs;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||
|
||||
while (data->done == 0)
|
||||
{
|
||||
usleep(100000);
|
||||
}
|
||||
Debug("Dialog done");
|
||||
Debug("Result = %s\n", data->result);
|
||||
|
||||
g_free(data);
|
||||
// Fingers crossed this wasn't freed by g_free above
|
||||
return data->result;
|
||||
}
|
||||
|
||||
char *OpenDirectoryDialog(struct Application *app, char *title, char *filter)
|
||||
{
|
||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
||||
data->result = NULL;
|
||||
data->done = 0;
|
||||
data->method = (dialogMethod)openDirectoryDialogInternal;
|
||||
const char* dialogArgs[]={ title, filter };
|
||||
data->args = dialogArgs;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||
|
||||
while (data->done == 0)
|
||||
{
|
||||
usleep(100000);
|
||||
}
|
||||
Debug("Directory Dialog done");
|
||||
Debug("Result = %s\n", data->result);
|
||||
g_free(data);
|
||||
// Fingers crossed this wasn't freed by g_free above
|
||||
return data->result;
|
||||
}
|
||||
|
||||
// Sets the icon to the XPM stored in icon
|
||||
void setIcon(struct Application *app)
|
||||
{
|
||||
GdkPixbuf *appIcon = gdk_pixbuf_new_from_xpm_data((const char **)icon);
|
||||
gtk_window_set_icon(app->mainWindow, appIcon);
|
||||
}
|
||||
|
||||
static void load_finished_cb(WebKitWebView *webView,
|
||||
WebKitLoadEvent load_event,
|
||||
struct Application *app)
|
||||
{
|
||||
switch (load_event)
|
||||
{
|
||||
// case WEBKIT_LOAD_STARTED:
|
||||
// /* New load, we have now a provisional URI */
|
||||
// // printf("Start downloading %s\n", webkit_web_view_get_uri(web_view));
|
||||
// /* Here we could start a spinner or update the
|
||||
// * location bar with the provisional URI */
|
||||
// break;
|
||||
// case WEBKIT_LOAD_REDIRECTED:
|
||||
// // printf("Redirected to: %s\n", webkit_web_view_get_uri(web_view));
|
||||
// break;
|
||||
// case WEBKIT_LOAD_COMMITTED:
|
||||
// /* The load is being performed. Current URI is
|
||||
// * the final one and it won't change unless a new
|
||||
// * load is requested or a navigation within the
|
||||
// * same page is performed */
|
||||
// // printf("Loading: %s\n", webkit_web_view_get_uri(web_view));
|
||||
// break;
|
||||
case WEBKIT_LOAD_FINISHED:
|
||||
/* Load finished, we can now stop the spinner */
|
||||
// printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view));
|
||||
|
||||
// Bindings
|
||||
Debug("Binding Methods");
|
||||
syncEval(app, app->bindings);
|
||||
|
||||
// Runtime
|
||||
Debug("Setting up Wails runtime");
|
||||
syncEval(app, &runtime);
|
||||
|
||||
// Loop over assets
|
||||
int index = 1;
|
||||
while (1)
|
||||
{
|
||||
// Get next asset pointer
|
||||
const char *asset = assets[index];
|
||||
|
||||
// If we have no more assets, break
|
||||
if (asset == 0x00)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// sync eval the asset
|
||||
syncEval(app, asset);
|
||||
index++;
|
||||
};
|
||||
|
||||
// Set the icon
|
||||
setIcon(app);
|
||||
|
||||
// Setup fullscreen
|
||||
if (app->fullscreen)
|
||||
{
|
||||
Debug("Going fullscreen");
|
||||
Fullscreen(app);
|
||||
}
|
||||
|
||||
// Setup resize
|
||||
gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height);
|
||||
|
||||
if (app->resizable)
|
||||
{
|
||||
gtk_window_set_default_size(GTK_WINDOW(app->mainWindow), app->width, app->height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_set_size_request(GTK_WIDGET(app->mainWindow), app->width, app->height);
|
||||
gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height);
|
||||
// Fix the min/max to the window size for good measure
|
||||
app->minHeight = app->maxHeight = app->height;
|
||||
app->minWidth = app->maxWidth = app->width;
|
||||
}
|
||||
gtk_window_set_resizable(GTK_WINDOW(app->mainWindow), app->resizable ? TRUE : FALSE);
|
||||
setMinMaxSize(app);
|
||||
|
||||
// Centre by default
|
||||
gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER);
|
||||
|
||||
// Show window and focus
|
||||
if( app->startHidden == 0) {
|
||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
||||
gtk_widget_grab_focus(app->webView);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean disable_context_menu_cb(
|
||||
WebKitWebView *web_view,
|
||||
WebKitContextMenu *context_menu,
|
||||
GdkEvent *event,
|
||||
WebKitHitTestResult *hit_test_result,
|
||||
gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void printEvent(const char *message, GdkEventButton *event)
|
||||
{
|
||||
Debug("%s: [button:%d] [x:%f] [y:%f] [time:%d]",
|
||||
message,
|
||||
event->button,
|
||||
event->x_root,
|
||||
event->y_root,
|
||||
event->time);
|
||||
}
|
||||
|
||||
|
||||
static void dragWindow(WebKitUserContentManager *contentManager,
|
||||
WebKitJavascriptResult *result,
|
||||
struct Application *app)
|
||||
{
|
||||
// If we get this message erroneously, ignore
|
||||
if (app->dragButtonEvent == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore non-toplevel widgets
|
||||
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(app->webView));
|
||||
if (!GTK_IS_WINDOW(window))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Initiate the drag
|
||||
printEvent("Starting drag with event", app->dragButtonEvent);
|
||||
|
||||
gtk_window_begin_move_drag(app->mainWindow,
|
||||
app->dragButton,
|
||||
app->dragButtonEvent->x_root,
|
||||
app->dragButtonEvent->y_root,
|
||||
app->dragButtonEvent->time);
|
||||
// Clear the event
|
||||
app->dragButtonEvent = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
||||
{
|
||||
if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton)
|
||||
{
|
||||
app->dragButtonEvent = event;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
||||
{
|
||||
if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton)
|
||||
{
|
||||
app->dragButtonEvent = NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
||||
WebKitJavascriptResult *result,
|
||||
struct Application *app)
|
||||
{
|
||||
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
||||
JSCValue *value = webkit_javascript_result_get_js_value(result);
|
||||
char *message = jsc_value_to_string(value);
|
||||
#else
|
||||
JSGlobalContextRef context = webkit_javascript_result_get_global_context(result);
|
||||
JSValueRef value = webkit_javascript_result_get_value(result);
|
||||
JSStringRef js = JSValueToStringCopy(context, value, NULL);
|
||||
size_t messageSize = JSStringGetMaximumUTF8CStringSize(js);
|
||||
char *message = g_new(char, messageSize);
|
||||
JSStringGetUTF8CString(js, message, messageSize);
|
||||
JSStringRelease(js);
|
||||
#endif
|
||||
app->sendMessageToBackend(message);
|
||||
g_free(message);
|
||||
}
|
||||
|
||||
void SetDebug(struct Application *app, int flag)
|
||||
{
|
||||
debug = flag;
|
||||
}
|
||||
|
||||
// getCurrentMonitorGeometry gets the geometry of the monitor
|
||||
// that the window is mostly on.
|
||||
GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) {
|
||||
// Get the monitor that the window is currently on
|
||||
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window));
|
||||
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
||||
GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, gdk_window);
|
||||
|
||||
// Get the geometry of the monitor
|
||||
GdkRectangle result;
|
||||
gdk_monitor_get_geometry (monitor,&result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************
|
||||
* Window Position *
|
||||
*******************/
|
||||
|
||||
// Position holds an x/y corrdinate
|
||||
struct Position {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
// Internal call for setting the position of the window.
|
||||
void setPositionInternal(struct Application *app, struct Position *pos) {
|
||||
|
||||
// Get the monitor geometry
|
||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
||||
|
||||
// Move the window relative to the monitor
|
||||
gtk_window_move(app->mainWindow, m.x + pos->x, m.y + pos->y);
|
||||
|
||||
// Free memory
|
||||
free(pos);
|
||||
}
|
||||
|
||||
// SetPosition sets the position of the window to the given x/y
|
||||
// coordinates. The x/y values are relative to the monitor
|
||||
// the window is mostly on.
|
||||
void SetPosition(struct Application *app, int x, int y) {
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)setPositionInternal;
|
||||
struct Position *pos = malloc(sizeof(struct Position));
|
||||
pos->x = x;
|
||||
pos->y = y;
|
||||
data->args = pos;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
/***************
|
||||
* Window Size *
|
||||
***************/
|
||||
|
||||
// Size holds a width/height
|
||||
struct Size {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
// Internal call for setting the size of the window.
|
||||
void setSizeInternal(struct Application *app, struct Size *size) {
|
||||
gtk_window_resize(app->mainWindow, size->width, size->height);
|
||||
free(size);
|
||||
}
|
||||
|
||||
// SetSize sets the size of the window to the given width/height
|
||||
void SetSize(struct Application *app, int width, int height) {
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)setSizeInternal;
|
||||
struct Size *size = malloc(sizeof(struct Size));
|
||||
size->width = width;
|
||||
size->height = height;
|
||||
data->args = size;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
// centerInternal will center the main window on the monitor it is mostly in
|
||||
void centerInternal(struct Application *app)
|
||||
{
|
||||
// Get the geometry of the monitor
|
||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
||||
|
||||
// Get the window width/height
|
||||
int windowWidth, windowHeight;
|
||||
gtk_window_get_size(app->mainWindow, &windowWidth, &windowHeight);
|
||||
|
||||
// Place the window at the center of the monitor
|
||||
gtk_window_move(app->mainWindow, ((m.width - windowWidth) / 2) + m.x, ((m.height - windowHeight) / 2) + m.y);
|
||||
}
|
||||
|
||||
// Center the window
|
||||
void Center(struct Application *app) {
|
||||
|
||||
// Setup a call to centerInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)centerInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// hideInternal hides the main window
|
||||
void hideInternal(struct Application *app) {
|
||||
gtk_widget_hide (GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Hide places the hideInternal method onto the main thread for execution
|
||||
void Hide(struct Application *app) {
|
||||
|
||||
// Setup a call to hideInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)hideInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// showInternal shows the main window
|
||||
void showInternal(struct Application *app) {
|
||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
||||
gtk_widget_grab_focus(app->webView);
|
||||
}
|
||||
|
||||
// Show places the showInternal method onto the main thread for execution
|
||||
void Show(struct Application *app) {
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)showInternal;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
// maximiseInternal maximises the main window
|
||||
void maximiseInternal(struct Application *app) {
|
||||
gtk_window_maximize(GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Maximise places the maximiseInternal method onto the main thread for execution
|
||||
void Maximise(struct Application *app) {
|
||||
|
||||
// Setup a call to maximiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)maximiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// unmaximiseInternal unmaximises the main window
|
||||
void unmaximiseInternal(struct Application *app) {
|
||||
gtk_window_unmaximize(GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Unmaximise places the unmaximiseInternal method onto the main thread for execution
|
||||
void Unmaximise(struct Application *app) {
|
||||
|
||||
// Setup a call to unmaximiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)unmaximiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
// minimiseInternal minimises the main window
|
||||
void minimiseInternal(struct Application *app) {
|
||||
gtk_window_iconify(app->mainWindow);
|
||||
}
|
||||
|
||||
// Minimise places the minimiseInternal method onto the main thread for execution
|
||||
void Minimise(struct Application *app) {
|
||||
|
||||
// Setup a call to minimiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)minimiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// unminimiseInternal unminimises the main window
|
||||
void unminimiseInternal(struct Application *app) {
|
||||
gtk_window_present(app->mainWindow);
|
||||
}
|
||||
|
||||
// Unminimise places the unminimiseInternal method onto the main thread for execution
|
||||
void Unminimise(struct Application *app) {
|
||||
|
||||
// Setup a call to unminimiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)unminimiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
void SetBindings(struct Application *app, const char *bindings)
|
||||
{
|
||||
const char *temp = concat("window.wailsbindings = \"", bindings);
|
||||
const char *jscall = concat(temp, "\";");
|
||||
free((void *)temp);
|
||||
app->bindings = jscall;
|
||||
}
|
||||
|
||||
// This is called when the close button on the window is pressed
|
||||
gboolean close_button_pressed(GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
struct Application *app)
|
||||
{
|
||||
app->sendMessageToBackend("WC"); // Window Close
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void setupWindow(struct Application *app)
|
||||
{
|
||||
|
||||
// Create the window
|
||||
GtkWidget *mainWindow = gtk_application_window_new(app->application);
|
||||
// Save reference
|
||||
app->mainWindow = GTK_WINDOW(mainWindow);
|
||||
|
||||
// Setup frame
|
||||
gtk_window_set_decorated((GtkWindow *)mainWindow, app->frame);
|
||||
|
||||
// Setup title
|
||||
gtk_window_set_title(GTK_WINDOW(mainWindow), app->title);
|
||||
|
||||
// Setup script handler
|
||||
WebKitUserContentManager *contentManager = webkit_user_content_manager_new();
|
||||
|
||||
// Setup the invoke handler
|
||||
webkit_user_content_manager_register_script_message_handler(contentManager, "external");
|
||||
app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app);
|
||||
|
||||
// Setup the window drag handler if this is a frameless app
|
||||
if ( app->frame == 0 ) {
|
||||
webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag");
|
||||
app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app);
|
||||
// Setup the mouse handlers
|
||||
app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app);
|
||||
app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app);
|
||||
}
|
||||
GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager);
|
||||
|
||||
// Save reference
|
||||
app->webView = webView;
|
||||
|
||||
// Add the webview to the window
|
||||
gtk_container_add(GTK_CONTAINER(mainWindow), webView);
|
||||
|
||||
|
||||
// Load default HTML
|
||||
app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app);
|
||||
|
||||
// Load the user's HTML
|
||||
// assets[0] is the HTML because the asset array is bundled like that by convention
|
||||
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webView), assets[0]);
|
||||
|
||||
// Check if we want to enable the dev tools
|
||||
if (app->devtools)
|
||||
{
|
||||
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView));
|
||||
// webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
||||
webkit_settings_set_enable_developer_extras(settings, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_signal_connect(G_OBJECT(webView), "context-menu", G_CALLBACK(disable_context_menu_cb), app);
|
||||
}
|
||||
|
||||
// Listen for close button signal
|
||||
g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app);
|
||||
}
|
||||
|
||||
static void activate(GtkApplication* _, struct Application *app)
|
||||
{
|
||||
setupWindow(app);
|
||||
}
|
||||
|
||||
void Run(struct Application *app, int argc, char **argv)
|
||||
{
|
||||
g_signal_connect(app->application, "activate", G_CALLBACK(activate), app);
|
||||
g_application_run(G_APPLICATION(app->application), argc, argv);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Source: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD
|
||||
*/
|
||||
|
||||
#ifndef CCAN_JSON_H
|
||||
#define CCAN_JSON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum {
|
||||
JSON_NULL,
|
||||
JSON_BOOL,
|
||||
JSON_STRING,
|
||||
JSON_NUMBER,
|
||||
JSON_ARRAY,
|
||||
JSON_OBJECT,
|
||||
} JsonTag;
|
||||
|
||||
typedef struct JsonNode JsonNode;
|
||||
|
||||
struct JsonNode
|
||||
{
|
||||
/* only if parent is an object or array (NULL otherwise) */
|
||||
JsonNode *parent;
|
||||
JsonNode *prev, *next;
|
||||
|
||||
/* only if parent is an object (NULL otherwise) */
|
||||
char *key; /* Must be valid UTF-8. */
|
||||
|
||||
JsonTag tag;
|
||||
union {
|
||||
/* JSON_BOOL */
|
||||
bool bool_;
|
||||
|
||||
/* JSON_STRING */
|
||||
char *string_; /* Must be valid UTF-8. */
|
||||
|
||||
/* JSON_NUMBER */
|
||||
double number_;
|
||||
|
||||
/* JSON_ARRAY */
|
||||
/* JSON_OBJECT */
|
||||
struct {
|
||||
JsonNode *head, *tail;
|
||||
} children;
|
||||
};
|
||||
};
|
||||
|
||||
/*** Encoding, decoding, and validation ***/
|
||||
|
||||
JsonNode *json_decode (const char *json);
|
||||
char *json_encode (const JsonNode *node);
|
||||
char *json_encode_string (const char *str);
|
||||
char *json_stringify (const JsonNode *node, const char *space);
|
||||
void json_delete (JsonNode *node);
|
||||
|
||||
bool json_validate (const char *json);
|
||||
|
||||
/*** Lookup and traversal ***/
|
||||
|
||||
JsonNode *json_find_element (JsonNode *array, int index);
|
||||
JsonNode *json_find_member (JsonNode *object, const char *key);
|
||||
|
||||
JsonNode *json_first_child (const JsonNode *node);
|
||||
|
||||
#define json_foreach(i, object_or_array) \
|
||||
for ((i) = json_first_child(object_or_array); \
|
||||
(i) != NULL; \
|
||||
(i) = (i)->next)
|
||||
|
||||
/*** Construction and manipulation ***/
|
||||
|
||||
JsonNode *json_mknull(void);
|
||||
JsonNode *json_mkbool(bool b);
|
||||
JsonNode *json_mkstring(const char *s);
|
||||
JsonNode *json_mknumber(double n);
|
||||
JsonNode *json_mkarray(void);
|
||||
JsonNode *json_mkobject(void);
|
||||
|
||||
void json_append_element(JsonNode *array, JsonNode *element);
|
||||
void json_prepend_element(JsonNode *array, JsonNode *element);
|
||||
void json_append_member(JsonNode *object, const char *key, JsonNode *value);
|
||||
void json_prepend_member(JsonNode *object, const char *key, JsonNode *value);
|
||||
|
||||
void json_remove_from_parent(JsonNode *node);
|
||||
|
||||
/*** Debugging ***/
|
||||
|
||||
/*
|
||||
* Look for structure and encoding problems in a JsonNode or its descendents.
|
||||
*
|
||||
* If a problem is detected, return false, writing a description of the problem
|
||||
* to errmsg (unless errmsg is NULL).
|
||||
*/
|
||||
bool json_check(const JsonNode *node, char errmsg[256]);
|
||||
|
||||
#endif
|
||||
@@ -1,202 +0,0 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
)
|
||||
|
||||
// LocalDirectory gets the caller's file directory
|
||||
// Equivalent to node's __DIRNAME
|
||||
func LocalDirectory() string {
|
||||
_, thisFile, _, _ := runtime.Caller(1)
|
||||
return filepath.Dir(thisFile)
|
||||
}
|
||||
|
||||
// RelativeToCwd returns an absolute path based on the cwd
|
||||
// and the given relative path
|
||||
func RelativeToCwd(relativePath string) (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(cwd, relativePath), nil
|
||||
}
|
||||
|
||||
// Mkdir will create the given directory
|
||||
func Mkdir(dirname string) error {
|
||||
return os.Mkdir(dirname, 0755)
|
||||
}
|
||||
|
||||
// DeleteFile will delete the given file
|
||||
func DeleteFile(filename string) error {
|
||||
return os.Remove(filename)
|
||||
}
|
||||
|
||||
// CopyFile from source to target
|
||||
func CopyFile(source string, target string) error {
|
||||
s, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
d, err := os.Create(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(d, s); err != nil {
|
||||
d.Close()
|
||||
return err
|
||||
}
|
||||
return d.Close()
|
||||
}
|
||||
|
||||
// DirExists - Returns true if the given path resolves to a directory on the filesystem
|
||||
func DirExists(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.Mode().IsDir()
|
||||
}
|
||||
|
||||
// FileExists returns a boolean value indicating whether
|
||||
// the given file exists
|
||||
func FileExists(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// RelativePath returns a qualified path created by joining the
|
||||
// directory of the calling file and the given relative path.
|
||||
//
|
||||
// Example: RelativePath("..") in *this* file would give you '/path/to/wails2/v2/internal`
|
||||
func RelativePath(relativepath string, optionalpaths ...string) string {
|
||||
_, thisFile, _, _ := runtime.Caller(1)
|
||||
localDir := filepath.Dir(thisFile)
|
||||
|
||||
// If we have optional paths, join them to the relativepath
|
||||
if len(optionalpaths) > 0 {
|
||||
paths := []string{relativepath}
|
||||
paths = append(paths, optionalpaths...)
|
||||
relativepath = filepath.Join(paths...)
|
||||
}
|
||||
result, err := filepath.Abs(filepath.Join(localDir, relativepath))
|
||||
if err != nil {
|
||||
// I'm allowing this for 1 reason only: It's fatal if the path
|
||||
// supplied is wrong as it's only used internally in Wails. If we get
|
||||
// that path wrong, we should know about it immediately. The other reason is
|
||||
// that it cuts down a ton of unnecassary error handling.
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MustLoadString attempts to load a string and will abort with a fatal message if
|
||||
// something goes wrong
|
||||
func MustLoadString(filename string) string {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Unable to load file '%s': %s\n", filename, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&data))
|
||||
}
|
||||
|
||||
// MD5File returns the md5sum of the given file
|
||||
func MD5File(filename string) (string, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := md5.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// MustMD5File will call MD5File and abort the program on error
|
||||
func MustMD5File(filename string) string {
|
||||
result, err := MD5File(filename)
|
||||
if err != nil {
|
||||
println("FATAL: Unable to MD5Sum file:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MustWriteString will attempt to write the given data to the given filename
|
||||
// It will abort the program in the event of a failure
|
||||
func MustWriteString(filename string, data string) {
|
||||
err := ioutil.WriteFile(filename, []byte(data), 0755)
|
||||
if err != nil {
|
||||
fatal("Unable to write file", filename, ":", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// fatal will print the optional messages and die
|
||||
func fatal(message ...string) {
|
||||
if len(message) > 0 {
|
||||
print("FATAL:")
|
||||
for text := range message {
|
||||
print(text)
|
||||
}
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// GetSubdirectories returns a list of subdirectories for the given root directory
|
||||
func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
|
||||
var result slicer.StringSlicer
|
||||
|
||||
// Iterate root dir
|
||||
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If we have a directory, save it
|
||||
if info.IsDir() {
|
||||
result.Add(path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func DirIsEmpty(dir string) (bool, error) {
|
||||
|
||||
if !DirExists(dir) {
|
||||
return false, fmt.Errorf("DirIsEmpty called with a non-existant directory: %s", dir)
|
||||
}
|
||||
|
||||
// CREDIT: https://stackoverflow.com/a/30708914/8325411
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Readdirnames(1) // Or f.Readdir(1)
|
||||
if err == io.EOF {
|
||||
return true, nil
|
||||
}
|
||||
return false, err // Either not empty or error, suits both cases
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestRelativePath(t *testing.T) {
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
is.Equal(err, nil)
|
||||
|
||||
// Check current directory
|
||||
actual := RelativePath(".")
|
||||
is.Equal(actual, cwd)
|
||||
|
||||
// Check 2 parameters
|
||||
actual = RelativePath("..", "fs")
|
||||
is.Equal(actual, cwd)
|
||||
|
||||
// Check 3 parameters including filename
|
||||
actual = RelativePath("..", "fs", "fs.go")
|
||||
expected := filepath.Join(cwd, "fs.go")
|
||||
is.Equal(actual, expected)
|
||||
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tdewolff/minify"
|
||||
"github.com/tdewolff/minify/js"
|
||||
)
|
||||
|
||||
type assetTypes struct {
|
||||
JS string
|
||||
CSS string
|
||||
FAVICON string
|
||||
HTML string
|
||||
}
|
||||
|
||||
// AssetTypes is an enum for the asset type keys
|
||||
var AssetTypes *assetTypes = &assetTypes{
|
||||
JS: "javascript",
|
||||
CSS: "css",
|
||||
FAVICON: "favicon",
|
||||
HTML: "html",
|
||||
}
|
||||
|
||||
// Asset describes an asset type and its path
|
||||
type Asset struct {
|
||||
Type string
|
||||
Path string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Load the asset from disk
|
||||
func (a *Asset) Load(basedirectory string) error {
|
||||
assetpath := filepath.Join(basedirectory, a.Path)
|
||||
data, err := ioutil.ReadFile(assetpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Data = string(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsString returns the data as a READ ONLY string
|
||||
func (a *Asset) AsString() string {
|
||||
return a.Data
|
||||
}
|
||||
|
||||
// AsCHexData processes the asset data so it may be used by C
|
||||
func (a *Asset) AsCHexData() string {
|
||||
|
||||
// This will be our final string to hexify
|
||||
dataString := a.Data
|
||||
|
||||
switch a.Type {
|
||||
case AssetTypes.HTML:
|
||||
|
||||
// Escape HTML
|
||||
var re = regexp.MustCompile(`\s{2,}`)
|
||||
result := re.ReplaceAllString(a.Data, ``)
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
result = strings.ReplaceAll(result, "\r\n", "")
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
|
||||
// Inject wailsloader code
|
||||
result = strings.Replace(result, `</body>`, `<script id='wailsloader'>window.wailsloader = { html: true, runtime: false, userjs: false, usercss: false };var self=document.querySelector('#wailsloader');self.parentNode.removeChild(self);</script></body>`, 1)
|
||||
|
||||
url := url.URL{Path: result}
|
||||
urlString := strings.ReplaceAll(url.String(), "/", "%2f")
|
||||
|
||||
// Save Data uRI string
|
||||
dataString = "data:text/html;charset=utf-8," + urlString
|
||||
|
||||
case AssetTypes.CSS:
|
||||
|
||||
// Escape CSS data
|
||||
var re = regexp.MustCompile(`\s{2,}`)
|
||||
result := re.ReplaceAllString(a.Data, ``)
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
result = strings.ReplaceAll(result, "\r\n", "")
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
result = strings.ReplaceAll(result, "\t", "")
|
||||
result = strings.ReplaceAll(result, `\`, `\\`)
|
||||
result = strings.ReplaceAll(result, `"`, `\"`)
|
||||
result = strings.ReplaceAll(result, `'`, `\'`)
|
||||
result = strings.ReplaceAll(result, ` {`, `{`)
|
||||
result = strings.ReplaceAll(result, `: `, `:`)
|
||||
dataString = fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result)
|
||||
|
||||
case AssetTypes.JS:
|
||||
m := minify.New()
|
||||
m.AddFunc("application/javascript", js.Minify)
|
||||
var err error
|
||||
dataString, err = m.String("application/javascript", a.Data+";")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
a.Data = dataString
|
||||
}
|
||||
|
||||
// Get byte data of the string
|
||||
bytes := *(*[]byte)(unsafe.Pointer(&dataString))
|
||||
|
||||
// Create a strings builder
|
||||
var cdata strings.Builder
|
||||
|
||||
// Set buffer size to 4k
|
||||
cdata.Grow(4096)
|
||||
|
||||
// Convert each byte to hex
|
||||
for _, b := range bytes {
|
||||
cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
||||
}
|
||||
|
||||
return cdata.String()
|
||||
}
|
||||
|
||||
// Dump will output the asset to the terminal
|
||||
func (a *Asset) Dump() {
|
||||
fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/assetdb"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// AssetBundle is a collection of Assets
|
||||
type AssetBundle struct {
|
||||
assets []*Asset
|
||||
basedirectory string
|
||||
}
|
||||
|
||||
// NewAssetBundle creates a new AssetBundle struct containing
|
||||
// the given html and all the assets referenced by it
|
||||
func NewAssetBundle(pathToHTML string) (*AssetBundle, error) {
|
||||
|
||||
// Create result
|
||||
result := &AssetBundle{
|
||||
basedirectory: filepath.Dir(pathToHTML),
|
||||
}
|
||||
|
||||
err := result.loadAssets(pathToHTML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// loadAssets processes the given html file and loads in
|
||||
// all referenced assets
|
||||
func (a *AssetBundle) loadAssets(pathToHTML string) error {
|
||||
|
||||
// Save HTML
|
||||
htmlAsset := &Asset{
|
||||
Type: AssetTypes.HTML,
|
||||
Path: filepath.Base(pathToHTML),
|
||||
}
|
||||
err := htmlAsset.Load(a.basedirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.assets = append(a.assets, htmlAsset)
|
||||
|
||||
return a.processHTML(htmlAsset.AsString())
|
||||
}
|
||||
|
||||
// Credit to: https://drstearns.github.io/tutorials/tokenizing/
|
||||
func (a *AssetBundle) processHTML(htmldata string) error {
|
||||
|
||||
// Tokenize the html
|
||||
buf := bytes.NewBufferString(htmldata)
|
||||
tokenizer := html.NewTokenizer(buf)
|
||||
|
||||
for {
|
||||
//get the next token type
|
||||
tokenType := tokenizer.Next()
|
||||
|
||||
//if it's an error token, we either reached
|
||||
//the end of the file, or the HTML was malformed
|
||||
if tokenType == html.ErrorToken {
|
||||
err := tokenizer.Err()
|
||||
if err == io.EOF {
|
||||
//end of the file, break out of the loop
|
||||
break
|
||||
}
|
||||
//otherwise, there was an error tokenizing,
|
||||
//which likely means the HTML was malformed.
|
||||
//since this is a simple command-line utility,
|
||||
//we can just use log.Fatalf() to report the error
|
||||
//and exit the process with a non-zero status code
|
||||
return tokenizer.Err()
|
||||
}
|
||||
|
||||
//process the token according to the token type...
|
||||
if tokenType == html.StartTagToken {
|
||||
//get the token
|
||||
token := tokenizer.Token()
|
||||
|
||||
//if the name of the element is "title"
|
||||
if "link" == token.Data {
|
||||
//the next token should be the page title
|
||||
tokenType = tokenizer.Next()
|
||||
//just make sure it's actually a text token
|
||||
asset := &Asset{}
|
||||
for _, attr := range token.Attr {
|
||||
// Favicon
|
||||
if attr.Key == "rel" && attr.Val == "icon" {
|
||||
asset.Type = AssetTypes.FAVICON
|
||||
}
|
||||
if attr.Key == "href" {
|
||||
asset.Path = attr.Val
|
||||
}
|
||||
// stylesheet
|
||||
if attr.Key == "rel" && attr.Val == "stylesheet" {
|
||||
asset.Type = AssetTypes.CSS
|
||||
}
|
||||
}
|
||||
err := asset.Load(a.basedirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.assets = append(a.assets, asset)
|
||||
}
|
||||
if "script" == token.Data {
|
||||
|
||||
tokenType = tokenizer.Next()
|
||||
//just make sure it's actually a text token
|
||||
asset := &Asset{Type: AssetTypes.JS}
|
||||
for _, attr := range token.Attr {
|
||||
if attr.Key == "src" {
|
||||
asset.Path = attr.Val
|
||||
break
|
||||
}
|
||||
}
|
||||
err := asset.Load(a.basedirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.assets = append(a.assets, asset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteToCFile dumps all the assets to C files in the given directory
|
||||
func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
||||
|
||||
// Write out the assets.c file
|
||||
var cdata strings.Builder
|
||||
|
||||
// Write header
|
||||
header := `// assets.c
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
||||
// This file was auto-generated. DO NOT MODIFY.
|
||||
|
||||
`
|
||||
cdata.WriteString(header)
|
||||
|
||||
// Loop over the Assets
|
||||
var err error
|
||||
assetVariables := slicer.String()
|
||||
var variableName string
|
||||
for index, asset := range a.assets {
|
||||
// For desktop we ignore the favicon
|
||||
if asset.Type == AssetTypes.FAVICON {
|
||||
continue
|
||||
}
|
||||
variableName = fmt.Sprintf("%s%d", asset.Type, index)
|
||||
assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
|
||||
cdata.WriteString(assetCdata)
|
||||
assetVariables.Add(variableName)
|
||||
}
|
||||
|
||||
if assetVariables.Length() > 0 {
|
||||
cdata.WriteString(fmt.Sprintf("\nconst unsigned char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
||||
} else {
|
||||
cdata.WriteString("\nconst unsigned char *assets[] = { 0x00 };")
|
||||
}
|
||||
|
||||
// Save file
|
||||
assetsFile := filepath.Join(targetDir, "assets.c")
|
||||
err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return assetsFile, nil
|
||||
}
|
||||
|
||||
// ConvertToAssetDB returns an assetdb.AssetDB initialized with
|
||||
// the items in the AssetBundle
|
||||
func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
|
||||
assetdb := assetdb.NewAssetDB()
|
||||
|
||||
// Loop over the Assets
|
||||
for _, asset := range a.assets {
|
||||
assetdb.AddAsset(asset.Path, []byte(asset.Data))
|
||||
}
|
||||
|
||||
return assetdb, nil
|
||||
}
|
||||
|
||||
// Dump will output the assets to the terminal
|
||||
func (a *AssetBundle) Dump() {
|
||||
println("Assets:")
|
||||
for _, asset := range a.assets {
|
||||
asset.Dump()
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CustomLogger defines what a user can do with a logger
|
||||
type CustomLogger interface {
|
||||
// Writeln writes directly to the output with no log level plus line ending
|
||||
Writeln(message string) error
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
Write(message string) error
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
Trace(format string, args ...interface{}) error
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
Debug(format string, args ...interface{}) error
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
Info(format string, args ...interface{}) error
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
Warning(format string, args ...interface{}) error
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
Error(format string, args ...interface{}) error
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
Fatal(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// customLogger is a utlility to log messages to a number of destinations
|
||||
type customLogger struct {
|
||||
logger *Logger
|
||||
name string
|
||||
}
|
||||
|
||||
// New creates a new customLogger. You may pass in a number of `io.Writer`s that
|
||||
// are the targets for the logs
|
||||
func newcustomLogger(logger *Logger, name string) *customLogger {
|
||||
result := &customLogger{
|
||||
name: name,
|
||||
logger: logger,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Writeln writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *customLogger) Writeln(message string) error {
|
||||
return l.logger.Writeln(message)
|
||||
}
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
func (l *customLogger) Write(message string) error {
|
||||
return l.logger.Write(message)
|
||||
}
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *customLogger) Trace(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Trace(format, args...)
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *customLogger) Debug(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Debug(format, args...)
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *customLogger) Info(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Info(format, args...)
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *customLogger) Warning(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Warning(format, args...)
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *customLogger) Error(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Error(format, args...)
|
||||
|
||||
}
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
func (l *customLogger) Fatal(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
l.logger.Fatal(format, args...)
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
)
|
||||
|
||||
// LogLevel is an alias for the public LogLevel
|
||||
type LogLevel = logger.LogLevel
|
||||
|
||||
// Logger is a utlility to log messages to a number of destinations
|
||||
type Logger struct {
|
||||
output logger.Logger
|
||||
logLevel LogLevel
|
||||
showLevelInLog bool
|
||||
}
|
||||
|
||||
// New creates a new Logger. You may pass in a number of `io.Writer`s that
|
||||
// are the targets for the logs
|
||||
func New(output logger.Logger) *Logger {
|
||||
result := &Logger{
|
||||
logLevel: logger.INFO,
|
||||
showLevelInLog: true,
|
||||
output: output,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CustomLogger creates a new custom logger that prints out a name/id
|
||||
// before the messages
|
||||
func (l *Logger) CustomLogger(name string) CustomLogger {
|
||||
return newcustomLogger(l, name)
|
||||
}
|
||||
|
||||
// HideLogLevel removes the loglevel text from the start of each logged line
|
||||
func (l *Logger) HideLogLevel() {
|
||||
l.showLevelInLog = true
|
||||
}
|
||||
|
||||
// SetLogLevel sets the minimum level of logs that will be output
|
||||
func (l *Logger) SetLogLevel(level LogLevel) {
|
||||
l.logLevel = level
|
||||
}
|
||||
|
||||
// Writeln writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Writeln(message string) error {
|
||||
return l.output.Print(message)
|
||||
}
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
func (l *Logger) Write(message string) error {
|
||||
return l.output.Print(message)
|
||||
}
|
||||
|
||||
// Print writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Print(message string) error {
|
||||
return l.Write(message)
|
||||
}
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *Logger) Trace(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.TRACE {
|
||||
return l.output.Trace(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *Logger) Debug(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.DEBUG {
|
||||
return l.output.Debug(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *Logger) Info(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.INFO {
|
||||
return l.output.Info(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *Logger) Warning(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.WARNING {
|
||||
return l.output.Warning(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *Logger) Error(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.ERROR {
|
||||
return l.output.Error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
func (l *Logger) Fatal(format string, args ...interface{}) {
|
||||
err := l.output.Fatal(fmt.Sprintf(format, args...))
|
||||
// Not much we can do but print it out before exiting
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package messagedispatcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Client defines what a frontend client can do
|
||||
type Client interface {
|
||||
Quit()
|
||||
NotifyEvent(message string)
|
||||
CallResult(message string)
|
||||
OpenDialog(dialogOptions *options.OpenDialog, callbackID string)
|
||||
SaveDialog(dialogOptions *options.SaveDialog, callbackID string)
|
||||
WindowSetTitle(title string)
|
||||
WindowShow()
|
||||
WindowHide()
|
||||
WindowCenter()
|
||||
WindowMaximise()
|
||||
WindowUnmaximise()
|
||||
WindowMinimise()
|
||||
WindowUnminimise()
|
||||
WindowPosition(x int, y int)
|
||||
WindowSize(width int, height int)
|
||||
WindowFullscreen()
|
||||
WindowUnFullscreen()
|
||||
WindowSetColour(colour int)
|
||||
DarkModeEnabled(callbackID string)
|
||||
}
|
||||
|
||||
// DispatchClient is what the frontends use to interface with the
|
||||
// dispatcher
|
||||
type DispatchClient struct {
|
||||
id string
|
||||
logger logger.CustomLogger
|
||||
|
||||
bus *servicebus.ServiceBus
|
||||
|
||||
// Client
|
||||
frontend Client
|
||||
}
|
||||
|
||||
func newDispatchClient(id string, frontend Client, logger logger.CustomLogger, bus *servicebus.ServiceBus) *DispatchClient {
|
||||
|
||||
return &DispatchClient{
|
||||
id: id,
|
||||
frontend: frontend,
|
||||
logger: logger,
|
||||
bus: bus,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DispatchMessage is called by the front ends. It is passed
|
||||
// an IPC message, translates it to a more concrete message
|
||||
// type then publishes it on the service bus.
|
||||
func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
||||
|
||||
// Parse the message
|
||||
d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
|
||||
parsedMessage, err := message.Parse(incomingMessage)
|
||||
if err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Save this client id
|
||||
parsedMessage.ClientID = d.id
|
||||
|
||||
d.logger.Trace("I got a parsedMessage: %+v", parsedMessage)
|
||||
|
||||
// Check error
|
||||
if err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
// Hrm... what do we do with this?
|
||||
d.bus.PublishForTarget("generic:message", incomingMessage, d.id)
|
||||
return
|
||||
}
|
||||
|
||||
// Publish the parsed message
|
||||
d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id)
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CallMessage struct {
|
||||
Name string `json:"name"`
|
||||
Args []json.RawMessage `json:"args"`
|
||||
CallbackID string `json:"callbackID,omitempty"`
|
||||
}
|
||||
|
||||
// callMessageParser does what it says on the tin!
|
||||
func callMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Call messages must be at least 3 bytes `C{}``
|
||||
if len(message) < 3 {
|
||||
return nil, fmt.Errorf("call message was an invalid length")
|
||||
}
|
||||
|
||||
callMessage := new(CallMessage)
|
||||
|
||||
m := message[1:]
|
||||
|
||||
err := json.Unmarshal([]byte(m), callMessage)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
topic := "call:invoke"
|
||||
|
||||
// Create a new parsed message struct
|
||||
parsedMessage := &parsedMessage{Topic: topic, Data: callMessage}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dialogMessageParser does what it says on the tin!
|
||||
func dialogMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Dialog messages must be at least 4 bytes
|
||||
if len(message) < 4 {
|
||||
return nil, fmt.Errorf("dialog message was an invalid length")
|
||||
}
|
||||
|
||||
var topic = "bad topic from dialogMessageParser"
|
||||
var responseMessage *parsedMessage
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
// Format of Dialog response messages: D<dialog type><callbackID>|<[]string as json encoded string>
|
||||
case 'D':
|
||||
dialogType := message[1]
|
||||
message = message[2:]
|
||||
idx := strings.IndexByte(message, '|')
|
||||
if idx < 0 {
|
||||
return nil, fmt.Errorf("Invalid dialog response message format")
|
||||
}
|
||||
callbackID := message[:idx+1]
|
||||
payloadData := message[idx+1:]
|
||||
|
||||
switch dialogType {
|
||||
case 'O':
|
||||
var data []string
|
||||
topic = "dialog:openselected:" + callbackID
|
||||
err := json.Unmarshal([]byte(payloadData), &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: data}
|
||||
case 'S':
|
||||
topic = "dialog:saveselected:" + callbackID
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: payloadData}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid message to dialogMessageParser()")
|
||||
}
|
||||
|
||||
return responseMessage, nil
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type EventMessage struct {
|
||||
Name string `json:"name"`
|
||||
Data []interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type OnEventMessage struct {
|
||||
Name string
|
||||
Callback func(optionalData ...interface{})
|
||||
Counter int
|
||||
}
|
||||
|
||||
// eventMessageParser does what it says on the tin!
|
||||
func eventMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Event messages must be at least 2 bytes
|
||||
if len(message) < 3 {
|
||||
return nil, fmt.Errorf("event message was an invalid length")
|
||||
}
|
||||
|
||||
eventMessage := new(EventMessage)
|
||||
direction := message[1]
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
case 'E':
|
||||
m := message[2:]
|
||||
err := json.Unmarshal([]byte(m), eventMessage)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
topic := "event:emit:from:" + string(direction)
|
||||
|
||||
// Create a new parsed message struct
|
||||
parsedMessage := &parsedMessage{Topic: topic, Data: eventMessage}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package message
|
||||
|
||||
import "fmt"
|
||||
|
||||
var logMessageMap = map[byte]string{
|
||||
'P': "log:print",
|
||||
'T': "log:trace",
|
||||
'D': "log:debug",
|
||||
'I': "log:info",
|
||||
'W': "log:warning",
|
||||
'E': "log:error",
|
||||
'F': "log:fatal",
|
||||
'S': "log:setlevel",
|
||||
}
|
||||
|
||||
// logMessageParser does what it says on the tin!
|
||||
func logMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Log messages must be at least 2 bytes
|
||||
if len(message) < 2 {
|
||||
return nil, fmt.Errorf("log message was an invalid length")
|
||||
}
|
||||
|
||||
// Switch on the log type
|
||||
messageTopic := logMessageMap[message[1]]
|
||||
|
||||
// If the type is invalid, raise error
|
||||
if messageTopic == "" {
|
||||
return nil, fmt.Errorf("log message type '%c' invalid", message[1])
|
||||
}
|
||||
|
||||
// Create a new parsed message struct
|
||||
parsedMessage := &parsedMessage{Topic: messageTopic, Data: message[2:]}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user