mirror of
https://github.com/taigrr/wails.git
synced 2026-04-04 06:02:43 -07:00
Compare commits
364 Commits
sync-store
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e44f2fe06d | ||
|
|
ad65d55abd | ||
|
|
2b5bbfd897 | ||
|
|
d2020fedda | ||
|
|
98789bd85a | ||
|
|
e6ace2fafd | ||
|
|
a55fc4d0e9 | ||
|
|
a09e9d4586 | ||
|
|
0d49a8cc83 | ||
|
|
ba7c8cf0e0 | ||
|
|
60f67d4642 | ||
|
|
0dc6c20c65 | ||
|
|
5d41aad539 | ||
|
|
e9a0e45d5c | ||
|
|
2dedd0b702 | ||
|
|
1889973af1 | ||
|
|
75e852451c | ||
|
|
cfee44b18b | ||
|
|
f384fc7562 | ||
|
|
44c55d06a6 | ||
|
|
f4ca9a6b9e | ||
|
|
a54d875ceb | ||
|
|
657df8bdda | ||
|
|
c0fabc0bb7 | ||
|
|
4fd476bb7d | ||
|
|
be0a9ddf6b | ||
|
|
f7db0b7373 | ||
|
|
5ca57e446a | ||
|
|
33a27b06a6 | ||
|
|
b50acaba91 | ||
|
|
6253ac30b7 | ||
|
|
2a93e2694d | ||
|
|
91fb3501c5 | ||
|
|
c7d5e7de72 | ||
|
|
02ef02ec9e | ||
|
|
f77626490f | ||
|
|
6fd13fb4d4 | ||
|
|
9cd5ad69ce | ||
|
|
7dbf74c3e0 | ||
|
|
6613cff1d5 | ||
|
|
6a2700e0bf | ||
|
|
e18ba0eb81 | ||
|
|
25f464c177 | ||
|
|
57a9d5f472 | ||
|
|
3b0f852f37 | ||
|
|
a85c8ba894 | ||
|
|
f0c932713b | ||
|
|
8625e99625 | ||
|
|
c79f86fd03 | ||
|
|
a0774cf71c | ||
|
|
16b872352d | ||
|
|
e414eda151 | ||
|
|
719f4b5113 | ||
|
|
8b75d57cff | ||
|
|
7aea6980b5 | ||
|
|
99e790944f | ||
|
|
937f0f77c2 | ||
|
|
c86aa7d3c8 | ||
|
|
d380a8d6a7 | ||
|
|
a8995c5377 | ||
|
|
2a6162a91f | ||
|
|
4e54229dfb | ||
|
|
ce58e9eb6c | ||
|
|
3c49577d5b | ||
|
|
fb8aa8cc24 | ||
|
|
b45e04f2db | ||
|
|
026daf5e57 | ||
|
|
34ac62e4ac | ||
|
|
a1f9d9ca06 | ||
|
|
e6fbd03346 | ||
|
|
dd35f0119b | ||
|
|
b837b1e131 | ||
|
|
7832a3be19 | ||
|
|
8a768cce77 | ||
|
|
0802d0d57a | ||
|
|
6fa2ebdd4f | ||
|
|
11bf564b73 | ||
|
|
65bea04080 | ||
|
|
4572b790c6 | ||
|
|
f419941065 | ||
|
|
d3d965ee1f | ||
|
|
2a55983bc9 | ||
|
|
7e42052da0 | ||
|
|
e8bb950e06 | ||
|
|
13dc0c78df | ||
|
|
a081c1e498 | ||
|
|
a3e50e760e | ||
|
|
b6aa53175f | ||
|
|
094d8d4b75 | ||
|
|
0fa67c94c1 | ||
|
|
083aee1588 | ||
|
|
5c39467879 | ||
|
|
fb1d4c6325 | ||
|
|
a0fe2f1e13 | ||
|
|
dbe6000632 | ||
|
|
7e67562e19 | ||
|
|
42dc96cf6b | ||
|
|
4fd3516f41 | ||
|
|
6f218264ed | ||
|
|
810b3c7440 | ||
|
|
40fdf75042 | ||
|
|
bc260b08b2 | ||
|
|
b920f45f60 | ||
|
|
7f02f6886f | ||
|
|
aa271d6498 | ||
|
|
9e6ae4d762 | ||
|
|
ef8b58b208 | ||
|
|
0422921dc7 | ||
|
|
5ba03937c3 | ||
|
|
cc4967d457 | ||
|
|
eaeecbb180 | ||
|
|
069de31003 | ||
|
|
ae5c74b6cd | ||
|
|
716888dcb2 | ||
|
|
ca4e2b2391 | ||
|
|
630abbd3c8 | ||
|
|
33f40c2eac | ||
|
|
7c7b6ba082 | ||
|
|
205a6db9f1 | ||
|
|
659fe1b281 | ||
|
|
e86d622219 | ||
|
|
ee3da60002 | ||
|
|
88643134c9 | ||
|
|
56553bb683 | ||
|
|
cd14f4221e | ||
|
|
108fcbb161 | ||
|
|
e12762a584 | ||
|
|
98cc356b92 | ||
|
|
32ba9e78fe | ||
|
|
a737d85fa5 | ||
|
|
435ac02647 | ||
|
|
255b4e103a | ||
|
|
bd8771849b | ||
|
|
b32349effe | ||
|
|
cd03b4b633 | ||
|
|
c45315d61d | ||
|
|
62374b9b53 | ||
|
|
25d8a8763a | ||
|
|
7929584ec5 | ||
|
|
73ee9ef530 | ||
|
|
003eecc4ff | ||
|
|
f97341abbe | ||
|
|
e2599c0f76 | ||
|
|
39878c1a52 | ||
|
|
c0932f3fa4 | ||
|
|
dc605c1683 | ||
|
|
c7c9ace232 | ||
|
|
5be197b68f | ||
|
|
3e5c406c95 | ||
|
|
be6bebebe4 | ||
|
|
5b33ed28fd | ||
|
|
b7a59daee1 | ||
|
|
3d4ea3918b | ||
|
|
4347d950d1 | ||
|
|
8e096ff0b0 | ||
|
|
6a03a5f8eb | ||
|
|
6116f5fc05 | ||
|
|
c49192e847 | ||
|
|
6fb2489dae | ||
|
|
c1acbed2c7 | ||
|
|
8f6275721a | ||
|
|
ef40a2eb8e | ||
|
|
4f7b5c71d1 | ||
|
|
49db62afd0 | ||
|
|
9098632aa3 | ||
|
|
f87a0f039a | ||
|
|
a03f5871bd | ||
|
|
46f026c04c | ||
|
|
5e2373acb1 | ||
|
|
918d6240f2 | ||
|
|
453a225427 | ||
|
|
2795684d5c | ||
|
|
e295a5abd9 | ||
|
|
fdf18b7dfa | ||
|
|
1e95d0eb44 | ||
|
|
d7f2d800de | ||
|
|
ef4d9ae97c | ||
|
|
ddb875f788 | ||
|
|
9a32852119 | ||
|
|
e795283482 | ||
|
|
e6036d31cf | ||
|
|
5a85a6e4f9 | ||
|
|
0113fbff4f | ||
|
|
145656bc43 | ||
|
|
6153c48c86 | ||
|
|
912c0125e5 | ||
|
|
dc2ad3cd3e | ||
|
|
e3783c5480 | ||
|
|
440abbe3b6 | ||
|
|
95369d7c3d | ||
|
|
9e0023961b | ||
|
|
307e07b4c8 | ||
|
|
fb88eadb58 | ||
|
|
6fdf088531 | ||
|
|
07b6fc0c52 | ||
|
|
1b466090e8 | ||
|
|
c990760f22 | ||
|
|
aeb7d857ee | ||
|
|
78f99c2697 | ||
|
|
7885718d42 | ||
|
|
288da8c147 | ||
|
|
7e31db809a | ||
|
|
fb0ccfc8e6 | ||
|
|
c9bf4e3d48 | ||
|
|
e8a85d4cd6 | ||
|
|
2a59272b86 | ||
|
|
ff5e2862b8 | ||
|
|
51678afdc7 | ||
|
|
3b6a3df03d | ||
|
|
1c97559151 | ||
|
|
082e695c83 | ||
|
|
4a5e4d3a5e | ||
|
|
8988f29cea | ||
|
|
6150010d17 | ||
|
|
c165b97d57 | ||
|
|
39c599d2de | ||
|
|
be952ba2da | ||
|
|
5a141d343e | ||
|
|
7e4ad307aa | ||
|
|
d8bb418851 | ||
|
|
f1cd84d0c8 | ||
|
|
ba6538da7c | ||
|
|
afea1cbb4c | ||
|
|
19fbc884ae | ||
|
|
67861e4f70 | ||
|
|
228285f693 | ||
|
|
9f62a08cd2 | ||
|
|
d97cd1b75f | ||
|
|
5fd8312f63 | ||
|
|
90b7d5f519 | ||
|
|
93f4549efa | ||
|
|
b5c8dfac97 | ||
|
|
ee01b7759e | ||
|
|
8dba591cda | ||
|
|
302db87bec | ||
|
|
161ff3b32a | ||
|
|
ffdfbb8ae5 | ||
|
|
53b54a8e52 | ||
|
|
0403e0a783 | ||
|
|
8a2acacc37 | ||
|
|
256e84c4d4 | ||
|
|
c10e8788d8 | ||
|
|
6eb89f61b3 | ||
|
|
e3d2ff9ea1 | ||
|
|
665dfa6aee | ||
|
|
4f7e2128d1 | ||
|
|
8112facb4e | ||
|
|
944261b5e4 | ||
|
|
ba528d0534 | ||
|
|
a28afe86ce | ||
|
|
5e0026e124 | ||
|
|
f23ed3c319 | ||
|
|
6aae2eb1df | ||
|
|
f4943bc26c | ||
|
|
1c6578e6ef | ||
|
|
5ef200f21c | ||
|
|
b3822137f7 | ||
|
|
858789f442 | ||
|
|
3209b39488 | ||
|
|
0c9f6edeb6 | ||
|
|
aabcef2958 | ||
|
|
10d68b2676 | ||
|
|
e960afe8f6 | ||
|
|
ae677ce9db | ||
|
|
7f9c59a021 | ||
|
|
d8fdc96899 | ||
|
|
c3280e8b60 | ||
|
|
26a1f78d56 | ||
|
|
ee9c98c515 | ||
|
|
29ec06fb0a | ||
|
|
c1155e255b | ||
|
|
4e39566118 | ||
|
|
3f3094f0aa | ||
|
|
84730d2f4d | ||
|
|
b8bb891275 | ||
|
|
7bcb5be1a5 | ||
|
|
081c842149 | ||
|
|
6bdcec8105 | ||
|
|
3c7937bff9 | ||
|
|
d7f832c00e | ||
|
|
8cd39f6a9a | ||
|
|
ac27137e5a | ||
|
|
762632d55a | ||
|
|
48c17dac87 | ||
|
|
8666935caf | ||
|
|
0ec6707263 | ||
|
|
d4224772b4 | ||
|
|
cd99376da9 | ||
|
|
02fd4ec477 | ||
|
|
3b851e9a22 | ||
|
|
5ce5e129cf | ||
|
|
9b0f58ddf5 | ||
|
|
bed5619d4e | ||
|
|
69c4e6ea28 | ||
|
|
ea7b593693 | ||
|
|
7ac833e396 | ||
|
|
a5e909337e | ||
|
|
0c120eccc9 | ||
|
|
e6addafcdd | ||
|
|
ef11f45df8 | ||
|
|
72fc2204b4 | ||
|
|
15c08ef425 | ||
|
|
52bb397105 | ||
|
|
5572fccaf6 | ||
|
|
65d591e2a6 | ||
|
|
4bf59301e5 | ||
|
|
629e8f73f4 | ||
|
|
eb68ba5120 | ||
|
|
a8bff7868b | ||
|
|
ae04b4fcc0 | ||
|
|
5eb91dd3fa | ||
|
|
3ad537fdbb | ||
|
|
2c570bb4f6 | ||
|
|
461f3aec0a | ||
|
|
fd47122e39 | ||
|
|
8de013f192 | ||
|
|
1dd3a602d7 | ||
|
|
a84a49a13f | ||
|
|
64a6a69bbd | ||
|
|
d75b9f26f1 | ||
|
|
10cb7f830f | ||
|
|
dd3e6de9b2 | ||
|
|
d42b84abc1 | ||
|
|
b6c649041b | ||
|
|
93ec65be6a | ||
|
|
bfa8929c47 | ||
|
|
360713c803 | ||
|
|
26ce682824 | ||
|
|
65b546c0f9 | ||
|
|
31494bba22 | ||
|
|
f25abb0b26 | ||
|
|
e831bc75c6 | ||
|
|
852bbd148c | ||
|
|
c158fd369a | ||
|
|
a213e8bcd1 | ||
|
|
84f407278c | ||
|
|
c72c6d2408 | ||
|
|
0ad0092aa2 | ||
|
|
a84f43d959 | ||
|
|
b48da620b3 | ||
|
|
ed8884a581 | ||
|
|
05d27dda64 | ||
|
|
fe2c5e8611 | ||
|
|
422ee22d0c | ||
|
|
22f94cfdb6 | ||
|
|
5d754f40de | ||
|
|
c9b26c6352 | ||
|
|
6f0696631f | ||
|
|
fefb54de12 | ||
|
|
b4224066f7 | ||
|
|
3d9ef75488 | ||
|
|
79ecb0704e | ||
|
|
8154887824 | ||
|
|
39ae91a250 | ||
|
|
bc0478b2b2 | ||
|
|
f86996705b | ||
|
|
254aa664d7 | ||
|
|
c8371ee824 | ||
|
|
02e0250555 | ||
|
|
e1b025cab6 | ||
|
|
626854f1b7 | ||
|
|
98468d1c4d | ||
|
|
1062aeb136 | ||
|
|
aa93e5d8d2 |
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -8,8 +8,12 @@ assignees: ''
|
||||
---
|
||||
|
||||
#####################################################
|
||||
If you have a technical issue, please do not open a bug this way!
|
||||
Please use the `wails issue` command!
|
||||
**If you have a technical issue, please do not open a bug this way!**
|
||||
Please use the `wails issue` command!
|
||||
If you do not do this then the issue may be closed automatically.
|
||||
|
||||
NOTE: If your bug is related to Windows, make sure you read
|
||||
the [Windows Developer Guide](https://wails.app/guides/windows/)
|
||||
#####################################################
|
||||
|
||||
**Description**
|
||||
@@ -33,3 +37,5 @@ Please provide your platform, GO version and variables, etc
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
- [ ] This issue is for Windows and I have read the [Windows Developer Guide](https://wails.app/guides/windows/)
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -16,4 +16,14 @@ examples/**/example*
|
||||
cmd/wails/wails
|
||||
.DS_Store
|
||||
tmp
|
||||
node_modules/
|
||||
node_modules/
|
||||
package.json.md5
|
||||
v2/test/**/frontend/dist
|
||||
v2/test/**/build/
|
||||
v2/test/frameless/icon.png
|
||||
v2/test/hidden/icon.png
|
||||
v2/test/kitchensink/frontend/public/bundle.*
|
||||
v2/pkg/parser/testproject/frontend/wails
|
||||
v2/test/kitchensink/frontend/public
|
||||
v2/test/kitchensink/build/darwin/desktop/kitchensink
|
||||
v2/test/kitchensink/frontend/package.json.md5
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"go.formatTool": "goimports",
|
||||
"eslint.alwaysShowStatus": true
|
||||
"eslint.alwaysShowStatus": true,
|
||||
"files.associations": {
|
||||
"__locale": "c",
|
||||
"ios": "c"
|
||||
}
|
||||
}
|
||||
@@ -33,3 +33,11 @@ Wails is what it is because of the time and effort given by these great people.
|
||||
* [artem](https://github.com/Unix4ever)
|
||||
* [Tim Kipp](https://github.com/timkippdev)
|
||||
* [Dmitry Gomzyakov](https://github.com/kyoto44)
|
||||
* [Arthur Wiebe](https://github.com/artooro)
|
||||
* [Ilgıt Yıldırım](https://github.com/ilgityildirim)
|
||||
* [Altynbek](https://github.com/gelleson)
|
||||
* [Kyle](https://github.com/kmuchmore)
|
||||
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
|
||||
* [Charaf Rezrazi](https://github.com/Rezrazi)
|
||||
* [misitebao](https://github.com/misitebao)
|
||||
* [Elie Grenon](https://github.com/DrunkenPoney)
|
||||
@@ -12,7 +12,6 @@
|
||||
<a href="https://houndci.com"><img src="https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg"/></a>
|
||||
<a href="https://github.com/avelino/awesome-go" rel="nofollow"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"></a>
|
||||
<a href="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" rel="nofollow"><img src="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" alt="Release Pipelines"></a>
|
||||
<a href="https://github.com/wailsapp/wails/workflows/latest-pre/badge.svg?branch=masterr" rel="nofollow"><img src="https://github.com/wailsapp/wails/workflows/latest-pre/badge.svg?branch=master" alt="Pre-Release Pipelines"></a>
|
||||
</p>
|
||||
|
||||
The traditional method of providing web interfaces to Go programs is via a built-in web server. Wails offers a different approach: it provides the ability to wrap both Go code and a web frontend into a single binary. Tools are provided to make this easy for you by handling project creation, compilation and bundling. All you have to do is get creative!
|
||||
@@ -148,7 +147,12 @@ This project was mainly coded to the following albums:
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
|
||||
|
||||
## Special Thank You
|
||||
## Special Thanks
|
||||
|
||||
<p align="center" style="text-align: center">
|
||||
A *huge* thanks to <a href="https://pace.dev"><img src="pace.jpeg"/> Pace</a> for sponsoring the project and helping the efforts to get Wails ported to Apple Silicon!<br/><br/>
|
||||
If you are looking for a Project Management tool that's powerful but quick and easy to use, check them out!<br/><br/>
|
||||
</p>
|
||||
|
||||
<p align="center" style="text-align: center">
|
||||
A special thank you to JetBrains for donating licenses to us!<br/><br/>
|
||||
|
||||
6
app.go
6
app.go
@@ -2,7 +2,6 @@ package wails
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/syossan27/tebata"
|
||||
@@ -117,11 +116,6 @@ func (a *App) start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable console for Windows debug builds
|
||||
if runtime.GOOS == "windows" && BuildMode == cmd.BuildModeDebug {
|
||||
a.renderer.EnableConsole()
|
||||
}
|
||||
|
||||
// Start signal handler
|
||||
t := tebata.New(os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
|
||||
t.Reserve(func() {
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/leaanthony/spinner"
|
||||
)
|
||||
|
||||
const xgoVersion = "1.0.1"
|
||||
|
||||
var fs = NewFSHelper()
|
||||
|
||||
// ValidateFrontendConfig checks if the frontend config is valid
|
||||
@@ -90,16 +92,17 @@ func InitializeCrossCompilation(verbose bool) error {
|
||||
}
|
||||
|
||||
var packSpinner *spinner.Spinner
|
||||
msg := fmt.Sprintf("Pulling wailsapp/xgo:%s docker image... (may take a while)", xgoVersion)
|
||||
if !verbose {
|
||||
packSpinner = spinner.New("Pulling wailsapp/xgo:latest docker image... (may take a while)")
|
||||
packSpinner = spinner.New(msg)
|
||||
packSpinner.SetSpinSpeed(50)
|
||||
packSpinner.Start()
|
||||
} else {
|
||||
println("Pulling wailsapp/xgo:latest docker image... (may take a while)")
|
||||
println(msg)
|
||||
}
|
||||
|
||||
err := NewProgramHelper(verbose).RunCommandArray([]string{"docker",
|
||||
"pull", "wailsapp/xgo:latest"})
|
||||
"pull", fmt.Sprintf("wailsapp/xgo:%s", xgoVersion)})
|
||||
|
||||
if err != nil {
|
||||
if packSpinner != nil {
|
||||
@@ -114,7 +117,7 @@ func InitializeCrossCompilation(verbose bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildDocker builds the project using the cross compiling wailsapp/xgo:latest container
|
||||
// BuildDocker builds the project using the cross compiling wailsapp/xgo:<xgoVersion> container
|
||||
func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOptions) error {
|
||||
var packSpinner *spinner.Spinner
|
||||
if buildMode == BuildModeBridge {
|
||||
@@ -140,24 +143,31 @@ func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOpt
|
||||
"-v", fmt.Sprintf("%s:/build", filepath.Join(fs.Cwd(), "build")),
|
||||
"-v", fmt.Sprintf("%s:/source", fs.Cwd()),
|
||||
"-e", fmt.Sprintf("LOCAL_USER_ID=%v", userid),
|
||||
"-e", fmt.Sprintf("FLAG_TAGS=%s", projectOptions.Tags),
|
||||
"-e", fmt.Sprintf("FLAG_LDFLAGS=%s", ldFlags(projectOptions, buildMode)),
|
||||
"-e", "FLAG_V=false",
|
||||
"-e", "FLAG_X=false",
|
||||
"-e", "FLAG_RACE=false",
|
||||
"-e", "FLAG_BUILDMODE=default",
|
||||
"-e", "FLAG_TRIMPATH=false",
|
||||
"-e", fmt.Sprintf("TARGETS=%s", projectOptions.Platform+"/"+projectOptions.Architecture),
|
||||
"-e", fmt.Sprintf("TARGETS=%s/%s", projectOptions.Platform, projectOptions.Architecture),
|
||||
"-e", "GOPROXY=",
|
||||
"-e", "GO111MODULE=on",
|
||||
"wailsapp/xgo:latest",
|
||||
".",
|
||||
} {
|
||||
buildCommand.Add(arg)
|
||||
}
|
||||
|
||||
if projectOptions.GoPath != "" {
|
||||
buildCommand.Add("-v")
|
||||
buildCommand.Add(fmt.Sprintf("%s:/go", projectOptions.GoPath))
|
||||
}
|
||||
|
||||
buildCommand.Add(fmt.Sprintf("wailsapp/xgo:%s", xgoVersion))
|
||||
buildCommand.Add(".")
|
||||
|
||||
compileMessage := fmt.Sprintf(
|
||||
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:latest",
|
||||
projectOptions.Platform, projectOptions.Architecture)
|
||||
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:%s",
|
||||
projectOptions.Platform, projectOptions.Architecture, xgoVersion)
|
||||
|
||||
if buildMode == BuildModeDebug {
|
||||
compileMessage += " (Debug Mode)"
|
||||
@@ -216,10 +226,6 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
|
||||
buildCommand.Add("go")
|
||||
|
||||
buildCommand.Add("build")
|
||||
if buildMode == BuildModeBridge {
|
||||
// Ignore errors
|
||||
buildCommand.Add("-i")
|
||||
}
|
||||
|
||||
if binaryName != "" {
|
||||
// Alter binary name based on OS
|
||||
@@ -243,6 +249,10 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
|
||||
|
||||
buildCommand.AddSlice([]string{"-ldflags", ldFlags(projectOptions, buildMode)})
|
||||
|
||||
if projectOptions.Tags != "" {
|
||||
buildCommand.AddSlice([]string{"--tags", projectOptions.Tags})
|
||||
}
|
||||
|
||||
if projectOptions.Verbose {
|
||||
fmt.Printf("Command: %v\n", buildCommand.AsSlice())
|
||||
}
|
||||
@@ -530,6 +540,9 @@ func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error
|
||||
func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
if projectOptions.Platform == "windows" {
|
||||
logger.Yellow("*** Please note: Windows builds use mshtml which is only compatible with IE11. We strongly recommend only using IE11 when running 'wails serve'! For more information, please read https://wails.app/guides/windows/ ***")
|
||||
}
|
||||
logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<")
|
||||
}()
|
||||
location, err := filepath.Abs(filepath.Join("build", projectOptions.BinaryName))
|
||||
@@ -561,6 +574,10 @@ func ldFlags(po *ProjectOptions, buildMode string) string {
|
||||
ldflags += "-H windowsgui "
|
||||
}
|
||||
|
||||
if po.UseFirebug {
|
||||
ldflags += "-X github.com/wailsapp/wails/lib/renderer.UseFirebug=true "
|
||||
}
|
||||
|
||||
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
|
||||
|
||||
// Add additional ldflags passed in via the `ldflags` cli flag
|
||||
|
||||
@@ -65,6 +65,8 @@ const (
|
||||
Solus
|
||||
// Ctlos Linux distribution
|
||||
Ctlos
|
||||
// EndeavourOS linux distribution
|
||||
EndeavourOS
|
||||
)
|
||||
|
||||
// DistroInfo contains all the information relating to a linux distribution
|
||||
@@ -132,7 +134,7 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
case "archlabs":
|
||||
result.Distribution = ArchLabs
|
||||
case "ctlos":
|
||||
result.Distribution = Ctlos
|
||||
result.Distribution = Ctlos
|
||||
case "debian":
|
||||
result.Distribution = Debian
|
||||
case "ubuntu":
|
||||
@@ -171,6 +173,8 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
result.Distribution = PopOS
|
||||
case "solus":
|
||||
result.Distribution = Solus
|
||||
case "endeavouros":
|
||||
result.Distribution = EndeavourOS
|
||||
default:
|
||||
result.Distribution = Unknown
|
||||
}
|
||||
|
||||
@@ -202,7 +202,16 @@ distributions:
|
||||
name: Ctlos Linux
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
libraries: *archdefaultlibraries
|
||||
endeavouros:
|
||||
id: endeavouros
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: EndeavourOS
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
manjaro:
|
||||
id: manjaro
|
||||
releases:
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -150,6 +151,7 @@ type ProjectOptions struct {
|
||||
Template string `json:"-"`
|
||||
BinaryName string `json:"binaryname"`
|
||||
FrontEnd *frontend `json:"frontend,omitempty"`
|
||||
Tags string `json:"tags"`
|
||||
NPMProjectName string `json:"-"`
|
||||
system *SystemHelper
|
||||
log *Logger
|
||||
@@ -162,6 +164,25 @@ type ProjectOptions struct {
|
||||
Platform string
|
||||
Architecture string
|
||||
LdFlags string
|
||||
GoPath string
|
||||
UseFirebug bool
|
||||
|
||||
// Supported platforms
|
||||
Platforms []string `json:"platforms,omitempty"`
|
||||
}
|
||||
|
||||
// PlatformSupported returns true if the template is supported
|
||||
// on the current platform
|
||||
func (po *ProjectOptions) PlatformSupported() bool {
|
||||
|
||||
// Default is all platforms supported
|
||||
if len(po.Platforms) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check that the platform is in the list
|
||||
platformsSupported := slicer.String(po.Platforms)
|
||||
return platformsSupported.Contains(runtime.GOOS)
|
||||
}
|
||||
|
||||
// Defaults sets the default project template
|
||||
@@ -232,13 +253,16 @@ func (po *ProjectOptions) PromptForInputs() error {
|
||||
for _, k := range keys {
|
||||
templateDetail := templateDetails[k]
|
||||
templateList.Add(templateDetail)
|
||||
if !templateDetail.Metadata.PlatformSupported() {
|
||||
templateDetail.Metadata.Name = "* " + templateDetail.Metadata.Name
|
||||
}
|
||||
options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription))
|
||||
}
|
||||
|
||||
templateIndex := 0
|
||||
|
||||
if len(options.AsSlice()) > 1 {
|
||||
templateIndex = PromptSelection("Please select a template", options.AsSlice(), 0)
|
||||
templateIndex = PromptSelection("Please select a template (* means unsupported on current platform)", options.AsSlice(), 0)
|
||||
}
|
||||
|
||||
if len(templateList.AsSlice()) == 0 {
|
||||
@@ -249,6 +273,10 @@ func (po *ProjectOptions) PromptForInputs() error {
|
||||
po.selectedTemplate = templateList.AsSlice()[templateIndex].(*TemplateDetails)
|
||||
}
|
||||
|
||||
po.selectedTemplate.Metadata.Name = strings.TrimPrefix(po.selectedTemplate.Metadata.Name, "* ")
|
||||
if !po.selectedTemplate.Metadata.PlatformSupported() {
|
||||
println("WARNING: This template is unsupported on this platform!")
|
||||
}
|
||||
fmt.Println("Template: " + po.selectedTemplate.Metadata.Name)
|
||||
|
||||
// Setup NPM Project name
|
||||
@@ -371,5 +399,9 @@ func processTemplateMetadata(templateMetadata *TemplateMetadata, po *ProjectOpti
|
||||
}
|
||||
po.FrontEnd.Serve = templateMetadata.Serve
|
||||
}
|
||||
|
||||
// Save platforms
|
||||
po.Platforms = templateMetadata.Platforms
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,11 +24,19 @@ func NewSemanticVersion(version string) (*SemanticVersion, error) {
|
||||
|
||||
// IsRelease returns true if it's a release version
|
||||
func (s *SemanticVersion) IsRelease() bool {
|
||||
// Limit to v1
|
||||
if s.Version.Major() != 1 {
|
||||
return false
|
||||
}
|
||||
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
|
||||
}
|
||||
|
||||
// IsPreRelease returns true if it's a prerelease version
|
||||
func (s *SemanticVersion) IsPreRelease() bool {
|
||||
// Limit to v1
|
||||
if s.Version.Major() != 1 {
|
||||
return false
|
||||
}
|
||||
return len(s.Version.Prerelease()) > 0
|
||||
}
|
||||
|
||||
|
||||
65
cmd/semver_test.go
Normal file
65
cmd/semver_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSemanticVersion_IsPreRelease(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
want bool
|
||||
}{
|
||||
{"v1.6.7-pre0", "v1.6.7-pre0", true},
|
||||
{"v2.6.7+pre0", "v2.6.7+pre0", false},
|
||||
{"v2.6.7", "v2.6.7", false},
|
||||
{"v2.0.0+alpha.1", "v2.0.0+alpha.1", false},
|
||||
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
|
||||
{"v1.6.7", "v1.6.7", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
semanticversion, err := NewSemanticVersion(tt.version)
|
||||
if err != nil {
|
||||
t.Errorf("Invalid semantic version: %s", semanticversion)
|
||||
return
|
||||
}
|
||||
s := &SemanticVersion{
|
||||
Version: semanticversion.Version,
|
||||
}
|
||||
if got := s.IsPreRelease(); got != tt.want {
|
||||
t.Errorf("IsPreRelease() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSemanticVersion_IsRelease(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
want bool
|
||||
}{
|
||||
{"v1.6.7", "v1.6.7", true},
|
||||
{"v2.6.7-pre0", "v2.6.7-pre0", false},
|
||||
{"v2.6.7", "v2.6.7", false},
|
||||
{"v2.6.7+release", "v2.6.7+release", false},
|
||||
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
|
||||
{"v1.6.7-pre0", "v1.6.7-pre0", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
semanticversion, err := NewSemanticVersion(tt.version)
|
||||
if err != nil {
|
||||
t.Errorf("Invalid semantic version: %s", semanticversion)
|
||||
return
|
||||
}
|
||||
s := &SemanticVersion{
|
||||
Version: semanticversion.Version,
|
||||
}
|
||||
if got := s.IsRelease(); got != tt.want {
|
||||
t.Errorf("IsRelease() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
switch distroInfo.Distribution {
|
||||
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
|
||||
libraryChecker = DpkgInstalled
|
||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM:
|
||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS:
|
||||
libraryChecker = PacmanInstalled
|
||||
case CentOS, Fedora, Tumbleweed, Leap:
|
||||
libraryChecker = RpmInstalled
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
@@ -29,6 +30,26 @@ type TemplateMetadata struct {
|
||||
Bridge string `json:"bridge"`
|
||||
WailsDir string `json:"wailsdir"`
|
||||
TemplateDependencies []*TemplateDependency `json:"dependencies,omitempty"`
|
||||
|
||||
// List of platforms that this template is supported on.
|
||||
// No value means all platforms. A platform name is the same string
|
||||
// as `runtime.GOOS` will return, eg: "darwin". NOTE: This is
|
||||
// case sensitive.
|
||||
Platforms []string `json:"platforms,omitempty"`
|
||||
}
|
||||
|
||||
// PlatformSupported returns true if this template supports the
|
||||
// currently running platform
|
||||
func (m *TemplateMetadata) PlatformSupported() bool {
|
||||
|
||||
// Default is all platforms supported
|
||||
if len(m.Platforms) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check that the platform is in the list
|
||||
platformsSupported := slicer.String(m.Platforms)
|
||||
return platformsSupported.Contains(runtime.GOOS)
|
||||
}
|
||||
|
||||
// TemplateDependency defines a binary dependency for the template
|
||||
@@ -128,11 +149,11 @@ func (t *TemplateHelper) GetTemplateDetails() (map[string]*TemplateDetails, erro
|
||||
result[name] = &TemplateDetails{
|
||||
Path: dir,
|
||||
}
|
||||
_ = &TemplateMetadata{}
|
||||
metadata, err := t.LoadMetadata(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result[name].Metadata = metadata
|
||||
if metadata.Name != "" {
|
||||
result[name].Name = metadata.Name
|
||||
|
||||
@@ -5,7 +5,7 @@ const routes: Routes = [];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes)
|
||||
RouterModule.forRoot(routes,{useHash:true})
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
html,
|
||||
body {
|
||||
background-color: white;
|
||||
color: black;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: rgb(254,254,254);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
width:100%;
|
||||
@@ -17,10 +23,8 @@ body {
|
||||
|
||||
button {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#newCounter {
|
||||
color: white;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.result {
|
||||
|
||||
3
cmd/templates/vue3-full/frontend/.browserslistrc
Normal file
3
cmd/templates/vue3-full/frontend/.browserslistrc
Normal file
@@ -0,0 +1,3 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
29
cmd/templates/vue3-full/frontend/.eslintrc.js
Normal file
29
cmd/templates/vue3-full/frontend/.eslintrc.js
Normal file
@@ -0,0 +1,29 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/typescript/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'**/__tests__/*.{j,t}s?(x)',
|
||||
'**/tests/unit/**/*.spec.{j,t}s?(x)'
|
||||
],
|
||||
env: {
|
||||
mocha: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
21
cmd/templates/vue3-full/frontend/.gitignore
vendored
Normal file
21
cmd/templates/vue3-full/frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw*
|
||||
35
cmd/templates/vue3-full/frontend/README.md
Normal file
35
cmd/templates/vue3-full/frontend/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# vue basic
|
||||
|
||||
## Project setup
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run your tests
|
||||
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
37
cmd/templates/vue3-full/frontend/package.json.template
Normal file
37
cmd/templates/vue3-full/frontend/package.json.template
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "{{.NPMProjectName}}",
|
||||
"author": "{{.Author.Name}}<{{.Author.Email}}>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.0.0-0",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"@wailsapp/runtime": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.12",
|
||||
"@types/mocha": "^8.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.3.0",
|
||||
"@typescript-eslint/parser": "^4.3.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.9",
|
||||
"@vue/cli-plugin-router": "~4.5.9",
|
||||
"@vue/cli-plugin-typescript": "~4.5.9",
|
||||
"@vue/cli-plugin-unit-mocha": "~4.5.9",
|
||||
"@vue/cli-service": "~4.5.9",
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"@vue/eslint-config-typescript": "^7.0.0",
|
||||
"@vue/test-utils": "^2.0.0-0",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "<7.0.0",
|
||||
"eslint-plugin-vue": "^7.0.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass-loader": "^10.0.2",
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
32
cmd/templates/vue3-full/frontend/src/App.vue
Normal file
32
cmd/templates/vue3-full/frontend/src/App.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div id=app>
|
||||
<div id="nav">
|
||||
<router-link to="/">Home</router-link> |
|
||||
<router-link to="/about">About</router-link>
|
||||
</div>
|
||||
<router-view/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
#nav {
|
||||
padding: 30px;
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
|
||||
&.router-link-exact-active {
|
||||
color: #42b983;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
cmd/templates/vue3-full/frontend/src/assets/appicon.png
Normal file
BIN
cmd/templates/vue3-full/frontend/src/assets/appicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped lang="scss">
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
8
cmd/templates/vue3-full/frontend/src/main.ts
Normal file
8
cmd/templates/vue3-full/frontend/src/main.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import * as Wails from '@wailsapp/runtime';
|
||||
|
||||
Wails.Init(() => {
|
||||
createApp(App).use(router).mount('#app');
|
||||
});
|
||||
27
cmd/templates/vue3-full/frontend/src/router/index.ts
Normal file
27
cmd/templates/vue3-full/frontend/src/router/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { createRouter, createMemoryHistory, RouteRecordRaw } from 'vue-router'
|
||||
import Home from '../views/Home.vue'
|
||||
import About from '../views/About.vue'
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'About',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
// component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
||||
component: About
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createMemoryHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
5
cmd/templates/vue3-full/frontend/src/shims-vue.d.ts
vendored
Normal file
5
cmd/templates/vue3-full/frontend/src/shims-vue.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
declare module '*.vue' {
|
||||
import { defineComponent } from 'vue'
|
||||
const component: ReturnType<typeof defineComponent>
|
||||
export default component
|
||||
}
|
||||
5
cmd/templates/vue3-full/frontend/src/views/About.vue
Normal file
5
cmd/templates/vue3-full/frontend/src/views/About.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
40
cmd/templates/vue3-full/frontend/src/views/Home.vue
Normal file
40
cmd/templates/vue3-full/frontend/src/views/Home.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<img @click="getMessage" alt="Vue logo" src="../assets/appicon.png" :style="{ height: '400px' }"/>
|
||||
<HelloWorld :msg="message" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent } from "vue";
|
||||
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
|
||||
|
||||
interface Backend {
|
||||
basic(): Promise<string>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
backend: Backend;
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: "Home",
|
||||
components: {
|
||||
HelloWorld,
|
||||
},
|
||||
setup() {
|
||||
|
||||
const message = ref("Click the Icon");
|
||||
|
||||
const getMessage = () => {
|
||||
window.backend.basic().then(result => {
|
||||
message.value = result;
|
||||
});
|
||||
}
|
||||
|
||||
return { message: message, getMessage: getMessage };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
14
cmd/templates/vue3-full/frontend/tests/unit/example.spec.ts
Normal file
14
cmd/templates/vue3-full/frontend/tests/unit/example.spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import HelloWorld from '@/components/HelloWorld.vue';
|
||||
|
||||
describe('HelloWorld.vue', () => {
|
||||
it('renders props.msg when passed', () => {
|
||||
const msg = 'new message';
|
||||
const wrapper = shallowMount(HelloWorld, {
|
||||
props: { msg }
|
||||
});
|
||||
expect(wrapper.text()).to.include(msg);
|
||||
});
|
||||
});
|
||||
41
cmd/templates/vue3-full/frontend/tsconfig.json
Normal file
41
cmd/templates/vue3-full/frontend/tsconfig.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env",
|
||||
"mocha",
|
||||
"chai"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
42
cmd/templates/vue3-full/frontend/vue.config.js
Normal file
42
cmd/templates/vue3-full/frontend/vue.config.js
Normal file
@@ -0,0 +1,42 @@
|
||||
let cssConfig = {};
|
||||
|
||||
if (process.env.NODE_ENV == 'production') {
|
||||
cssConfig = {
|
||||
extract: {
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[name].css'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
chainWebpack: config => {
|
||||
let limit = 9999999999999999;
|
||||
config.module
|
||||
.rule('images')
|
||||
.test(/\.(png|gif|jpg)(\?.*)?$/i)
|
||||
.use('url-loader')
|
||||
.loader('url-loader')
|
||||
.tap(options => Object.assign(options, { limit: limit }));
|
||||
config.module
|
||||
.rule('fonts')
|
||||
.test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i)
|
||||
.use('url-loader')
|
||||
.loader('url-loader')
|
||||
.options({
|
||||
limit: limit
|
||||
});
|
||||
},
|
||||
css: cssConfig,
|
||||
configureWebpack: {
|
||||
output: {
|
||||
filename: '[name].js'
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: false
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
disableHostCheck: true
|
||||
}
|
||||
};
|
||||
5
cmd/templates/vue3-full/go.mod.template
Normal file
5
cmd/templates/vue3-full/go.mod.template
Normal file
@@ -0,0 +1,5 @@
|
||||
module {{.BinaryName}}
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails {{.WailsVersion}}
|
||||
)
|
||||
27
cmd/templates/vue3-full/main.go.template
Normal file
27
cmd/templates/vue3-full/main.go.template
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/mewn"
|
||||
"github.com/wailsapp/wails"
|
||||
)
|
||||
|
||||
func basic() string {
|
||||
return "Hello World!"
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
js := mewn.String("./frontend/dist/app.js")
|
||||
css := mewn.String("./frontend/dist/app.css")
|
||||
|
||||
app := wails.CreateApp(&wails.AppConfig{
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
Title: "{{.Name}}",
|
||||
JS: js,
|
||||
CSS: css,
|
||||
Colour: "#131313",
|
||||
})
|
||||
app.Bind(basic)
|
||||
app.Run()
|
||||
}
|
||||
15
cmd/templates/vue3-full/template.json
Executable file
15
cmd/templates/vue3-full/template.json
Executable file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "Vue3 Full",
|
||||
"version": "1.0.0",
|
||||
"shortdescription": "Vue 3, Vuex, Vue-router, and Webpack4",
|
||||
"description": "Vue3.0.0 Vuex, Vue-router, and Webpack 4",
|
||||
"install": "npm install",
|
||||
"build": "npm run build",
|
||||
"author": "Kyle Muchmore <kmuchmor@gmail.com>",
|
||||
"created": "2020-09-24 21:18:55.09417 +0000 UTC m=+90.125590001",
|
||||
"frontenddir": "frontend",
|
||||
"serve": "npm run serve",
|
||||
"bridge": "src",
|
||||
"wailsdir": "",
|
||||
"platforms": ["linux", "darwin"]
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
"core-js": "^3.6.4",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"vue": "^2.6.11",
|
||||
"vuetify": "^2.2.15",
|
||||
"vuetify": "^2.3.15",
|
||||
"@wailsapp/runtime": "^1.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
|
||||
<v-toolbar-title>Application</v-toolbar-title>
|
||||
</v-app-bar>
|
||||
<v-content>
|
||||
<v-main>
|
||||
<v-container fluid class="px-0">
|
||||
<v-layout justify-center align-center class="px-0">
|
||||
<hello-world></hello-world>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</v-content>
|
||||
</v-main>
|
||||
<v-footer app fixed>
|
||||
<span style="margin-left:1em">© You</span>
|
||||
</v-footer>
|
||||
@@ -57,4 +57,4 @@
|
||||
.logo {
|
||||
width: 16em;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
|
||||
// Version - Wails version
|
||||
const Version = "v1.7.2-pre9"
|
||||
const Version = "v1.11.0"
|
||||
|
||||
@@ -26,10 +26,13 @@ func init() {
|
||||
var packageApp = false
|
||||
var forceRebuild = false
|
||||
var debugMode = false
|
||||
var usefirebug = false
|
||||
var gopath = ""
|
||||
var typescriptFilename = ""
|
||||
var verbose = false
|
||||
var platform = ""
|
||||
var ldflags = ""
|
||||
var tags = ""
|
||||
|
||||
buildSpinner := spinner.NewSpinner()
|
||||
buildSpinner.SetSpinSpeed(50)
|
||||
@@ -40,9 +43,12 @@ func init() {
|
||||
BoolFlag("p", "Package application on successful build", &packageApp).
|
||||
BoolFlag("f", "Force rebuild of application components", &forceRebuild).
|
||||
BoolFlag("d", "Build in Debug mode", &debugMode).
|
||||
BoolFlag("firebug", "Enable firebug console for debug builds", &usefirebug).
|
||||
BoolFlag("verbose", "Verbose output", &verbose).
|
||||
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename).
|
||||
StringFlag("ldflags", "Extra options for -ldflags", &ldflags)
|
||||
StringFlag("ldflags", "Extra options for -ldflags", &ldflags).
|
||||
StringFlag("gopath", "Specify your GOPATH location. Mounted to /go during cross-compilation.", &gopath).
|
||||
StringFlag("tags", "Build tags to pass to the go compiler (quoted and space separated)", &tags)
|
||||
|
||||
var b strings.Builder
|
||||
for _, plat := range getSupportedPlatforms() {
|
||||
@@ -67,6 +73,7 @@ func init() {
|
||||
// Project options
|
||||
projectOptions := &cmd.ProjectOptions{}
|
||||
projectOptions.Verbose = verbose
|
||||
projectOptions.UseFirebug = usefirebug
|
||||
|
||||
// Check we are in project directory
|
||||
// Check project.json loads correctly
|
||||
@@ -76,6 +83,14 @@ func init() {
|
||||
return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory")
|
||||
}
|
||||
|
||||
// Set firebug flag
|
||||
projectOptions.UseFirebug = usefirebug
|
||||
|
||||
// Check that this platform is supported
|
||||
if !projectOptions.PlatformSupported() {
|
||||
logger.Yellow("WARNING: This project is unsupported on %s - it probably won't work!\n Valid platforms: %s\n", runtime.GOOS, strings.Join(projectOptions.Platforms, ", "))
|
||||
}
|
||||
|
||||
// Set cross-compile
|
||||
projectOptions.Platform = runtime.GOOS
|
||||
if len(platform) > 0 {
|
||||
@@ -97,6 +112,10 @@ func init() {
|
||||
|
||||
// Add ldflags
|
||||
projectOptions.LdFlags = ldflags
|
||||
projectOptions.GoPath = gopath
|
||||
|
||||
// Add tags
|
||||
projectOptions.Tags = tags
|
||||
|
||||
// Validate config
|
||||
// Check if we have a frontend
|
||||
@@ -181,6 +200,10 @@ func init() {
|
||||
return err
|
||||
}
|
||||
|
||||
if projectOptions.Platform == "windows" {
|
||||
logger.Yellow("*** Please note: Windows builds use mshtml which is only compatible with IE11. For more information, please read https://wails.app/guides/windows/ ***")
|
||||
}
|
||||
|
||||
logger.Yellow("Awesome! Project '%s' built!", projectOptions.Name)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -70,6 +70,7 @@ func init() {
|
||||
}
|
||||
|
||||
logger.Yellow("Awesome! Project '%s' built!", projectOptions.Name)
|
||||
|
||||
return cmd.ServeProject(projectOptions, logger)
|
||||
})
|
||||
}
|
||||
|
||||
70
config.go
70
config.go
@@ -1,20 +1,37 @@
|
||||
package wails
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/mewn"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/runtime"
|
||||
)
|
||||
|
||||
// AppConfig is the configuration structure used when creating a Wails App object
|
||||
type AppConfig struct {
|
||||
Width, Height int
|
||||
Title string
|
||||
defaultHTML string
|
||||
HTML string
|
||||
JS string
|
||||
CSS string
|
||||
Colour string
|
||||
Resizable bool
|
||||
// The width and height of your application in pixels
|
||||
Width, Height int
|
||||
|
||||
// The title to put in the title bar
|
||||
Title string
|
||||
|
||||
// The HTML your app should use. If you leave it blank, a default will be used:
|
||||
// <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="IE=edge" content="IE=edge"></head><body><div id="app"></div><script type="text/javascript"></script></body></html>
|
||||
HTML string
|
||||
|
||||
// The Javascript your app should use. Normally this should be generated by a bundler.
|
||||
JS string
|
||||
|
||||
// The CSS your app should use. Normally this should be generated by a bundler.
|
||||
CSS string
|
||||
|
||||
// The colour of your window. Can take "#fff", "rgb(255,255,255)", "rgba(255,255,255,1)" formats
|
||||
Colour string
|
||||
|
||||
// Indicates whether your app should be resizable
|
||||
Resizable bool
|
||||
|
||||
// Indicated if the devtools should be disabled
|
||||
DisableInspector bool
|
||||
}
|
||||
|
||||
@@ -33,9 +50,14 @@ func (a *AppConfig) GetTitle() string {
|
||||
return a.Title
|
||||
}
|
||||
|
||||
// GetDefaultHTML returns the default HTML
|
||||
func (a *AppConfig) GetDefaultHTML() string {
|
||||
return a.defaultHTML
|
||||
// GetHTML returns the default HTML
|
||||
func (a *AppConfig) GetHTML() string {
|
||||
if len(a.HTML) > 0 {
|
||||
a.HTML = url.QueryEscape(a.HTML)
|
||||
a.HTML = "data:text/html," + strings.ReplaceAll(a.HTML, "+", "%20")
|
||||
a.HTML = strings.ReplaceAll(a.HTML, "%3D", "=")
|
||||
}
|
||||
return a.HTML
|
||||
}
|
||||
|
||||
// GetResizable returns true if the window should be resizable
|
||||
@@ -75,10 +97,18 @@ func (a *AppConfig) merge(in *AppConfig) error {
|
||||
a.Colour = in.Colour
|
||||
}
|
||||
|
||||
if in.HTML != "" {
|
||||
a.HTML = in.HTML
|
||||
}
|
||||
|
||||
if in.JS != "" {
|
||||
a.JS = in.JS
|
||||
}
|
||||
|
||||
if in.HTML != "" {
|
||||
a.HTML = in.HTML
|
||||
}
|
||||
|
||||
if in.Width != 0 {
|
||||
a.Width = in.Width
|
||||
}
|
||||
@@ -99,7 +129,7 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) {
|
||||
Resizable: true,
|
||||
Title: "My Wails App",
|
||||
Colour: "#FFF", // White by default
|
||||
HTML: mewn.String("./runtime/assets/default.html"),
|
||||
HTML: defaultHTML,
|
||||
}
|
||||
|
||||
if userConfig != nil {
|
||||
@@ -111,3 +141,17 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var defaultHTML = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
|
||||
</html>`
|
||||
|
||||
2
go.mod
2
go.mod
@@ -22,7 +22,7 @@ require (
|
||||
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-20200323222414-85ca7c5b95cd
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
golang.org/x/text v0.3.0
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
|
||||
|
||||
2
go.sum
2
go.sum
@@ -81,6 +81,8 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-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 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=
|
||||
|
||||
@@ -6,9 +6,9 @@ type AppConfig interface {
|
||||
GetHeight() int
|
||||
GetTitle() string
|
||||
GetResizable() bool
|
||||
GetDefaultHTML() string
|
||||
GetHTML() string
|
||||
GetDisableInspector() bool
|
||||
GetColour() string
|
||||
GetCSS() string
|
||||
GetJS() string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
type Renderer interface {
|
||||
Initialise(AppConfig, IPCManager, EventManager) error
|
||||
Run() error
|
||||
EnableConsole()
|
||||
|
||||
// Binding
|
||||
NewBinding(bindingName string) error
|
||||
|
||||
@@ -56,10 +56,6 @@ func (h *Bridge) Initialise(appConfig interfaces.AppConfig, ipcManager interface
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableConsole not needed for bridge!
|
||||
func (h *Bridge) EnableConsole() {
|
||||
}
|
||||
|
||||
func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
|
||||
if err != nil {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,6 @@ package renderer
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/leaanthony/mewn"
|
||||
@@ -50,7 +49,7 @@ func (s *session) Identifier() string {
|
||||
|
||||
func (s *session) sendMessage(msg string) error {
|
||||
if !s.done {
|
||||
s.writeChan <- *(*[]byte)(unsafe.Pointer(&msg))
|
||||
s.writeChan <- []byte(msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -18,14 +18,17 @@ import (
|
||||
|
||||
// WebView defines the main webview application window
|
||||
// Default values in []
|
||||
|
||||
// UseFirebug indicates whether to inject the firebug console
|
||||
var UseFirebug = ""
|
||||
|
||||
type WebView struct {
|
||||
window wv.WebView // The webview object
|
||||
ipc interfaces.IPCManager
|
||||
log *logger.CustomLogger
|
||||
config interfaces.AppConfig
|
||||
eventManager interfaces.EventManager
|
||||
bindingCache []string
|
||||
enableConsole bool
|
||||
window wv.WebView // The webview object
|
||||
ipc interfaces.IPCManager
|
||||
log *logger.CustomLogger
|
||||
config interfaces.AppConfig
|
||||
eventManager interfaces.EventManager
|
||||
bindingCache []string
|
||||
}
|
||||
|
||||
// NewWebView returns a new WebView struct
|
||||
@@ -55,7 +58,7 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana
|
||||
Height: config.GetHeight(),
|
||||
Title: config.GetTitle(),
|
||||
Resizable: config.GetResizable(),
|
||||
URL: config.GetDefaultHTML(),
|
||||
URL: config.GetHTML(),
|
||||
Debug: !config.GetDisableInspector(),
|
||||
ExternalInvokeCallback: func(_ wv.WebView, message string) {
|
||||
w.ipc.Dispatch(message, w.callback)
|
||||
@@ -104,11 +107,6 @@ func (w *WebView) evalJS(js string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableConsole enables the console!
|
||||
func (w *WebView) EnableConsole() {
|
||||
w.enableConsole = true
|
||||
}
|
||||
|
||||
// Escape the Javascripts!
|
||||
func escapeJS(js string) (string, error) {
|
||||
result := strings.Replace(js, "\\", "\\\\", -1)
|
||||
@@ -179,10 +177,9 @@ func (w *WebView) Run() error {
|
||||
w.log.Info("Running...")
|
||||
|
||||
// Inject firebug in debug mode on Windows
|
||||
if w.enableConsole {
|
||||
w.log.Debug("Enabling Wails console")
|
||||
console := mewn.String("../../runtime/assets/console.js")
|
||||
w.evalJS(console)
|
||||
if UseFirebug != "" {
|
||||
w.log.Debug("Injecting Firebug")
|
||||
w.evalJS(`window.usefirebug=true;`)
|
||||
}
|
||||
|
||||
// Runtime assets
|
||||
|
||||
@@ -364,6 +364,7 @@ struct webview_priv
|
||||
webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview));
|
||||
webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
||||
webkit_settings_set_enable_developer_extras(settings, true);
|
||||
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1431,6 +1432,13 @@ struct webview_priv
|
||||
style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
|
||||
}
|
||||
|
||||
// Scale
|
||||
// Credit: https://github.com/webview/webview/issues/54#issuecomment-379528243
|
||||
HDC hDC = GetDC(NULL);
|
||||
w->width = GetDeviceCaps(hDC, 88)*w->width/96.0;
|
||||
w->height = GetDeviceCaps(hDC, 90)*w->height/96.0;
|
||||
ReleaseDC(NULL, hDC);
|
||||
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = w->width;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript">function AddScript(js, callbackID) {
|
||||
var script = document.createElement('script');
|
||||
script.text = js;
|
||||
document.body.appendChild(script);
|
||||
}</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -13,7 +13,7 @@ import * as Browser from './browser';
|
||||
import { On, OnMultiple, Emit, Notify, Heartbeat, Acknowledge } from './events';
|
||||
import { NewBinding } from './bindings';
|
||||
import { Callback } from './calls';
|
||||
import { AddScript, InjectCSS } from './utils';
|
||||
import { AddScript, InjectCSS, InjectFirebug } from './utils';
|
||||
import { AddIPCListener } from './ipc';
|
||||
import * as Store from './store';
|
||||
|
||||
@@ -60,6 +60,11 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
window.wails.Log.Error('error: ' + error);
|
||||
};
|
||||
|
||||
// Use firebug?
|
||||
if( window.usefirebug ) {
|
||||
InjectFirebug();
|
||||
}
|
||||
|
||||
// Emit loaded event
|
||||
Emit('wails:loaded');
|
||||
|
||||
|
||||
@@ -20,6 +20,18 @@ export function AddScript(js, callbackID) {
|
||||
}
|
||||
}
|
||||
|
||||
export function InjectFirebug() {
|
||||
// set the debug attribute on HTML
|
||||
var html = document.getElementsByTagName('html')[0];
|
||||
html.setAttribute('debug', 'true');
|
||||
var firebugURL = 'https://wails.app/assets/js/firebug-lite.js#startOpened=true,disableWhenFirebugActive=false';
|
||||
var script = document.createElement('script');
|
||||
script.src = firebugURL;
|
||||
script.type = 'application/javascript';
|
||||
document.head.appendChild(script);
|
||||
window.wails.Log.Info('Injected firebug');
|
||||
}
|
||||
|
||||
// Adapted from webview - thanks zserge!
|
||||
export function InjectCSS(css) {
|
||||
var elem = document.createElement('style');
|
||||
|
||||
6
runtime/js/package-lock.json
generated
6
runtime/js/package-lock.json
generated
@@ -3959,9 +3959,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
},
|
||||
"interpret": {
|
||||
|
||||
8
runtime/js/runtime/package-lock.json
generated
8
runtime/js/runtime/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wailsapp/runtime",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -130,9 +130,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
},
|
||||
"invert-kv": {
|
||||
|
||||
@@ -45,7 +45,7 @@ type Store struct {
|
||||
data reflect.Value
|
||||
dataType reflect.Type
|
||||
eventPrefix string
|
||||
callbacks []func(interface{})
|
||||
callbacks []reflect.Value
|
||||
runtime *Runtime
|
||||
notifySynchronously bool
|
||||
|
||||
@@ -143,10 +143,13 @@ func (s *Store) notify() {
|
||||
// Execute callbacks
|
||||
for _, callback := range s.callbacks {
|
||||
|
||||
// Build args
|
||||
args := []reflect.Value{s.data}
|
||||
|
||||
if s.notifySynchronously {
|
||||
callback(s.data)
|
||||
callback.Call(args)
|
||||
} else {
|
||||
go callback(s.data)
|
||||
go callback.Call(args)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -184,11 +187,50 @@ func (s *Store) Set(data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// callbackCheck ensures the given function to Subscribe() is
|
||||
// of the correct signature. Absolutely cannot wait for
|
||||
// generics to land rather than writing this nonsense.
|
||||
func (s *Store) callbackCheck(callback interface{}) error {
|
||||
|
||||
// Get type
|
||||
callbackType := reflect.TypeOf(callback)
|
||||
|
||||
// Check callback is a function
|
||||
if callbackType.Kind() != reflect.Func {
|
||||
return fmt.Errorf("invalid value given to store.Subscribe(). Expected 'func(%s)'", s.dataType.String())
|
||||
}
|
||||
|
||||
// Check input param
|
||||
if callbackType.NumIn() != 1 {
|
||||
return fmt.Errorf("invalid number of parameters given in callback function. Expected 1")
|
||||
}
|
||||
|
||||
// Check input data type
|
||||
if callbackType.In(0) != s.dataType {
|
||||
return fmt.Errorf("invalid type for input parameter given in callback function. Expected %s, got %s", s.dataType.String(), callbackType.In(0))
|
||||
}
|
||||
|
||||
// Check output param
|
||||
if callbackType.NumOut() != 0 {
|
||||
return fmt.Errorf("invalid number of return parameters given in callback function. Expected 0")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Subscribe will subscribe to updates to the store by
|
||||
// providing a callback. Any updates to the store are sent
|
||||
// to the callback
|
||||
func (s *Store) Subscribe(callback func(interface{})) {
|
||||
s.callbacks = append(s.callbacks, callback)
|
||||
func (s *Store) Subscribe(callback interface{}) {
|
||||
|
||||
err := s.callbackCheck(callback)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
callbackFunc := reflect.ValueOf(callback)
|
||||
|
||||
s.callbacks = append(s.callbacks, callbackFunc)
|
||||
}
|
||||
|
||||
// updaterCheck ensures the given function to Update() is
|
||||
@@ -244,5 +286,13 @@ func (s *Store) Update(updater interface{}) {
|
||||
results := reflect.ValueOf(updater).Call(args)
|
||||
|
||||
// We will only have 1 result. Set the store to it
|
||||
s.Set(results[0].Interface())
|
||||
err = s.Set(results[0].Interface())
|
||||
if err != nil && s.errorHandler != nil {
|
||||
s.errorHandler(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the data that's kept in the current state / Store
|
||||
func (s *Store) Get() interface{} {
|
||||
return s.data.Interface()
|
||||
}
|
||||
|
||||
13
v2/.vscode/settings.json
vendored
Normal file
13
v2/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"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
Normal file
40
v2/NOTES.md
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
# 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
|
||||
5
v2/README.md
Normal file
5
v2/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 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.
|
||||
119
v2/cmd/wails/internal/commands/build/build.go
Normal file
119
v2/cmd/wails/internal/commands/build/build.go
Normal file
@@ -0,0 +1,119 @@
|
||||
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("package", "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", "Suppress 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)
|
||||
|
||||
// Retain assets
|
||||
keepAssets := false
|
||||
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
||||
|
||||
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,
|
||||
KeepAssets: keepAssets,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
261
v2/cmd/wails/internal/commands/dev/dev.go
Normal file
261
v2/cmd/wails/internal/commands/dev/dev.go
Normal file
@@ -0,0 +1,261 @@
|
||||
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)
|
||||
|
||||
}
|
||||
154
v2/cmd/wails/internal/commands/doctor/doctor.go
Normal file
154
v2/cmd/wails/internal/commands/doctor/doctor.go
Normal file
@@ -0,0 +1,154 @@
|
||||
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
|
||||
}
|
||||
91
v2/cmd/wails/internal/commands/generate/generate.go
Normal file
91
v2/cmd/wails/internal/commands/generate/generate.go
Normal file
@@ -0,0 +1,91 @@
|
||||
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)
|
||||
}
|
||||
182
v2/cmd/wails/internal/commands/initialise/initialise.go
Normal file
182
v2/cmd/wails/internal/commands/initialise/initialise.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/internal/templates"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/git"
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
initGit := false
|
||||
gitInstalled := git.IsInstalled()
|
||||
if gitInstalled {
|
||||
// Git Init
|
||||
command.BoolFlag("g", "Initialise git repository", &initGit)
|
||||
}
|
||||
|
||||
// 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,
|
||||
InitGit: initGit,
|
||||
}
|
||||
|
||||
// Try to discover author details from git config
|
||||
err := findAuthorDetails(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if options.InitGit {
|
||||
err = initGit(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)
|
||||
if options.GenerateVSCode {
|
||||
options.Logger.Println("VSCode config files generated.")
|
||||
}
|
||||
if options.InitGit {
|
||||
options.Logger.Println("Git repository initialised.")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func initGit(options *templates.Options) error {
|
||||
err := git.InitRepo(options.TargetDir)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Unable to initialise git repository:")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findAuthorDetails(options *templates.Options) error {
|
||||
if git.IsInstalled() {
|
||||
name, err := git.Name()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.AuthorName = strings.TrimSpace(name)
|
||||
|
||||
email, err := git.Email()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.AuthorEmail = strings.TrimSpace(email)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
49
v2/cmd/wails/main.go
Normal file
49
v2/cmd/wails/main.go
Normal file
@@ -0,0 +1,49 @@
|
||||
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
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
3
v2/cmd/wails/version.go
Normal file
3
v2/cmd/wails/version.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v2.0.0-alpha.4"
|
||||
28
v2/go.mod
Normal file
28
v2/go.mod
Normal file
@@ -0,0 +1,28 @@
|
||||
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/jackmordaunt/icns v1.0.0
|
||||
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/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/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
|
||||
)
|
||||
134
v2/go.sum
Normal file
134
v2/go.sum
Normal file
@@ -0,0 +1,134 @@
|
||||
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/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ=
|
||||
github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo=
|
||||
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/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/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=
|
||||
12
v2/internal/app/debug.go
Normal file
12
v2/internal/app/debug.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build debug
|
||||
|
||||
package app
|
||||
|
||||
// Init initialises the application for a debug environment
|
||||
func (a *App) Init() error {
|
||||
// Indicate debug mode
|
||||
a.debug = true
|
||||
// Enable dev tools
|
||||
a.options.DevTools = true
|
||||
return nil
|
||||
}
|
||||
44
v2/internal/app/default.go
Normal file
44
v2/internal/app/default.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// +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 (
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"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
|
||||
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
||||
// CreateApp returns a null application
|
||||
func CreateApp(_ *options.App) (*App, error) {
|
||||
return &App{}, nil
|
||||
}
|
||||
|
||||
// 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(_ interface{}) {
|
||||
}
|
||||
233
v2/internal/app/desktop.go
Normal file
233
v2/internal/app/desktop.go
Normal file
@@ -0,0 +1,233 @@
|
||||
// +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/runtime"
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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
|
||||
menu *subsystem.Menu
|
||||
tray *subsystem.Tray
|
||||
contextmenus *subsystem.ContextMenus
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
|
||||
// Indicates if the app is in debug mode
|
||||
debug bool
|
||||
|
||||
// This is our binding DB
|
||||
bindings *binding.Bindings
|
||||
|
||||
// Application Stores
|
||||
loglevelStore *runtime.Store
|
||||
appconfigStore *runtime.Store
|
||||
}
|
||||
|
||||
// Create App
|
||||
func CreateApp(appoptions *options.App) (*App, error) {
|
||||
|
||||
// Merge default options
|
||||
options.MergeDefaults(appoptions)
|
||||
|
||||
// Set up logger
|
||||
myLogger := logger.New(appoptions.Logger)
|
||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
||||
|
||||
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger)
|
||||
|
||||
result := &App{
|
||||
window: window,
|
||||
servicebus: servicebus.New(myLogger),
|
||||
logger: myLogger,
|
||||
bindings: binding.NewBindings(myLogger),
|
||||
}
|
||||
|
||||
result.options = appoptions
|
||||
|
||||
// Initialise the app
|
||||
err := result.Init()
|
||||
|
||||
return result, err
|
||||
|
||||
}
|
||||
|
||||
// Run the application
|
||||
func (a *App) Run() error {
|
||||
|
||||
var err error
|
||||
|
||||
// Setup signal handler
|
||||
signalsubsystem, err := signal.NewManager(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.signal = signalsubsystem
|
||||
a.signal.Start()
|
||||
|
||||
// Start the service bus
|
||||
a.servicebus.Debug()
|
||||
err = a.servicebus.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the runtime
|
||||
applicationMenu := options.GetApplicationMenu(a.options)
|
||||
trayMenu := options.GetTray(a.options)
|
||||
contextMenus := options.GetContextMenus(a.options)
|
||||
|
||||
runtimesubsystem, err := subsystem.NewRuntime(a.servicebus, a.logger, applicationMenu, trayMenu, contextMenus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.runtime = runtimesubsystem
|
||||
err = a.runtime.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Application Stores
|
||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
||||
|
||||
// Start the binding subsystem
|
||||
bindingsubsystem, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.binding = bindingsubsystem
|
||||
err = a.binding.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the logging subsystem
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.log = log
|
||||
err = a.log.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the dispatcher
|
||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.dispatcher = dispatcher
|
||||
err = dispatcher.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start the eventing subsystem
|
||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.event = event
|
||||
err = a.event.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Optionally start the menu subsystem
|
||||
if applicationMenu != nil {
|
||||
menusubsystem, err := subsystem.NewMenu(applicationMenu, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.menu = menusubsystem
|
||||
err = a.menu.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally start the tray subsystem
|
||||
if trayMenu != nil {
|
||||
traysubsystem, err := subsystem.NewTray(trayMenu, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.tray = traysubsystem
|
||||
err = a.tray.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally start the context menu subsystem
|
||||
if contextMenus != nil {
|
||||
contextmenussubsystem, err := subsystem.NewContextMenus(contextMenus, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.contextmenus = contextmenussubsystem
|
||||
err = a.contextmenus.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
err = a.call.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Dump bindings as a debug
|
||||
bindingDump, err := a.bindings.ToJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := a.window.Run(dispatcher, bindingDump, a.debug)
|
||||
a.logger.Trace("Ffenestri.Run() exited")
|
||||
err = a.servicebus.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
205
v2/internal/app/hybrid.go
Normal file
205
v2/internal/app/hybrid.go
Normal file
@@ -0,0 +1,205 @@
|
||||
// +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,
|
||||
DevTools: options.DevTools,
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
11
v2/internal/app/production.go
Normal file
11
v2/internal/app/production.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build !debug
|
||||
|
||||
package app
|
||||
|
||||
import "github.com/wailsapp/wails/v2/pkg/logger"
|
||||
|
||||
// Init initialises the application for a production environment
|
||||
func (a *App) Init() error {
|
||||
a.logger.SetLogLevel(logger.ERROR)
|
||||
return nil
|
||||
}
|
||||
160
v2/internal/app/server.go
Normal file
160
v2/internal/app/server.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// +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())
|
||||
}
|
||||
}
|
||||
112
v2/internal/assetdb/assetdb.go
Normal file
112
v2/internal/assetdb/assetdb.go
Normal file
@@ -0,0 +1,112 @@
|
||||
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()
|
||||
}
|
||||
70
v2/internal/assetdb/assetdb_test.go
Normal file
70
v2/internal/assetdb/assetdb_test.go
Normal file
@@ -0,0 +1,70 @@
|
||||
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, "")
|
||||
}
|
||||
176
v2/internal/assetdb/filesystem.go
Normal file
176
v2/internal/assetdb/filesystem.go
Normal file
@@ -0,0 +1,176 @@
|
||||
// +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
|
||||
}
|
||||
108
v2/internal/assetdb/filesystem_test.go
Normal file
108
v2/internal/assetdb/filesystem_test.go
Normal file
@@ -0,0 +1,108 @@
|
||||
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)
|
||||
}
|
||||
9
v2/internal/bind/bind.go
Normal file
9
v2/internal/bind/bind.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package bind
|
||||
|
||||
func IsStructPointer(value interface{}) bool {
|
||||
switch t := value.(type) {
|
||||
default:
|
||||
println(t)
|
||||
}
|
||||
return false
|
||||
}
|
||||
70
v2/internal/binding/binding.go
Executable file
70
v2/internal/binding/binding.go
Executable file
@@ -0,0 +1,70 @@
|
||||
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("cannot 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()
|
||||
}
|
||||
150
v2/internal/binding/boundMethod.go
Normal file
150
v2/internal/binding/boundMethod.go
Normal file
@@ -0,0 +1,150 @@
|
||||
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
|
||||
}
|
||||
150
v2/internal/binding/db.go
Normal file
150
v2/internal/binding/db.go
Normal file
@@ -0,0 +1,150 @@
|
||||
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
|
||||
}
|
||||
28
v2/internal/binding/parameter.go
Normal file
28
v2/internal/binding/parameter.go
Normal file
@@ -0,0 +1,28 @@
|
||||
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")
|
||||
}
|
||||
97
v2/internal/binding/reflect.go
Executable file
97
v2/internal/binding/reflect.go
Executable file
@@ -0,0 +1,97 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// isFunction returns true if the given value is a function
|
||||
func isFunction(value interface{}) bool {
|
||||
return reflect.ValueOf(value).Kind() == reflect.Func
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
if isFunction(value) {
|
||||
name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name()
|
||||
return nil, fmt.Errorf("%s is a function, not a pointer to a struct. Wails v2 has deprecated the binding of functions. Please wrap your functions up in a struct and bind a pointer to that 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
|
||||
}
|
||||
17
v2/internal/crypto/crypto.go
Normal file
17
v2/internal/crypto/crypto.go
Normal file
@@ -0,0 +1,17 @@
|
||||
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)
|
||||
}
|
||||
13
v2/internal/ffenestri/LICENCES.md
Normal file
13
v2/internal/ffenestri/LICENCES.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 3rd Party Licenses
|
||||
|
||||
## vec
|
||||
Homepage: https://github.com/rxi/vec
|
||||
License: https://github.com/rxi/vec/blob/master/LICENSE
|
||||
|
||||
## json
|
||||
Homepage: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD
|
||||
License: http://git.ozlabs.org/?p=ccan;a=blob;f=licenses/BSD-MIT;h=89de354795ec7a7cdab07c091029653d3618540d;hb=HEAD
|
||||
|
||||
## hashmap
|
||||
Homepage: https://github.com/sheredom/hashmap.h
|
||||
License: https://github.com/sheredom/hashmap.h/blob/master/LICENSE
|
||||
41
v2/internal/ffenestri/defaultdialogicons_darwin.c
Normal file
41
v2/internal/ffenestri/defaultdialogicons_darwin.c
Normal file
File diff suppressed because one or more lines are too long
177
v2/internal/ffenestri/ffenestri.go
Normal file
177
v2/internal/ffenestri/ffenestri.go
Normal file
@@ -0,0 +1,177 @@
|
||||
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"
|
||||
|
||||
// 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, debug bool) 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))
|
||||
}
|
||||
42
v2/internal/ffenestri/ffenestri.h
Normal file
42
v2/internal/ffenestri/ffenestri.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#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 resolvesAliases, 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 MessageDialog(void *appPointer, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton);
|
||||
extern void DarkModeEnabled(void *appPointer, char *callbackID);
|
||||
extern void UpdateMenu(void *app, char *menuAsJSON);
|
||||
extern void UpdateTray(void *app, char *menuAsJSON);
|
||||
extern void UpdateContextMenus(void *app, char *contextMenusAsJSON);
|
||||
extern void UpdateTrayLabel(void *app, const char *label);
|
||||
extern void UpdateTrayIcon(void *app, const char *label);
|
||||
|
||||
#endif
|
||||
242
v2/internal/ffenestri/ffenestri_client.go
Normal file
242
v2/internal/ffenestri/ffenestri_client.go
Normal file
@@ -0,0 +1,242 @@
|
||||
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 (
|
||||
"encoding/json"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"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.ResolvesAliases),
|
||||
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),
|
||||
)
|
||||
}
|
||||
|
||||
// MessageDialog will open a message dialog with the given options
|
||||
func (c *Client) MessageDialog(dialogOptions *options.MessageDialog, callbackID string) {
|
||||
|
||||
// Sanity check button length
|
||||
if len(dialogOptions.Buttons) > 4 {
|
||||
c.app.logger.Error("Given %d message dialog buttons. Maximum is 4", len(dialogOptions.Buttons))
|
||||
return
|
||||
}
|
||||
|
||||
// Process buttons
|
||||
buttons := []string{"", "", "", ""}
|
||||
for i, button := range dialogOptions.Buttons {
|
||||
buttons[i] = button
|
||||
}
|
||||
|
||||
C.MessageDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(string(dialogOptions.Type)),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(dialogOptions.Message),
|
||||
c.app.string2CString(dialogOptions.Icon),
|
||||
c.app.string2CString(buttons[0]),
|
||||
c.app.string2CString(buttons[1]),
|
||||
c.app.string2CString(buttons[2]),
|
||||
c.app.string2CString(buttons[3]),
|
||||
c.app.string2CString(dialogOptions.DefaultButton),
|
||||
c.app.string2CString(dialogOptions.CancelButton))
|
||||
}
|
||||
|
||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
||||
}
|
||||
|
||||
func (c *Client) UpdateMenu(menu *menu.Menu) {
|
||||
|
||||
// Guard against nil menus
|
||||
if menu == nil {
|
||||
return
|
||||
}
|
||||
// Process the menu
|
||||
processedMenu := NewProcessedMenu(menu)
|
||||
menuJSON, err := json.Marshal(processedMenu)
|
||||
if err != nil {
|
||||
c.app.logger.Error("Error processing updated Menu: %s", err.Error())
|
||||
return
|
||||
}
|
||||
C.UpdateMenu(c.app.app, c.app.string2CString(string(menuJSON)))
|
||||
}
|
||||
|
||||
func (c *Client) UpdateTray(menu *menu.Menu) {
|
||||
|
||||
// Guard against nil menus
|
||||
if menu == nil {
|
||||
return
|
||||
}
|
||||
// Process the menu
|
||||
processedMenu := NewProcessedMenu(menu)
|
||||
trayMenuJSON, err := json.Marshal(processedMenu)
|
||||
if err != nil {
|
||||
c.app.logger.Error("Error processing updated Tray: %s", err.Error())
|
||||
return
|
||||
}
|
||||
C.UpdateTray(c.app.app, c.app.string2CString(string(trayMenuJSON)))
|
||||
}
|
||||
|
||||
func (c *Client) UpdateTrayLabel(label string) {
|
||||
C.UpdateTrayLabel(c.app.app, c.app.string2CString(label))
|
||||
}
|
||||
|
||||
func (c *Client) UpdateTrayIcon(name string) {
|
||||
C.UpdateTrayIcon(c.app.app, c.app.string2CString(name))
|
||||
}
|
||||
|
||||
func (c *Client) UpdateContextMenus(contextMenus *menu.ContextMenus) {
|
||||
|
||||
// Guard against nil contextMenus
|
||||
if contextMenus == nil {
|
||||
return
|
||||
}
|
||||
// Process the menu
|
||||
contextMenusJSON, err := json.Marshal(contextMenus)
|
||||
if err != nil {
|
||||
c.app.logger.Error("Error processing updated Context Menus: %s", err.Error())
|
||||
return
|
||||
}
|
||||
C.UpdateContextMenus(c.app.app, c.app.string2CString(string(contextMenusJSON)))
|
||||
}
|
||||
2816
v2/internal/ffenestri/ffenestri_darwin.c
Normal file
2816
v2/internal/ffenestri/ffenestri_darwin.c
Normal file
File diff suppressed because it is too large
Load Diff
117
v2/internal/ffenestri/ffenestri_darwin.go
Normal file
117
v2/internal/ffenestri/ffenestri_darwin.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||
|
||||
#include "ffenestri_darwin.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
func (a *Application) processPlatformSettings() error {
|
||||
|
||||
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.WindowBackgroundIsTranslucent(a.app)
|
||||
}
|
||||
|
||||
// Process menu
|
||||
applicationMenu := options.GetApplicationMenu(a.config)
|
||||
if applicationMenu != nil {
|
||||
|
||||
/*
|
||||
As radio groups need to be manually managed on OSX,
|
||||
we preprocess the menu to determine the radio groups.
|
||||
This is defined as any adjacent menu item of type "RadioType".
|
||||
We keep a record of every radio group member we discover by saving
|
||||
a list of all members of the group and the number of members
|
||||
in the group (this last one is for optimisation at the C layer).
|
||||
*/
|
||||
processedMenu := NewProcessedMenu(applicationMenu)
|
||||
applicationMenuJSON, err := json.Marshal(processedMenu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
C.SetMenu(a.app, a.string2CString(string(applicationMenuJSON)))
|
||||
}
|
||||
|
||||
// Process tray
|
||||
tray := options.GetTray(a.config)
|
||||
if tray != nil {
|
||||
|
||||
/*
|
||||
As radio groups need to be manually managed on OSX,
|
||||
we preprocess the menu to determine the radio groups.
|
||||
This is defined as any adjacent menu item of type "RadioType".
|
||||
We keep a record of every radio group member we discover by saving
|
||||
a list of all members of the group and the number of members
|
||||
in the group (this last one is for optimisation at the C layer).
|
||||
*/
|
||||
processedMenu := NewProcessedMenu(tray.Menu)
|
||||
trayMenuJSON, err := json.Marshal(processedMenu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
C.SetTray(a.app, a.string2CString(string(trayMenuJSON)), a.string2CString(tray.Label), a.string2CString(tray.Icon))
|
||||
}
|
||||
|
||||
// Process context menus
|
||||
contextMenus := options.GetContextMenus(a.config)
|
||||
if contextMenus != nil {
|
||||
contextMenusJSON, err := json.Marshal(contextMenus)
|
||||
fmt.Printf("\n\nCONTEXT MENUS:\n %+v\n\n", string(contextMenusJSON))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
C.SetContextMenus(a.app, a.string2CString(string(contextMenusJSON)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
19
v2/internal/ffenestri/ffenestri_darwin.h
Normal file
19
v2/internal/ffenestri/ffenestri_darwin.h
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
#ifndef FFENESTRI_DARWIN_H
|
||||
#define FFENESTRI_DARWIN_H
|
||||
|
||||
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 WindowBackgroundIsTranslucent(void *);
|
||||
extern void SetMenu(void *, const char *);
|
||||
extern void SetTray(void *, const char *, const char *, const char *);
|
||||
extern void SetContextMenus(void *, const char *);
|
||||
|
||||
#endif
|
||||
984
v2/internal/ffenestri/ffenestri_linux.c
Normal file
984
v2/internal/ffenestri/ffenestri_linux.c
Normal file
@@ -0,0 +1,984 @@
|
||||
|
||||
#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
|
||||
518
v2/internal/ffenestri/hashmap.h
Normal file
518
v2/internal/ffenestri/hashmap.h
Normal file
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
The latest version of this library is available on GitHub;
|
||||
https://github.com/sheredom/hashmap.h
|
||||
*/
|
||||
|
||||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
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 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.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
#ifndef SHEREDOM_HASHMAP_H_INCLUDED
|
||||
#define SHEREDOM_HASHMAP_H_INCLUDED
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Workaround a bug in the MSVC runtime where it uses __cplusplus when not
|
||||
// defined.
|
||||
#pragma warning(push, 0)
|
||||
#pragma warning(disable : 4668)
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if (defined(_MSC_VER) && defined(__AVX__)) || \
|
||||
(!defined(_MSC_VER) && defined(__SSE4_2__))
|
||||
#define HASHMAP_SSE42
|
||||
#endif
|
||||
|
||||
#if defined(HASHMAP_SSE42)
|
||||
#include <nmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
// Stop MSVC complaining about not inlining functions.
|
||||
#pragma warning(disable : 4710)
|
||||
// Stop MSVC complaining about inlining functions!
|
||||
#pragma warning(disable : 4711)
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define HASHMAP_USED
|
||||
#elif defined(__GNUC__)
|
||||
#define HASHMAP_USED __attribute__((used))
|
||||
#else
|
||||
#define HASHMAP_USED
|
||||
#endif
|
||||
|
||||
/* We need to keep keys and values. */
|
||||
struct hashmap_element_s {
|
||||
const char *key;
|
||||
unsigned key_len;
|
||||
int in_use;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/* A hashmap has some maximum size and current size, as well as the data to
|
||||
* hold. */
|
||||
struct hashmap_s {
|
||||
unsigned table_size;
|
||||
unsigned size;
|
||||
struct hashmap_element_s *data;
|
||||
};
|
||||
|
||||
#define HASHMAP_MAX_CHAIN_LENGTH (8)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Create a hashmap.
|
||||
/// @param initial_size The initial size of the hashmap. Must be a power of two.
|
||||
/// @param out_hashmap The storage for the created hashmap.
|
||||
/// @return On success 0 is returned.
|
||||
///
|
||||
/// Note that the initial size of the hashmap must be a power of two, and
|
||||
/// creation of the hashmap will fail if this is not the case.
|
||||
static int hashmap_create(const unsigned initial_size,
|
||||
struct hashmap_s *const out_hashmap) HASHMAP_USED;
|
||||
|
||||
/// @brief Put an element into the hashmap.
|
||||
/// @param hashmap The hashmap to insert into.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @param value The value to insert.
|
||||
/// @return On success 0 is returned.
|
||||
///
|
||||
/// The key string slice is not copied when creating the hashmap entry, and thus
|
||||
/// must remain a valid pointer until the hashmap entry is removed or the
|
||||
/// hashmap is destroyed.
|
||||
static int hashmap_put(struct hashmap_s *const hashmap, const char *const key,
|
||||
const unsigned len, void *const value) HASHMAP_USED;
|
||||
|
||||
/// @brief Get an element from the hashmap.
|
||||
/// @param hashmap The hashmap to get from.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @return The previously set element, or NULL if none exists.
|
||||
static void *hashmap_get(const struct hashmap_s *const hashmap,
|
||||
const char *const key,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
|
||||
/// @brief Remove an element from the hashmap.
|
||||
/// @param hashmap The hashmap to remove from.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @return On success 0 is returned.
|
||||
static int hashmap_remove(struct hashmap_s *const hashmap,
|
||||
const char *const key,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
|
||||
/// @brief Iterate over all the elements in a hashmap.
|
||||
/// @param hashmap The hashmap to iterate over.
|
||||
/// @param f The function pointer to call on each element.
|
||||
/// @param context The context to pass as the first argument to f.
|
||||
/// @return If the entire hashmap was iterated then 0 is returned. Otherwise if
|
||||
/// the callback function f returned non-zero then non-zero is returned.
|
||||
static int hashmap_iterate(const struct hashmap_s *const hashmap,
|
||||
int (*f)(void *const context, void *const value),
|
||||
void *const context) HASHMAP_USED;
|
||||
|
||||
/// @brief Iterate over all the elements in a hashmap.
|
||||
/// @param hashmap The hashmap to iterate over.
|
||||
/// @param f The function pointer to call on each element.
|
||||
/// @param context The context to pass as the first argument to f.
|
||||
/// @return If the entire hashmap was iterated then 0 is returned.
|
||||
/// Otherwise if the callback function f returned positive then the positive
|
||||
/// value is returned. If the callback function returns -1, the current item
|
||||
/// is removed and iteration continues.
|
||||
static int hashmap_iterate_pairs(struct hashmap_s *const hashmap,
|
||||
int (*f)(void *const, struct hashmap_element_s *const),
|
||||
void *const context) HASHMAP_USED;
|
||||
|
||||
/// @brief Get the size of the hashmap.
|
||||
/// @param hashmap The hashmap to get the size of.
|
||||
/// @return The size of the hashmap.
|
||||
static unsigned
|
||||
hashmap_num_entries(const struct hashmap_s *const hashmap) HASHMAP_USED;
|
||||
|
||||
/// @brief Destroy the hashmap.
|
||||
/// @param hashmap The hashmap to destroy.
|
||||
static void hashmap_destroy(struct hashmap_s *const hashmap) HASHMAP_USED;
|
||||
|
||||
static unsigned hashmap_crc32_helper(const char *const s,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
static unsigned
|
||||
hashmap_hash_helper_int_helper(const struct hashmap_s *const m,
|
||||
const char *const keystring,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
static int hashmap_match_helper(const struct hashmap_element_s *const element,
|
||||
const char *const key,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
static int hashmap_hash_helper(const struct hashmap_s *const m,
|
||||
const char *const key, const unsigned len,
|
||||
unsigned *const out_index) HASHMAP_USED;
|
||||
static int hashmap_rehash_iterator(void *const new_hash,
|
||||
struct hashmap_element_s *const e) HASHMAP_USED;
|
||||
static int hashmap_rehash_helper(struct hashmap_s *const m) HASHMAP_USED;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define HASHMAP_CAST(type, x) static_cast<type>(x)
|
||||
#define HASHMAP_PTR_CAST(type, x) reinterpret_cast<type>(x)
|
||||
#define HASHMAP_NULL NULL
|
||||
#else
|
||||
#define HASHMAP_CAST(type, x) ((type)x)
|
||||
#define HASHMAP_PTR_CAST(type, x) ((type)x)
|
||||
#define HASHMAP_NULL 0
|
||||
#endif
|
||||
|
||||
int hashmap_create(const unsigned initial_size,
|
||||
struct hashmap_s *const out_hashmap) {
|
||||
if (0 == initial_size || 0 != (initial_size & (initial_size - 1))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
out_hashmap->data =
|
||||
HASHMAP_CAST(struct hashmap_element_s *,
|
||||
calloc(initial_size, sizeof(struct hashmap_element_s)));
|
||||
if (!out_hashmap->data) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
out_hashmap->table_size = initial_size;
|
||||
out_hashmap->size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_put(struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len, void *const value) {
|
||||
unsigned int index;
|
||||
|
||||
/* Find a place to put our value. */
|
||||
while (!hashmap_hash_helper(m, key, len, &index)) {
|
||||
if (hashmap_rehash_helper(m)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the data. */
|
||||
m->data[index].data = value;
|
||||
m->data[index].key = key;
|
||||
m->data[index].key_len = len;
|
||||
m->data[index].in_use = 1;
|
||||
m->size++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *hashmap_get(const struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len) {
|
||||
unsigned int curr;
|
||||
unsigned int i;
|
||||
|
||||
/* Find data location */
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) {
|
||||
if (m->data[curr].in_use) {
|
||||
if (hashmap_match_helper(&m->data[curr], key, len)) {
|
||||
return m->data[curr].data;
|
||||
}
|
||||
}
|
||||
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return HASHMAP_NULL;
|
||||
}
|
||||
|
||||
int hashmap_remove(struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len) {
|
||||
unsigned int i;
|
||||
unsigned int curr;
|
||||
|
||||
/* Find key */
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) {
|
||||
if (m->data[curr].in_use) {
|
||||
if (hashmap_match_helper(&m->data[curr], key, len)) {
|
||||
/* Blank out the fields including in_use */
|
||||
memset(&m->data[curr], 0, sizeof(struct hashmap_element_s));
|
||||
|
||||
/* Reduce the size */
|
||||
m->size--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hashmap_iterate(const struct hashmap_s *const m,
|
||||
int (*f)(void *const, void *const), void *const context) {
|
||||
unsigned int i;
|
||||
|
||||
/* Linear probing */
|
||||
for (i = 0; i < m->table_size; i++) {
|
||||
if (m->data[i].in_use) {
|
||||
if (!f(context, m->data[i].data)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_iterate_pairs(struct hashmap_s *const hashmap,
|
||||
int (*f)(void *const, struct hashmap_element_s *const),
|
||||
void *const context) {
|
||||
unsigned int i;
|
||||
struct hashmap_element_s *p;
|
||||
int r;
|
||||
|
||||
/* Linear probing */
|
||||
for (i = 0; i < hashmap->table_size; i++) {
|
||||
p=&hashmap->data[i];
|
||||
if (p->in_use) {
|
||||
r=f(context, p);
|
||||
switch (r)
|
||||
{
|
||||
case -1: /* remove item */
|
||||
memset(p, 0, sizeof(struct hashmap_element_s));
|
||||
hashmap->size--;
|
||||
break;
|
||||
case 0: /* continue iterating */
|
||||
break;
|
||||
default: /* early exit */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hashmap_destroy(struct hashmap_s *const m) {
|
||||
free(m->data);
|
||||
memset(m, 0, sizeof(struct hashmap_s));
|
||||
}
|
||||
|
||||
unsigned hashmap_num_entries(const struct hashmap_s *const m) {
|
||||
return m->size;
|
||||
}
|
||||
|
||||
unsigned hashmap_crc32_helper(const char *const s, const unsigned len) {
|
||||
unsigned i;
|
||||
unsigned crc32val = 0;
|
||||
|
||||
#if defined(HASHMAP_SSE42)
|
||||
for (i = 0; i < len; i++) {
|
||||
crc32val = _mm_crc32_u8(crc32val, HASHMAP_CAST(unsigned char, s[i]));
|
||||
}
|
||||
|
||||
return crc32val;
|
||||
#else
|
||||
// Using polynomial 0x11EDC6F41 to match SSE 4.2's crc function.
|
||||
static const unsigned crc32_tab[] = {
|
||||
0x00000000U, 0xF26B8303U, 0xE13B70F7U, 0x1350F3F4U, 0xC79A971FU,
|
||||
0x35F1141CU, 0x26A1E7E8U, 0xD4CA64EBU, 0x8AD958CFU, 0x78B2DBCCU,
|
||||
0x6BE22838U, 0x9989AB3BU, 0x4D43CFD0U, 0xBF284CD3U, 0xAC78BF27U,
|
||||
0x5E133C24U, 0x105EC76FU, 0xE235446CU, 0xF165B798U, 0x030E349BU,
|
||||
0xD7C45070U, 0x25AFD373U, 0x36FF2087U, 0xC494A384U, 0x9A879FA0U,
|
||||
0x68EC1CA3U, 0x7BBCEF57U, 0x89D76C54U, 0x5D1D08BFU, 0xAF768BBCU,
|
||||
0xBC267848U, 0x4E4DFB4BU, 0x20BD8EDEU, 0xD2D60DDDU, 0xC186FE29U,
|
||||
0x33ED7D2AU, 0xE72719C1U, 0x154C9AC2U, 0x061C6936U, 0xF477EA35U,
|
||||
0xAA64D611U, 0x580F5512U, 0x4B5FA6E6U, 0xB93425E5U, 0x6DFE410EU,
|
||||
0x9F95C20DU, 0x8CC531F9U, 0x7EAEB2FAU, 0x30E349B1U, 0xC288CAB2U,
|
||||
0xD1D83946U, 0x23B3BA45U, 0xF779DEAEU, 0x05125DADU, 0x1642AE59U,
|
||||
0xE4292D5AU, 0xBA3A117EU, 0x4851927DU, 0x5B016189U, 0xA96AE28AU,
|
||||
0x7DA08661U, 0x8FCB0562U, 0x9C9BF696U, 0x6EF07595U, 0x417B1DBCU,
|
||||
0xB3109EBFU, 0xA0406D4BU, 0x522BEE48U, 0x86E18AA3U, 0x748A09A0U,
|
||||
0x67DAFA54U, 0x95B17957U, 0xCBA24573U, 0x39C9C670U, 0x2A993584U,
|
||||
0xD8F2B687U, 0x0C38D26CU, 0xFE53516FU, 0xED03A29BU, 0x1F682198U,
|
||||
0x5125DAD3U, 0xA34E59D0U, 0xB01EAA24U, 0x42752927U, 0x96BF4DCCU,
|
||||
0x64D4CECFU, 0x77843D3BU, 0x85EFBE38U, 0xDBFC821CU, 0x2997011FU,
|
||||
0x3AC7F2EBU, 0xC8AC71E8U, 0x1C661503U, 0xEE0D9600U, 0xFD5D65F4U,
|
||||
0x0F36E6F7U, 0x61C69362U, 0x93AD1061U, 0x80FDE395U, 0x72966096U,
|
||||
0xA65C047DU, 0x5437877EU, 0x4767748AU, 0xB50CF789U, 0xEB1FCBADU,
|
||||
0x197448AEU, 0x0A24BB5AU, 0xF84F3859U, 0x2C855CB2U, 0xDEEEDFB1U,
|
||||
0xCDBE2C45U, 0x3FD5AF46U, 0x7198540DU, 0x83F3D70EU, 0x90A324FAU,
|
||||
0x62C8A7F9U, 0xB602C312U, 0x44694011U, 0x5739B3E5U, 0xA55230E6U,
|
||||
0xFB410CC2U, 0x092A8FC1U, 0x1A7A7C35U, 0xE811FF36U, 0x3CDB9BDDU,
|
||||
0xCEB018DEU, 0xDDE0EB2AU, 0x2F8B6829U, 0x82F63B78U, 0x709DB87BU,
|
||||
0x63CD4B8FU, 0x91A6C88CU, 0x456CAC67U, 0xB7072F64U, 0xA457DC90U,
|
||||
0x563C5F93U, 0x082F63B7U, 0xFA44E0B4U, 0xE9141340U, 0x1B7F9043U,
|
||||
0xCFB5F4A8U, 0x3DDE77ABU, 0x2E8E845FU, 0xDCE5075CU, 0x92A8FC17U,
|
||||
0x60C37F14U, 0x73938CE0U, 0x81F80FE3U, 0x55326B08U, 0xA759E80BU,
|
||||
0xB4091BFFU, 0x466298FCU, 0x1871A4D8U, 0xEA1A27DBU, 0xF94AD42FU,
|
||||
0x0B21572CU, 0xDFEB33C7U, 0x2D80B0C4U, 0x3ED04330U, 0xCCBBC033U,
|
||||
0xA24BB5A6U, 0x502036A5U, 0x4370C551U, 0xB11B4652U, 0x65D122B9U,
|
||||
0x97BAA1BAU, 0x84EA524EU, 0x7681D14DU, 0x2892ED69U, 0xDAF96E6AU,
|
||||
0xC9A99D9EU, 0x3BC21E9DU, 0xEF087A76U, 0x1D63F975U, 0x0E330A81U,
|
||||
0xFC588982U, 0xB21572C9U, 0x407EF1CAU, 0x532E023EU, 0xA145813DU,
|
||||
0x758FE5D6U, 0x87E466D5U, 0x94B49521U, 0x66DF1622U, 0x38CC2A06U,
|
||||
0xCAA7A905U, 0xD9F75AF1U, 0x2B9CD9F2U, 0xFF56BD19U, 0x0D3D3E1AU,
|
||||
0x1E6DCDEEU, 0xEC064EEDU, 0xC38D26C4U, 0x31E6A5C7U, 0x22B65633U,
|
||||
0xD0DDD530U, 0x0417B1DBU, 0xF67C32D8U, 0xE52CC12CU, 0x1747422FU,
|
||||
0x49547E0BU, 0xBB3FFD08U, 0xA86F0EFCU, 0x5A048DFFU, 0x8ECEE914U,
|
||||
0x7CA56A17U, 0x6FF599E3U, 0x9D9E1AE0U, 0xD3D3E1ABU, 0x21B862A8U,
|
||||
0x32E8915CU, 0xC083125FU, 0x144976B4U, 0xE622F5B7U, 0xF5720643U,
|
||||
0x07198540U, 0x590AB964U, 0xAB613A67U, 0xB831C993U, 0x4A5A4A90U,
|
||||
0x9E902E7BU, 0x6CFBAD78U, 0x7FAB5E8CU, 0x8DC0DD8FU, 0xE330A81AU,
|
||||
0x115B2B19U, 0x020BD8EDU, 0xF0605BEEU, 0x24AA3F05U, 0xD6C1BC06U,
|
||||
0xC5914FF2U, 0x37FACCF1U, 0x69E9F0D5U, 0x9B8273D6U, 0x88D28022U,
|
||||
0x7AB90321U, 0xAE7367CAU, 0x5C18E4C9U, 0x4F48173DU, 0xBD23943EU,
|
||||
0xF36E6F75U, 0x0105EC76U, 0x12551F82U, 0xE03E9C81U, 0x34F4F86AU,
|
||||
0xC69F7B69U, 0xD5CF889DU, 0x27A40B9EU, 0x79B737BAU, 0x8BDCB4B9U,
|
||||
0x988C474DU, 0x6AE7C44EU, 0xBE2DA0A5U, 0x4C4623A6U, 0x5F16D052U,
|
||||
0xAD7D5351U};
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
crc32val = crc32_tab[(HASHMAP_CAST(unsigned char, crc32val) ^
|
||||
HASHMAP_CAST(unsigned char, s[i]))] ^
|
||||
(crc32val >> 8);
|
||||
}
|
||||
return crc32val;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned hashmap_hash_helper_int_helper(const struct hashmap_s *const m,
|
||||
const char *const keystring,
|
||||
const unsigned len) {
|
||||
unsigned key = hashmap_crc32_helper(keystring, len);
|
||||
|
||||
/* Robert Jenkins' 32 bit Mix Function */
|
||||
key += (key << 12);
|
||||
key ^= (key >> 22);
|
||||
key += (key << 4);
|
||||
key ^= (key >> 9);
|
||||
key += (key << 10);
|
||||
key ^= (key >> 2);
|
||||
key += (key << 7);
|
||||
key ^= (key >> 12);
|
||||
|
||||
/* Knuth's Multiplicative Method */
|
||||
key = (key >> 3) * 2654435761;
|
||||
|
||||
return key % m->table_size;
|
||||
}
|
||||
|
||||
int hashmap_match_helper(const struct hashmap_element_s *const element,
|
||||
const char *const key, const unsigned len) {
|
||||
return (element->key_len == len) && (0 == memcmp(element->key, key, len));
|
||||
}
|
||||
|
||||
int hashmap_hash_helper(const struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len, unsigned *const out_index) {
|
||||
unsigned int curr;
|
||||
unsigned int i;
|
||||
|
||||
/* If full, return immediately */
|
||||
if (m->size >= m->table_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the best index */
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing */
|
||||
for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) {
|
||||
if (!m->data[curr].in_use) {
|
||||
*out_index = curr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (m->data[curr].in_use &&
|
||||
hashmap_match_helper(&m->data[curr], key, len)) {
|
||||
*out_index = curr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_rehash_iterator(void *const new_hash,
|
||||
struct hashmap_element_s *const e) {
|
||||
int temp=hashmap_put(HASHMAP_PTR_CAST(struct hashmap_s *, new_hash),
|
||||
e->key, e->key_len, e->data);
|
||||
if (0<temp) {
|
||||
return 1;
|
||||
}
|
||||
/* clear old value to avoid stale pointers */
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Doubles the size of the hashmap, and rehashes all the elements
|
||||
*/
|
||||
int hashmap_rehash_helper(struct hashmap_s *const m) {
|
||||
/* If this multiplication overflows hashmap_create will fail. */
|
||||
unsigned new_size = 2 * m->table_size;
|
||||
|
||||
struct hashmap_s new_hash;
|
||||
|
||||
int flag = hashmap_create(new_size, &new_hash);
|
||||
if (0!=flag) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* copy the old elements to the new table */
|
||||
flag = hashmap_iterate_pairs(m, hashmap_rehash_iterator, HASHMAP_PTR_CAST(void *, &new_hash));
|
||||
if (0!=flag) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
hashmap_destroy(m);
|
||||
/* put new hash into old hash structure by copying */
|
||||
memcpy(m, &new_hash, sizeof(struct hashmap_s));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1401
v2/internal/ffenestri/json.c
Normal file
1401
v2/internal/ffenestri/json.c
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user