Compare commits

..

14 Commits

Author SHA1 Message Date
Lea\Anthony
b296fbbb99 Fix signatures
(cherry picked from commit 0f09e8d433)
2021-12-30 11:20:54 +11:00
Lea Anthony
26c4f112e3 [linux] Dialogs WIP 2021-12-29 09:47:31 +11:00
Lea\Anthony
f338dff171 Fix EventsOff in JS runtime 2021-12-29 09:11:12 +11:00
Lea\Anthony
3c6ed12637 New build flag: -debug 2021-12-29 06:54:52 +11:00
Lea Anthony
e2f3a11a33 [mac] Fix for cancelling Dialogs. Fixes #1047 2021-12-28 20:04:30 +11:00
Lea Anthony
0571deb290 Merge pull request #1046 from buddyabaddon/master
Make linuxdb.yaml an embedded resource
2021-12-28 17:27:59 +11:00
Matt McKenzie
451b357e40 Make linuxdb.yaml an embedded resource 2021-12-27 18:22:03 -08:00
Lea\Anthony
84b67a8f53 Add -u flag to sync project go.mod with CLI version 2021-12-28 06:40:44 +11:00
Lea Anthony
448cf731bb [mac] Fix for Save Dialog 2021-12-27 20:09:03 +11:00
Lea\Anthony
9cb480f0f0 v2.0.0-beta.27 2021-12-27 19:40:56 +11:00
Lea\Anthony
6825a631f5 Commit MicrosoftEdgeWebview2Setup.exe 2021-12-27 19:40:19 +11:00
Lea\Anthony
39f91a030f v2.0.0-beta.26 2021-12-27 18:12:48 +11:00
Lea\Anthony
202e4d5be8 Actually commit sudo_mattn 2021-12-27 18:11:12 +11:00
Lea\Anthony
045e58778a Actually commit webview2runtime 2021-12-27 18:09:41 +11:00
19 changed files with 597 additions and 43 deletions

View File

@@ -148,16 +148,6 @@ func (fs *FSHelper) LocalDir(dir string) (*Dir, error) {
}, err
}
// LoadRelativeFile loads the given file relative to the caller's directory
func (fs *FSHelper) LoadRelativeFile(relativePath string) ([]byte, error) {
_, filename, _, _ := runtime.Caller(0)
fullPath, err := filepath.Abs(filepath.Join(path.Dir(filename), relativePath))
if err != nil {
return nil, err
}
return os.ReadFile(fullPath)
}
// GetSubdirs will return a list of FQPs to subdirectories in the given directory
func (d *Dir) GetSubdirs() (map[string]string, error) {

View File

@@ -1,11 +1,15 @@
package cmd
import (
_ "embed"
"log"
"gopkg.in/yaml.v3"
)
//go:embed linuxdb.yaml
var LinuxDBYaml []byte
// LinuxDB is the database for linux distribution data.
type LinuxDB struct {
Distributions map[string]*Distribution `yaml:"distributions"`
@@ -78,14 +82,10 @@ func (l *LinuxDB) GetDistro(distro string) *Distribution {
// NewLinuxDB creates a new LinuxDB instance from the bundled
// linuxdb.yaml file.
func NewLinuxDB() *LinuxDB {
data, err := fs.LoadRelativeFile("./linuxdb.yaml")
if err != nil {
log.Fatal("Could not load linuxdb.yaml")
}
result := LinuxDB{
Distributions: make(map[string]*Distribution),
}
err = result.ImportData(data)
err := result.ImportData(LinuxDBYaml)
if err != nil {
log.Fatal(err)
}

View File

@@ -2,6 +2,7 @@ package build
import (
"fmt"
"github.com/wailsapp/wails/v2/internal/colour"
"io"
"os"
"os/exec"
@@ -79,6 +80,12 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
forceBuild := false
command.BoolFlag("f", "Force build application", &forceBuild)
updateGoMod := false
command.BoolFlag("u", "Updates go.mod to use the same Wails version as the CLI", &updateGoMod)
debug := false
command.BoolFlag("debug", "Retains debug data in the compiled application", &debug)
command.Action(func() error {
quiet := verbosity == 0
@@ -160,13 +167,20 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
}
}
mode := build.Production
modeString := "Production"
if debug {
mode = build.Debug
modeString = "Debug"
}
// Create BuildOptions
buildOptions := &build.Options{
Logger: logger,
OutputType: outputType,
OutputFile: outputFilename,
CleanBuildDirectory: cleanBuildDirectory,
Mode: build.Production,
Mode: mode,
Pack: !noPackage,
LDFlags: ldflags,
Compiler: compilerCommand,
@@ -202,6 +216,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
fmt.Fprintf(w, "Compiler: \t%s\n", compilerPath)
fmt.Fprintf(w, "Build Mode: \t%s\n", modeString)
fmt.Fprintf(w, "Skip Frontend: \t%t\n", skipFrontend)
fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
@@ -214,7 +229,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
fmt.Fprintf(w, "\n")
w.Flush()
err = checkGoModVersion(logger)
err = checkGoModVersion(logger, updateGoMod)
if err != nil {
return err
}
@@ -243,7 +258,7 @@ func doBuild(buildOptions *build.Options) error {
return nil
}
func checkGoModVersion(logger *clilogger.CLILogger) error {
func checkGoModVersion(logger *clilogger.CLILogger, updateGoMod bool) error {
cwd, err := os.Getwd()
if err != nil {
return err
@@ -265,6 +280,36 @@ func checkGoModVersion(logger *clilogger.CLILogger) error {
return err
}
logger.Println("Warning: go.mod is using Wails '%s' but the CLI is '%s'. Consider updating it.\n", gomodversion.String(), internal.Version)
if updateGoMod {
return syncGoModVersion(cwd)
}
logger.Println("Warning: go.mod is using Wails '%s' but the CLI is '%s'. Consider updating your project's `go.mod` file.\n", gomodversion.String(), internal.Version)
return nil
}
func LogGreen(message string, args ...interface{}) {
text := fmt.Sprintf(message, args...)
println(colour.Green(text))
}
func syncGoModVersion(cwd string) error {
gomodFilename := filepath.Join(cwd, "go.mod")
gomodData, err := os.ReadFile(gomodFilename)
if err != nil {
return err
}
outOfSync, err := gomod.GoModOutOfSync(gomodData, internal.Version)
if err != nil {
return err
}
if !outOfSync {
return nil
}
LogGreen("Updating go.mod to use Wails '%s'", internal.Version)
newGoData, err := gomod.UpdateGoModVersion(gomodData, internal.Version)
if err != nil {
return err
}
return os.WriteFile(gomodFilename, newGoData, 0755)
}

View File

@@ -1,3 +1,3 @@
package internal
var Version = "v2.0.0-beta.25"
var Version = "v2.0.0-beta.27"

View File

@@ -540,6 +540,10 @@
// Setup callback handler
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
if ( returnCode != NSModalResponseOK) {
processOpenFileDialogResponse("[]");
return;
}
NSMutableArray *arr = [NSMutableArray new];
for (NSURL *url in [dialog URLs]) {
[arr addObject:[url path]];
@@ -558,7 +562,7 @@
// Create the dialog
NSSavePanel *dialog = [NSOpenPanel savePanel];
NSSavePanel *dialog = [NSSavePanel savePanel];
// Valid but appears to do nothing.... :/
if( title != nil ) {
@@ -592,10 +596,12 @@
// Setup callback handler
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
NSURL *url = [dialog URL];
if ( url != nil ) {
processSaveFileDialogResponse([url.path UTF8String]);
return;
if ( returnCode == NSModalResponseOK ) {
NSURL *url = [dialog URL];
if ( url != nil ) {
processSaveFileDialogResponse([url.path UTF8String]);
return;
}
}
processSaveFileDialogResponse("");
}];

View File

@@ -0,0 +1,32 @@
package linux
/*
#include <stdlib.h>
*/
import "C"
import "unsafe"
// Calloc handles alloc/dealloc of C data
type Calloc struct {
pool []unsafe.Pointer
}
// NewCalloc creates a new allocator
func NewCalloc() Calloc {
return Calloc{}
}
// String creates a new C string and retains a reference to it
func (c Calloc) String(in string) *C.char {
result := C.CString(in)
c.pool = append(c.pool, unsafe.Pointer(result))
return result
}
// Free frees all allocated C memory
func (c Calloc) Free() {
for _, str := range c.pool {
C.free(str)
}
c.pool = []unsafe.Pointer{}
}

View File

@@ -3,10 +3,24 @@
package linux
import "github.com/wailsapp/wails/v2/internal/frontend"
import (
"github.com/wailsapp/wails/v2/internal/frontend"
)
import "C"
func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (string, error) {
panic("implement me")
var openFileResults = make(chan string)
func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (result string, err error) {
f.dispatch(func() {
println("Before OpenFileDialog")
f.mainWindow.OpenFileDialog(dialogOptions)
println("After OpenFileDialog")
})
println("Waiting for result")
result = <-openFileResults
println("Got result")
return
}
func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) {
@@ -24,3 +38,8 @@ func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (str
func (f *Frontend) MessageDialog(dialogOptions frontend.MessageDialogOptions) (string, error) {
panic("implement me")
}
//export processOpenFileResult
func processOpenFileResult(result *C.char) {
openFileResults <- C.GoString(result)
}

View File

@@ -24,6 +24,7 @@ import "C"
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"strconv"
@@ -62,6 +63,7 @@ type Frontend struct {
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
println("[NewFrontend] PID:", os.Getpid())
// Set GDK_BACKEND=x11 to prevent warnings
os.Setenv("GDK_BACKEND", "x11")
@@ -301,10 +303,11 @@ var dispatchCallbackLock sync.Mutex
//export callDispatchedMethod
func callDispatchedMethod(cid C.int) {
println("[callDispatchedMethod] PID:", os.Getpid())
id := int(cid)
fn := dispatchCallbacks[id]
if fn != nil {
go fn()
fn()
dispatchCallbackLock.Lock()
delete(dispatchCallbacks, id)
dispatchCallbackLock.Unlock()
@@ -342,11 +345,24 @@ func (f *Frontend) processRequest(request unsafe.Pointer) {
// Load file from asset store
content, mimeType, err := f.assets.Load(file)
if err != nil {
return
}
// TODO How to return 404/500 errors to webkit?
if err != nil {
if os.IsNotExist(err) {
f.dispatch(func() {
message := C.CString("not found")
defer C.free(unsafe.Pointer(message))
C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(404), message))
})
} else {
err = fmt.Errorf("Error processing request %s: %w", uri, err)
f.logger.Error(err.Error())
message := C.CString("internal server error")
defer C.free(unsafe.Pointer(message))
C.webkit_uri_scheme_request_finish_error(req, C.g_error_new_literal(C.G_FILE_ERROR_NOENT, C.int(500), message))
}
return
}
cContent := C.CString(string(content))
defer C.free(unsafe.Pointer(cContent))

View File

@@ -4,10 +4,11 @@
package linux
/*
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 x11
#include "gtk/gtk.h"
#include "webkit2/webkit2.h"
#include <X11/Xlib.h>
#include <stdio.h>
#include <limits.h>
@@ -83,7 +84,7 @@ extern void processMessage(char*);
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
WebKitJavascriptResult *result,
void*)
void* data)
{
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
JSCValue *value = webkit_javascript_result_get_js_value(result);
@@ -105,6 +106,11 @@ ulong setupInvokeSignal(void* contentManager) {
return g_signal_connect((WebKitUserContentManager*)contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), NULL);
}
void initThreads() {
printf("init threads\n");
XInitThreads();
}
// These are the x,y & time of the last mouse down event
// It's used for window dragging
float xroot = 0.0f;
@@ -145,7 +151,7 @@ void connectButtons(void* webview) {
extern void processURLRequest(WebKitURISchemeRequest *request);
// This is called when the close button on the window is pressed
gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void*)
gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void* data)
{
processMessage("Q");
return FALSE;
@@ -184,10 +190,36 @@ static void startDrag(void *webview, GtkWindow* mainwindow)
gtk_window_begin_move_drag(mainwindow, 1, xroot, yroot, dragTime);
}
void extern processOpenFileResult(char*);
static void OpenDialog(GtkWindow* window, char *title) {
printf("Here\n");
GtkWidget *dlg = gtk_file_chooser_dialog_new(title, window, GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT,
NULL);
printf("Here3\n");
gint response = gtk_dialog_run(GTK_DIALOG(dlg));
printf("Here 4\n");
if (response == GTK_RESPONSE_ACCEPT)
{
printf("Here 5\n");
gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
processOpenFileResult(filename);
g_free(filename);
}
gtk_widget_destroy(dlg);
}
*/
import "C"
import (
"github.com/wailsapp/wails/v2/internal/frontend"
"github.com/wailsapp/wails/v2/pkg/options"
"os"
"unsafe"
)
@@ -369,6 +401,8 @@ func (w *Window) Run() {
case options.Maximised:
w.Maximise()
}
C.initThreads()
C.gtk_main()
w.Destroy()
}
@@ -408,3 +442,11 @@ func (w *Window) StartDrag() {
func (w *Window) Quit() {
C.gtk_main_quit()
}
func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) {
println("OpenFileDialog PID:", os.Getpid())
mem := NewCalloc()
title := mem.String(dialogOptions.Title)
C.OpenDialog(w.asGTKWindow(), title)
mem.Free()
}

View File

@@ -152,7 +152,7 @@ export function EventsEmit(eventName) {
export function EventsOff(eventName) {
// Remove local listeners
eventListeners.delete(eventName);
delete eventListeners[eventName];
// Notify Go listeners
window.WailsInvoke('EX' + eventName);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,232 @@
//go:build windows
// +build windows
// Original File (c) 2017 Yasuhiro Matsumoto: https://github.com/mattn/sudo/blob/master/win32.go
// License: https://github.com/mattn/sudo/blob/master/LICENSE
package webview2runtime
import (
"errors"
"fmt"
"os"
"syscall"
"unsafe"
)
var (
modshell32 = syscall.NewLazyDLL("shell32.dll")
procShellExecuteEx = modshell32.NewProc("ShellExecuteExW")
)
const (
_SEE_MASK_DEFAULT = 0x00000000
_SEE_MASK_CLASSNAME = 0x00000001
_SEE_MASK_CLASSKEY = 0x00000003
_SEE_MASK_IDLIST = 0x00000004
_SEE_MASK_INVOKEIDLIST = 0x0000000C
_SEE_MASK_ICON = 0x00000010
_SEE_MASK_HOTKEY = 0x00000020
_SEE_MASK_NOCLOSEPROCESS = 0x00000040
_SEE_MASK_CONNECTNETDRV = 0x00000080
_SEE_MASK_NOASYNC = 0x00000100
_SEE_MASK_FLAG_DDEWAIT = 0x00000100
_SEE_MASK_DOENVSUBST = 0x00000200
_SEE_MASK_FLAG_NO_UI = 0x00000400
_SEE_MASK_UNICODE = 0x00004000
_SEE_MASK_NO_CONSOLE = 0x00008000
_SEE_MASK_ASYNCOK = 0x00100000
_SEE_MASK_NOQUERYCLASSSTORE = 0x01000000
_SEE_MASK_HMONITOR = 0x00200000
_SEE_MASK_NOZONECHECKS = 0x00800000
_SEE_MASK_WAITFORINPUTIDLE = 0x02000000
_SEE_MASK_FLAG_LOG_USAGE = 0x04000000
_SEE_MASK_FLAG_HINST_IS_SITE = 0x08000000
)
const (
_ERROR_BAD_FORMAT = 11
)
const (
_SE_ERR_FNF = 2
_SE_ERR_PNF = 3
_SE_ERR_ACCESSDENIED = 5
_SE_ERR_OOM = 8
_SE_ERR_DLLNOTFOUND = 32
_SE_ERR_SHARE = 26
_SE_ERR_ASSOCINCOMPLETE = 27
_SE_ERR_DDETIMEOUT = 28
_SE_ERR_DDEFAIL = 29
_SE_ERR_DDEBUSY = 30
_SE_ERR_NOASSOC = 31
)
type (
dword uint32
hinstance syscall.Handle
hkey syscall.Handle
hwnd syscall.Handle
ulong uint32
lpctstr uintptr
lpvoid uintptr
)
// SHELLEXECUTEINFO struct
type _SHELLEXECUTEINFO struct {
cbSize dword
fMask ulong
hwnd hwnd
lpVerb lpctstr
lpFile lpctstr
lpParameters lpctstr
lpDirectory lpctstr
nShow int
hInstApp hinstance
lpIDList lpvoid
lpClass lpctstr
hkeyClass hkey
dwHotKey dword
hIconOrMonitor syscall.Handle
hProcess syscall.Handle
}
// ShellExecuteAndWait is version of ShellExecuteEx which want process
func ShellExecuteAndWait(hwnd hwnd, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error {
var lpctstrVerb, lpctstrParameters, lpctstrDirectory, lpctstrFile lpctstr
var err error
if len(lpOperation) != 0 {
lpctstrVerb, err = toUTF16(lpOperation)
if err != nil {
return err
}
}
if len(lpParameters) != 0 {
lpctstrParameters, err = toUTF16(lpParameters)
if err != nil {
return err
}
}
if len(lpDirectory) != 0 {
lpctstrDirectory, err = toUTF16(lpDirectory)
if err != nil {
return err
}
}
if len(lpDirectory) != 0 {
lpctstrFile, err = toUTF16(lpFile)
if err != nil {
return err
}
}
i := &_SHELLEXECUTEINFO{
fMask: _SEE_MASK_NOCLOSEPROCESS,
hwnd: hwnd,
lpVerb: lpctstrVerb,
lpFile: lpctstrFile,
lpParameters: lpctstrParameters,
lpDirectory: lpctstrDirectory,
nShow: nShowCmd,
}
i.cbSize = dword(unsafe.Sizeof(*i))
return ShellExecuteEx(i)
}
func toUTF16(lpOperation string) (lpctstr, error) {
result, err := syscall.UTF16PtrFromString(lpOperation)
if err != nil {
return 0, err
}
return lpctstr(unsafe.Pointer(result)), nil
}
// ShellExecuteNoWait is version of ShellExecuteEx which don't want process
func ShellExecuteNowait(hwnd hwnd, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error {
var lpctstrVerb, lpctstrParameters, lpctstrDirectory, lpctstrFile lpctstr
var err error
if len(lpOperation) != 0 {
lpctstrVerb, err = toUTF16(lpOperation)
if err != nil {
return err
}
}
if len(lpParameters) != 0 {
lpctstrParameters, err = toUTF16(lpParameters)
if err != nil {
return err
}
}
if len(lpDirectory) != 0 {
lpctstrDirectory, err = toUTF16(lpDirectory)
if err != nil {
return err
}
}
if len(lpDirectory) != 0 {
lpctstrFile, err = toUTF16(lpFile)
if err != nil {
return err
}
}
i := &_SHELLEXECUTEINFO{
fMask: _SEE_MASK_DEFAULT,
hwnd: hwnd,
lpVerb: lpctstrVerb,
lpFile: lpctstrFile,
lpParameters: lpctstrParameters,
lpDirectory: lpctstrDirectory,
nShow: nShowCmd,
}
i.cbSize = dword(unsafe.Sizeof(*i))
return ShellExecuteEx(i)
}
// ShellExecuteEx is Windows API
func ShellExecuteEx(pExecInfo *_SHELLEXECUTEINFO) error {
ret, _, _ := procShellExecuteEx.Call(uintptr(unsafe.Pointer(pExecInfo)))
if ret == 1 && pExecInfo.fMask&_SEE_MASK_NOCLOSEPROCESS != 0 {
s, e := syscall.WaitForSingleObject(pExecInfo.hProcess, syscall.INFINITE)
switch s {
case syscall.WAIT_OBJECT_0:
break
case syscall.WAIT_FAILED:
return os.NewSyscallError("WaitForSingleObject", e)
default:
return errors.New("Unexpected result from WaitForSingleObject")
}
}
errorMsg := ""
if pExecInfo.hInstApp != 0 && pExecInfo.hInstApp <= 32 {
switch int(pExecInfo.hInstApp) {
case _SE_ERR_FNF:
errorMsg = "The specified file was not found"
case _SE_ERR_PNF:
errorMsg = "The specified path was not found"
case _ERROR_BAD_FORMAT:
errorMsg = "The .exe file is invalid (non-Win32 .exe or error in .exe image)"
case _SE_ERR_ACCESSDENIED:
errorMsg = "The operating system denied access to the specified file"
case _SE_ERR_ASSOCINCOMPLETE:
errorMsg = "The file name association is incomplete or invalid"
case _SE_ERR_DDEBUSY:
errorMsg = "The DDE transaction could not be completed because other DDE transactions were being processed"
case _SE_ERR_DDEFAIL:
errorMsg = "The DDE transaction failed"
case _SE_ERR_DDETIMEOUT:
errorMsg = "The DDE transaction could not be completed because the request timed out"
case _SE_ERR_DLLNOTFOUND:
errorMsg = "The specified DLL was not found"
case _SE_ERR_NOASSOC:
errorMsg = "There is no application associated with the given file name extension"
case _SE_ERR_OOM:
errorMsg = "There was not enough memory to complete the operation"
case _SE_ERR_SHARE:
errorMsg = "A sharing violation occurred"
default:
errorMsg = fmt.Sprintf("Unknown error occurred with error code %v", pExecInfo.hInstApp)
}
} else {
return nil
}
return errors.New(errorMsg)
}

View File

@@ -0,0 +1,168 @@
//go:build windows
// +build windows
package webview2runtime
import (
_ "embed"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"syscall"
"unsafe"
)
//go:embed MicrosoftEdgeWebview2Setup.exe
var setupexe []byte
// Info contains all the information about an installation of the webview2 runtime.
type Info struct {
Location string
Name string
Version string
SilentUninstall string
}
// IsOlderThan returns true if the installed version is older than the given required version.
// Returns error if something goes wrong.
func (i *Info) IsOlderThan(requiredVersion string) (bool, error) {
var mod = syscall.NewLazyDLL("WebView2Loader.dll")
var CompareBrowserVersions = mod.NewProc("CompareBrowserVersions")
v1, err := syscall.UTF16PtrFromString(i.Version)
if err != nil {
return false, err
}
v2, err := syscall.UTF16PtrFromString(requiredVersion)
if err != nil {
return false, err
}
var result int = 9
_, _, err = CompareBrowserVersions.Call(uintptr(unsafe.Pointer(v1)), uintptr(unsafe.Pointer(v2)), uintptr(unsafe.Pointer(&result)))
if result < -1 || result > 1 {
return false, err
}
return result == -1, nil
}
func downloadBootstrapper() (string, error) {
bootstrapperURL := `https://go.microsoft.com/fwlink/p/?LinkId=2124703`
installer := filepath.Join(os.TempDir(), `MicrosoftEdgeWebview2Setup.exe`)
// Download installer
out, err := os.Create(installer)
defer out.Close()
if err != nil {
return "", err
}
resp, err := http.Get(bootstrapperURL)
defer resp.Body.Close()
if err != nil {
err = out.Close()
return "", err
}
_, err = io.Copy(out, resp.Body)
if err != nil {
return "", err
}
return installer, nil
}
// InstallUsingEmbeddedBootstrapper will download the bootstrapper from Microsoft and run it to install
// the latest version of the runtime.
// Returns true if the installer ran successfully.
// Returns an error if something goes wrong
func InstallUsingEmbeddedBootstrapper() (bool, error) {
installer := filepath.Join(os.TempDir(), `MicrosoftEdgeWebview2Setup.exe`)
err := os.WriteFile(installer, setupexe, 0755)
if err != nil {
return false, err
}
result, err := runInstaller(installer)
if err != nil {
return false, err
}
return result, os.Remove(installer)
}
// InstallUsingBootstrapper will extract the embedded bootstrapper from Microsoft and run it to install
// the latest version of the runtime.
// Returns true if the installer ran successfully.
// Returns an error if something goes wrong
func InstallUsingBootstrapper() (bool, error) {
installer, err := downloadBootstrapper()
if err != nil {
return false, err
}
result, err := runInstaller(installer)
if err != nil {
return false, err
}
return result, os.Remove(installer)
}
func runInstaller(installer string) (bool, error) {
err := ShellExecuteAndWait(0, "runas", installer, "", os.Getenv("TMP"), syscall.SW_NORMAL)
if err != nil {
fmt.Println(err)
return false, err
}
return true, nil
}
// Confirm will prompt the user with a message and OK / CANCEL buttons.
// Returns true if OK is selected by the user.
// Returns an error if something went wrong.
func Confirm(caption string, title string) (bool, error) {
var flags uint = 0x00000001 // MB_OKCANCEL
result, err := MessageBox(caption, title, flags)
if err != nil {
return false, err
}
return result == 1, nil
}
// Error will an error message to the user.
// Returns an error if something went wrong.
func Error(caption string, title string) error {
var flags uint = 0x00000010 // MB_ICONERROR
_, err := MessageBox(caption, title, flags)
return err
}
// MessageBox prompts the user with the given caption and title.
// Flags may be provided to customise the dialog.
// Returns an error if something went wrong.
func MessageBox(caption string, title string, flags uint) (int, error) {
captionUTF16, err := syscall.UTF16PtrFromString(caption)
if err != nil {
return -1, err
}
titleUTF16, err := syscall.UTF16PtrFromString(title)
if err != nil {
return -1, err
}
ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
uintptr(0),
uintptr(unsafe.Pointer(captionUTF16)),
uintptr(unsafe.Pointer(titleUTF16)),
uintptr(flags))
return int(ret), nil
}
// OpenInstallerDownloadWebpage will open the browser on the WebView2 download page
func OpenInstallerDownloadWebpage() error {
cmd := exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://developer.microsoft.com/en-us/microsoft-edge/webview2/")
return cmd.Run()
}

View File

@@ -215,7 +215,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
commands := slicer.String([]string{"build"})
// Add better debugging flags
if options.Mode == Dev {
if options.Mode == Dev || options.Mode == Debug {
commands.Add("-gcflags")
commands.Add(`"all=-N -l"`)
}
@@ -233,7 +233,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
tags.Add(options.WebView2Strategy)
}
if options.Mode == Production {
if options.Mode == Production || options.Mode == Debug {
tags.Add("production")
}

View File

@@ -23,6 +23,8 @@ const (
Dev Mode = iota
// Production mode
Production
// Debug build
Debug
)
// Options contains all the build options as well as the project data

View File

@@ -66,6 +66,8 @@ A list of community maintained templates can be found [here](/docs/community/tem
| -upxflags | Flags to pass to upx | |
| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 |
| -webview2 | WebView2 installer strategy: download,embed,browser,error | download |
| -u | Updates your project's `go.mod` to use the same version of Wails as the CLI | |
| -debug | Retains debug information in the application | false |
For a detailed description of the `webview2` flag, please refer to the [Windows](/docs/guides/windows) Guide.