Files

527 lines
12 KiB
Go
Raw Permalink Normal View History

2021-11-13 17:06:48 -08:00
//go:build linux
// +build linux
package linux
2021-11-27 20:36:25 +11:00
/*
2024-05-08 18:26:27 -06:00
#cgo linux pkg-config: gtk+-3.0
#cgo !webkit2_41 pkg-config: webkit2gtk-4.0
#cgo webkit2_41 pkg-config: webkit2gtk-4.1
2021-11-27 20:36:25 +11:00
#include "gtk/gtk.h"
2021-11-29 08:25:42 +11:00
#include "webkit2/webkit2.h"
2021-11-27 20:36:25 +11:00
2022-11-30 20:37:55 +11:00
// CREDIT: https://github.com/rainycape/magick
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
static void fix_signal(int signum)
{
struct sigaction st;
if (sigaction(signum, NULL, &st) < 0) {
goto fix_signal_error;
}
st.sa_flags |= SA_ONSTACK;
if (sigaction(signum, &st, NULL) < 0) {
goto fix_signal_error;
}
return;
fix_signal_error:
fprintf(stderr, "error fixing handler for signal %d, please "
"report this issue to "
"https://github.com/wailsapp/wails: %s\n",
signum, strerror(errno));
}
static void install_signal_handlers()
{
#if defined(SIGCHLD)
fix_signal(SIGCHLD);
#endif
#if defined(SIGHUP)
fix_signal(SIGHUP);
#endif
#if defined(SIGINT)
fix_signal(SIGINT);
#endif
#if defined(SIGQUIT)
fix_signal(SIGQUIT);
#endif
#if defined(SIGABRT)
fix_signal(SIGABRT);
#endif
#if defined(SIGFPE)
fix_signal(SIGFPE);
#endif
#if defined(SIGTERM)
fix_signal(SIGTERM);
#endif
#if defined(SIGBUS)
fix_signal(SIGBUS);
#endif
#if defined(SIGSEGV)
fix_signal(SIGSEGV);
#endif
#if defined(SIGXCPU)
fix_signal(SIGXCPU);
#endif
#if defined(SIGXFSZ)
fix_signal(SIGXFSZ);
#endif
}
2021-11-27 20:36:25 +11:00
*/
import "C"
2021-11-13 17:06:48 -08:00
import (
"context"
"encoding/json"
2023-08-25 21:41:53 +01:00
"errors"
2022-04-12 12:18:27 +02:00
"fmt"
2021-11-27 20:36:25 +11:00
"log"
"net"
2022-04-12 12:18:27 +02:00
"net/url"
2021-11-27 20:36:25 +11:00
"os"
"runtime"
"strings"
2023-08-25 21:41:53 +01:00
"sync"
2021-11-27 20:36:25 +11:00
"text/template"
2021-11-29 08:25:42 +11:00
"unsafe"
2023-01-08 21:25:21 +11:00
"github.com/wailsapp/wails/v2/pkg/assetserver"
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
2023-01-08 21:25:21 +11:00
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/frontend"
2022-12-31 10:36:07 +11:00
wailsruntime "github.com/wailsapp/wails/v2/internal/frontend/runtime"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/pkg/options"
2021-11-13 17:06:48 -08:00
)
2023-08-25 21:41:53 +01:00
var initOnce = sync.Once{}
const startURL = "wails://wails/"
var secondInstanceBuffer = make(chan options.SecondInstanceData, 1)
2021-11-13 17:06:48 -08:00
type Frontend struct {
// Context
ctx context.Context
frontendOptions *options.App
logger *logger.Logger
debug bool
devtoolsEnabled bool
2021-11-13 17:06:48 -08:00
// Assets
assets *assetserver.AssetServer
2022-04-12 12:18:27 +02:00
startURL *url.URL
2021-11-13 17:06:48 -08:00
// main window handle
2022-04-12 12:18:27 +02:00
mainWindow *Window
bindings *binding.Bindings
dispatcher frontend.Dispatcher
2021-11-27 20:36:25 +11:00
}
2022-09-29 18:43:35 +10:00
func (f *Frontend) RunMainLoop() {
C.gtk_main()
}
func (f *Frontend) WindowClose() {
f.mainWindow.Destroy()
}
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
2023-08-25 21:41:53 +01:00
initOnce.Do(func() {
runtime.LockOSThread()
// Set GDK_BACKEND=x11 if currently unset and XDG_SESSION_TYPE is unset, unspecified or x11 to prevent warnings
if os.Getenv("GDK_BACKEND") == "" && (os.Getenv("XDG_SESSION_TYPE") == "" || os.Getenv("XDG_SESSION_TYPE") == "unspecified" || os.Getenv("XDG_SESSION_TYPE") == "x11") {
_ = os.Setenv("GDK_BACKEND", "x11")
}
if ok := C.gtk_init_check(nil, nil); ok != 1 {
panic(errors.New("failed to init GTK"))
}
})
2021-11-13 17:06:48 -08:00
result := &Frontend{
frontendOptions: appoptions,
logger: myLogger,
bindings: appBindings,
dispatcher: dispatcher,
ctx: ctx,
}
result.startURL, _ = url.Parse(startURL)
2021-11-13 17:06:48 -08:00
2022-04-12 12:18:27 +02:00
if _starturl, _ := ctx.Value("starturl").(*url.URL); _starturl != nil {
result.startURL = _starturl
} else {
if port, _ := ctx.Value("assetserverport").(string); port != "" {
result.startURL.Host = net.JoinHostPort(result.startURL.Host+".localhost", port)
}
2022-09-13 10:05:37 +10:00
var bindings string
var err error
if _obfuscated, _ := ctx.Value("obfuscated").(bool); !_obfuscated {
bindings, err = appBindings.ToJSON()
if err != nil {
log.Fatal(err)
}
} else {
appBindings.DB().UpdateObfuscatedCallMap()
}
2022-12-31 10:36:07 +11:00
assets, err := assetserver.NewAssetServerMainPage(bindings, appoptions, ctx.Value("assetdir") != nil, myLogger, wailsruntime.RuntimeAssetsBundle)
if err != nil {
log.Fatal(err)
}
result.assets = assets
2021-11-13 17:06:48 -08:00
go result.startRequestProcessor()
2021-11-13 17:06:48 -08:00
}
2021-11-28 14:21:02 +11:00
go result.startMessageProcessor()
2021-11-27 20:36:25 +11:00
var _debug = ctx.Value("debug")
var _devtoolsEnabled = ctx.Value("devtoolsEnabled")
2021-11-27 20:36:25 +11:00
if _debug != nil {
result.debug = _debug.(bool)
}
if _devtoolsEnabled != nil {
result.devtoolsEnabled = _devtoolsEnabled.(bool)
}
result.mainWindow = NewWindow(appoptions, result.debug, result.devtoolsEnabled)
2021-11-14 22:40:37 +11:00
2022-11-30 20:37:55 +11:00
C.install_signal_handlers()
if appoptions.Linux != nil && appoptions.Linux.ProgramName != "" {
prgname := C.CString(appoptions.Linux.ProgramName)
C.g_set_prgname(prgname)
C.free(unsafe.Pointer(prgname))
}
go result.startSecondInstanceProcessor()
2021-11-13 17:06:48 -08:00
return result
}
2021-11-28 14:21:02 +11:00
func (f *Frontend) startMessageProcessor() {
for message := range messageBuffer {
f.processMessage(message)
}
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) WindowReload() {
f.ExecJS("runtime.WindowReload();")
}
func (f *Frontend) WindowSetSystemDefaultTheme() {
return
}
func (f *Frontend) WindowSetLightTheme() {
return
}
func (f *Frontend) WindowSetDarkTheme() {
return
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) Run(ctx context.Context) error {
2022-10-05 08:44:23 +11:00
f.ctx = ctx
2021-11-13 17:06:48 -08:00
go func() {
if f.frontendOptions.OnStartup != nil {
f.frontendOptions.OnStartup(f.ctx)
}
}()
if f.frontendOptions.SingleInstanceLock != nil {
SetupSingleInstance(f.frontendOptions.SingleInstanceLock.UniqueId)
}
2022-04-12 12:18:27 +02:00
f.mainWindow.Run(f.startURL.String())
2021-11-13 17:06:48 -08:00
return nil
}
func (f *Frontend) WindowCenter() {
2021-11-29 20:19:18 +11:00
f.mainWindow.Center()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowSetAlwaysOnTop(b bool) {
f.mainWindow.SetKeepAbove(b)
}
2022-02-03 21:42:14 +11:00
func (f *Frontend) WindowSetPosition(x, y int) {
f.mainWindow.SetPosition(x, y)
2021-11-13 17:06:48 -08:00
}
2022-02-03 21:42:14 +11:00
func (f *Frontend) WindowGetPosition() (int, int) {
return f.mainWindow.GetPosition()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowSetSize(width, height int) {
2021-11-29 20:19:18 +11:00
f.mainWindow.SetSize(width, height)
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowGetSize() (int, int) {
2021-11-29 20:19:18 +11:00
return f.mainWindow.Size()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowSetTitle(title string) {
2021-11-29 20:19:18 +11:00
f.mainWindow.SetTitle(title)
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowFullscreen() {
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
f.ExecJS("window.wails.flags.enableResize = false;")
}
2021-11-29 20:19:18 +11:00
f.mainWindow.Fullscreen()
2021-11-13 17:06:48 -08:00
}
2022-02-19 20:29:55 +11:00
func (f *Frontend) WindowUnfullscreen() {
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
f.ExecJS("window.wails.flags.enableResize = true;")
}
2021-11-29 20:19:18 +11:00
f.mainWindow.UnFullscreen()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowReloadApp() {
f.ExecJS(fmt.Sprintf("window.location.href = '%s';", f.startURL))
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) WindowShow() {
2021-11-29 20:19:18 +11:00
f.mainWindow.Show()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowHide() {
2021-11-29 20:19:18 +11:00
f.mainWindow.Hide()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) Show() {
f.mainWindow.Show()
}
func (f *Frontend) Hide() {
f.mainWindow.Hide()
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) WindowMaximise() {
2021-11-29 20:19:18 +11:00
f.mainWindow.Maximise()
2021-11-13 17:06:48 -08:00
}
2022-02-18 20:28:16 +11:00
func (f *Frontend) WindowToggleMaximise() {
f.mainWindow.ToggleMaximise()
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) WindowUnmaximise() {
2021-11-29 20:19:18 +11:00
f.mainWindow.UnMaximise()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowMinimise() {
2021-11-29 20:19:18 +11:00
f.mainWindow.Minimise()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowUnminimise() {
2021-11-29 20:19:18 +11:00
f.mainWindow.UnMinimise()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowSetMinSize(width int, height int) {
2021-11-29 20:19:18 +11:00
f.mainWindow.SetMinSize(width, height)
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowSetMaxSize(width int, height int) {
2021-11-29 20:19:18 +11:00
f.mainWindow.SetMaxSize(width, height)
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) WindowSetBackgroundColour(col *options.RGBA) {
2021-11-13 17:06:48 -08:00
if col == nil {
return
}
f.mainWindow.SetBackgroundColour(col.R, col.G, col.B, col.A)
2021-11-13 17:06:48 -08:00
}
2022-07-16 05:33:37 +03:00
func (f *Frontend) ScreenGetAll() ([]Screen, error) {
return GetAllScreens(f.mainWindow.asGTKWindow())
}
2022-08-29 13:52:01 -07:00
func (f *Frontend) WindowIsMaximised() bool {
return f.mainWindow.IsMaximised()
}
func (f *Frontend) WindowIsMinimised() bool {
return f.mainWindow.IsMinimised()
}
func (f *Frontend) WindowIsNormal() bool {
return f.mainWindow.IsNormal()
}
func (f *Frontend) WindowIsFullscreen() bool {
return f.mainWindow.IsFullScreen()
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) Quit() {
2022-07-14 21:04:45 +10:00
if f.frontendOptions.OnBeforeClose != nil {
go func() {
if !f.frontendOptions.OnBeforeClose(f.ctx) {
f.mainWindow.Quit()
}
}()
2021-12-04 04:55:36 +09:00
return
}
2021-11-29 20:19:18 +11:00
f.mainWindow.Quit()
2021-11-13 17:06:48 -08:00
}
2023-09-05 22:55:36 +01:00
func (f *Frontend) WindowPrint() {
f.ExecJS("window.print();")
}
2021-11-13 17:06:48 -08:00
type EventNotify struct {
Name string `json:"name"`
Data []interface{} `json:"data"`
}
func (f *Frontend) Notify(name string, data ...interface{}) {
notification := EventNotify{
Name: name,
Data: data,
}
payload, err := json.Marshal(notification)
if err != nil {
f.logger.Error(err.Error())
return
}
2022-01-31 21:22:17 +11:00
f.mainWindow.ExecJS(`window.wails.EventsNotify('` + template.JSEscapeString(string(payload)) + `');`)
2021-11-13 17:06:48 -08:00
}
var edgeMap = map[string]uintptr{
"n-resize": C.GDK_WINDOW_EDGE_NORTH,
"ne-resize": C.GDK_WINDOW_EDGE_NORTH_EAST,
"e-resize": C.GDK_WINDOW_EDGE_EAST,
"se-resize": C.GDK_WINDOW_EDGE_SOUTH_EAST,
"s-resize": C.GDK_WINDOW_EDGE_SOUTH,
"sw-resize": C.GDK_WINDOW_EDGE_SOUTH_WEST,
"w-resize": C.GDK_WINDOW_EDGE_WEST,
"nw-resize": C.GDK_WINDOW_EDGE_NORTH_WEST,
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) processMessage(message string) {
if message == "DomReady" {
if f.frontendOptions.OnDomReady != nil {
f.frontendOptions.OnDomReady(f.ctx)
}
return
}
2021-11-13 17:06:48 -08:00
if message == "drag" {
2022-01-29 11:41:50 +11:00
if !f.mainWindow.IsFullScreen() {
f.startDrag()
}
2021-11-13 17:06:48 -08:00
return
}
2023-09-19 05:56:14 +10:00
if message == "wails:showInspector" {
f.mainWindow.ShowInspector()
return
}
if strings.HasPrefix(message, "resize:") {
if !f.mainWindow.IsFullScreen() {
sl := strings.Split(message, ":")
if len(sl) != 2 {
f.logger.Info("Unknown message returned from dispatcher: %+v", message)
return
}
edge := edgeMap[sl[1]]
err := f.startResize(edge)
if err != nil {
f.logger.Error(err.Error())
}
}
return
}
if message == "runtime:ready" {
cmd := fmt.Sprintf(
"window.wails.setCSSDragProperties('%s', '%s');\n"+
"window.wails.flags.deferDragToMouseMove = true;", f.frontendOptions.CSSDragProperty, f.frontendOptions.CSSDragValue)
f.ExecJS(cmd)
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
f.ExecJS("window.wails.flags.enableResize = true;")
}
return
}
go func() {
result, err := f.dispatcher.ProcessMessage(message, f)
if err != nil {
f.logger.Error(err.Error())
f.Callback(result)
return
}
if result == "" {
return
}
switch result[0] {
case 'c':
// Callback from a method call
f.Callback(result[1:])
default:
f.logger.Info("Unknown message returned from dispatcher: %+v", result)
}
}()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) Callback(message string) {
escaped, err := json.Marshal(message)
if err != nil {
panic(err)
}
f.ExecJS(`window.wails.Callback(` + string(escaped) + `);`)
2021-11-13 17:06:48 -08:00
}
2021-11-29 20:04:57 +11:00
func (f *Frontend) startDrag() {
2022-01-31 21:22:17 +11:00
f.mainWindow.StartDrag()
2021-11-13 17:06:48 -08:00
}
func (f *Frontend) startResize(edge uintptr) error {
f.mainWindow.StartResize(edge)
return nil
}
2021-11-13 17:06:48 -08:00
func (f *Frontend) ExecJS(js string) {
2022-01-31 21:22:17 +11:00
f.mainWindow.ExecJS(js)
2021-11-13 17:06:48 -08:00
}
2021-11-28 14:21:02 +11:00
var messageBuffer = make(chan string, 100)
//export processMessage
func processMessage(message *C.char) {
goMessage := C.GoString(message)
messageBuffer <- goMessage
}
2021-11-28 20:35:40 +11:00
var requestBuffer = make(chan webview.Request, 100)
2021-11-29 08:25:42 +11:00
func (f *Frontend) startRequestProcessor() {
for request := range requestBuffer {
f.assets.ServeWebViewRequest(request)
2021-11-29 08:25:42 +11:00
}
}
//export processURLRequest
func processURLRequest(request unsafe.Pointer) {
requestBuffer <- webview.NewRequest(request)
2021-11-29 08:25:42 +11:00
}
func (f *Frontend) startSecondInstanceProcessor() {
for secondInstanceData := range secondInstanceBuffer {
if f.frontendOptions.SingleInstanceLock != nil &&
f.frontendOptions.SingleInstanceLock.OnSecondInstanceLaunch != nil {
f.frontendOptions.SingleInstanceLock.OnSecondInstanceLaunch(secondInstanceData)
}
}
}