mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 11:10:47 +08:00
411 lines
11 KiB
Go
411 lines
11 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package linux
|
|
|
|
/*
|
|
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
|
|
#include "gtk/gtk.h"
|
|
#include "webkit2/webkit2.h"
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
|
|
static GtkWidget* GTKWIDGET(void *pointer) {
|
|
return GTK_WIDGET(pointer);
|
|
}
|
|
|
|
static GtkWindow* GTKWINDOW(void *pointer) {
|
|
return GTK_WINDOW(pointer);
|
|
}
|
|
|
|
static void SetMinSize(GtkWindow* window, int width, int height) {
|
|
GdkGeometry size;
|
|
size.min_height = height;
|
|
size.min_width = width;
|
|
gtk_window_set_geometry_hints(window, NULL, &size, GDK_HINT_MIN_SIZE);
|
|
}
|
|
|
|
static void SetMaxSize(GtkWindow* window, int width, int height) {
|
|
GdkGeometry size;
|
|
if( width == 0 ) {
|
|
width = INT_MAX;
|
|
}
|
|
if( height == 0 ) {
|
|
height = INT_MAX;
|
|
}
|
|
|
|
size.max_height = height;
|
|
size.max_width = width;
|
|
gtk_window_set_geometry_hints(window, NULL, &size, GDK_HINT_MAX_SIZE);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void SetPosition(GtkWindow *window, int x, int y) {
|
|
GdkRectangle monitorDimensions = getCurrentMonitorGeometry(window);
|
|
gtk_window_move(window, monitorDimensions.x + x, monitorDimensions.y + y);
|
|
}
|
|
|
|
void Center(GtkWindow *window)
|
|
{
|
|
// Get the geometry of the monitor
|
|
GdkRectangle m = getCurrentMonitorGeometry(window);
|
|
|
|
// Get the window width/height
|
|
int windowWidth, windowHeight;
|
|
gtk_window_get_size(window, &windowWidth, &windowHeight);
|
|
|
|
int newX = ((m.width - windowWidth) / 2) + m.x;
|
|
int newY = ((m.height - windowHeight) / 2) + m.y;
|
|
|
|
// Place the window at the center of the monitor
|
|
gtk_window_move(window, newX, newY);
|
|
}
|
|
|
|
int IsFullscreen(GtkWidget *widget) {
|
|
GdkWindow *gdkwindow = gtk_widget_get_window(widget);
|
|
GdkWindowState state = gdk_window_get_state(GDK_WINDOW(gdkwindow));
|
|
return state & GDK_WINDOW_STATE_FULLSCREEN == GDK_WINDOW_STATE_FULLSCREEN;
|
|
}
|
|
|
|
extern void processMessage(char*);
|
|
|
|
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
|
WebKitJavascriptResult *result,
|
|
void*)
|
|
{
|
|
#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
|
|
processMessage(message);
|
|
g_free(message);
|
|
}
|
|
|
|
ulong setupInvokeSignal(void* contentManager) {
|
|
return g_signal_connect((WebKitUserContentManager*)contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), NULL);
|
|
}
|
|
|
|
// These are the x,y & time of the last mouse down event
|
|
// It's used for window dragging
|
|
float xroot = 0.0f;
|
|
float yroot = 0.0f;
|
|
int dragTime = -1;
|
|
|
|
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, void* dummy)
|
|
{
|
|
if( event == NULL ) {
|
|
xroot = yroot = 0.0f;
|
|
dragTime = -1;
|
|
return FALSE;
|
|
}
|
|
if (event->type == GDK_BUTTON_PRESS && event->button == 1)
|
|
{
|
|
xroot = event->x_root;
|
|
yroot = event->y_root;
|
|
dragTime = event->time;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, void* dummy)
|
|
{
|
|
if (event == NULL || (event->type == GDK_BUTTON_RELEASE && event->button == 1))
|
|
{
|
|
xroot = yroot = 0.0f;
|
|
dragTime = -1;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void connectButtons(void* webview) {
|
|
g_signal_connect(WEBKIT_WEB_VIEW(webview), "button-press-event", G_CALLBACK(buttonPress), NULL);
|
|
g_signal_connect(WEBKIT_WEB_VIEW(webview), "button-release-event", G_CALLBACK(buttonRelease), NULL);
|
|
}
|
|
|
|
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*)
|
|
{
|
|
processMessage("Q");
|
|
return FALSE;
|
|
}
|
|
|
|
GtkWidget* setupWebview(void* contentManager, GtkWindow* window, int hideWindowOnClose) {
|
|
GtkWidget* webview = webkit_web_view_new_with_user_content_manager((WebKitUserContentManager*)contentManager);
|
|
gtk_container_add(GTK_CONTAINER(window), webview);
|
|
WebKitWebContext *context = webkit_web_context_get_default();
|
|
webkit_web_context_register_uri_scheme(context, "wails", (WebKitURISchemeRequestCallback)processURLRequest, NULL, NULL);
|
|
//g_signal_connect(G_OBJECT(webview), "load-changed", G_CALLBACK(webview_load_changed_cb), NULL);
|
|
if (hideWindowOnClose) {
|
|
g_signal_connect(GTK_WIDGET(window), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
|
} else {
|
|
g_signal_connect(GTK_WIDGET(window), "delete-event", G_CALLBACK(close_button_pressed), NULL);
|
|
}
|
|
return webview;
|
|
}
|
|
|
|
void devtoolsEnabled(void* webview, int enabled) {
|
|
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
|
|
gboolean genabled = enabled == 1 ? true : false;
|
|
webkit_settings_set_enable_developer_extras(settings, genabled);
|
|
}
|
|
|
|
void loadIndex(void* webview) {
|
|
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), "wails:///");
|
|
}
|
|
|
|
static void startDrag(void *webview, GtkWindow* mainwindow)
|
|
{
|
|
// Ignore non-toplevel widgets
|
|
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(webview));
|
|
if (!GTK_IS_WINDOW(window)) return;
|
|
|
|
gtk_window_begin_move_drag(mainwindow, 1, xroot, yroot, dragTime);
|
|
}
|
|
|
|
*/
|
|
import "C"
|
|
import (
|
|
"github.com/wailsapp/wails/v2/pkg/options"
|
|
"unsafe"
|
|
)
|
|
|
|
func gtkBool(input bool) C.gboolean {
|
|
if input {
|
|
return C.gboolean(1)
|
|
}
|
|
return C.gboolean(0)
|
|
}
|
|
|
|
type Window struct {
|
|
appoptions *options.App
|
|
debug bool
|
|
gtkWindow unsafe.Pointer
|
|
contentManager unsafe.Pointer
|
|
webview unsafe.Pointer
|
|
}
|
|
|
|
func bool2Cint(value bool) C.int {
|
|
if value {
|
|
return C.int(1)
|
|
}
|
|
return C.int(0)
|
|
}
|
|
|
|
func NewWindow(appoptions *options.App, debug bool) *Window {
|
|
|
|
result := &Window{
|
|
appoptions: appoptions,
|
|
debug: debug,
|
|
}
|
|
|
|
gtkWindow := C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL)
|
|
C.g_object_ref_sink(C.gpointer(gtkWindow))
|
|
result.gtkWindow = unsafe.Pointer(gtkWindow)
|
|
|
|
result.contentManager = unsafe.Pointer(C.webkit_user_content_manager_new())
|
|
external := C.CString("external")
|
|
defer C.free(unsafe.Pointer(external))
|
|
C.webkit_user_content_manager_register_script_message_handler(result.cWebKitUserContentManager(), external)
|
|
C.setupInvokeSignal(result.contentManager)
|
|
|
|
webview := C.setupWebview(result.contentManager, result.asGTKWindow(), bool2Cint(appoptions.HideWindowOnClose))
|
|
result.webview = unsafe.Pointer(webview)
|
|
buttonPressedName := C.CString("button-press-event")
|
|
defer C.free(unsafe.Pointer(buttonPressedName))
|
|
C.connectButtons(unsafe.Pointer(webview))
|
|
|
|
if debug {
|
|
C.devtoolsEnabled(unsafe.Pointer(webview), C.int(1))
|
|
}
|
|
|
|
// Setup window
|
|
result.SetKeepAbove(appoptions.AlwaysOnTop)
|
|
result.SetResizable(!appoptions.DisableResize)
|
|
result.SetSize(appoptions.Width, appoptions.Height)
|
|
result.SetDecorated(!appoptions.Frameless)
|
|
result.SetTitle(appoptions.Title)
|
|
result.SetMinSize(appoptions.MinWidth, appoptions.MinHeight)
|
|
result.SetMaxSize(appoptions.MaxWidth, appoptions.MaxHeight)
|
|
|
|
return result
|
|
}
|
|
|
|
func (w *Window) asGTKWidget() *C.GtkWidget {
|
|
return C.GTKWIDGET(w.gtkWindow)
|
|
}
|
|
|
|
func (w *Window) asGTKWindow() *C.GtkWindow {
|
|
return C.GTKWINDOW(w.gtkWindow)
|
|
}
|
|
|
|
func (w *Window) cWebKitUserContentManager() *C.WebKitUserContentManager {
|
|
return (*C.WebKitUserContentManager)(w.contentManager)
|
|
}
|
|
|
|
func (w *Window) Fullscreen() {
|
|
C.gtk_window_fullscreen(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) UnFullscreen() {
|
|
C.gtk_window_unfullscreen(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) Destroy() {
|
|
C.g_object_unref(C.gpointer(w.gtkWindow))
|
|
C.gtk_widget_destroy(w.asGTKWidget())
|
|
}
|
|
|
|
func (w *Window) Close() {
|
|
C.gtk_window_close(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) Center() {
|
|
C.Center(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) SetPos(x int, y int) {
|
|
cX := C.int(x)
|
|
cY := C.int(y)
|
|
C.gtk_window_move(w.asGTKWindow(), cX, cY)
|
|
}
|
|
|
|
func (w *Window) Size() (int, int) {
|
|
var width, height C.int
|
|
C.gtk_window_get_size(w.asGTKWindow(), &width, &height)
|
|
return int(width), int(height)
|
|
}
|
|
|
|
func (w *Window) Pos() (int, int) {
|
|
var width, height C.int
|
|
C.gtk_window_get_position(w.asGTKWindow(), &width, &height)
|
|
return int(width), int(height)
|
|
}
|
|
|
|
func (w *Window) SetMaxSize(maxWidth int, maxHeight int) {
|
|
C.SetMaxSize(w.asGTKWindow(), C.int(maxWidth), C.int(maxHeight))
|
|
}
|
|
|
|
func (w *Window) SetMinSize(minWidth int, minHeight int) {
|
|
C.SetMinSize(w.asGTKWindow(), C.int(minWidth), C.int(minHeight))
|
|
}
|
|
|
|
func (w *Window) Show() {
|
|
C.gtk_widget_show(w.asGTKWidget())
|
|
}
|
|
|
|
func (w *Window) Hide() {
|
|
C.gtk_widget_hide(w.asGTKWidget())
|
|
}
|
|
|
|
func (w *Window) Maximise() {
|
|
C.gtk_window_maximize(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) UnMaximise() {
|
|
C.gtk_window_unmaximize(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) Minimise() {
|
|
C.gtk_window_iconify(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) UnMinimise() {
|
|
C.gtk_window_present(w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) IsFullScreen() bool {
|
|
result := C.IsFullscreen(w.asGTKWidget())
|
|
if result == 1 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (w *Window) SetRGBA(r uint8, g uint8, b uint8, a uint8) {
|
|
//C.SetRGBA(w.context, C.int(r), C.int(g), C.int(b), C.int(a))
|
|
}
|
|
|
|
//func (w *Window) SetApplicationMenu(inMenu *menu.Menu) {
|
|
// //mainMenu := NewNSMenu(w.context, "")
|
|
// //processMenu(mainMenu, inMenu)
|
|
// //C.SetAsApplicationMenu(w.context, mainMenu.nsmenu)
|
|
//}
|
|
|
|
func (w *Window) UpdateApplicationMenu() {
|
|
//C.UpdateApplicationMenu(w.context)
|
|
}
|
|
|
|
func (w *Window) Run() {
|
|
C.loadIndex(w.webview)
|
|
C.gtk_widget_show_all(w.asGTKWidget())
|
|
w.Center()
|
|
switch w.appoptions.WindowStartState {
|
|
case options.Fullscreen:
|
|
w.Fullscreen()
|
|
case options.Minimised:
|
|
w.Minimise()
|
|
case options.Maximised:
|
|
w.Maximise()
|
|
}
|
|
C.gtk_main()
|
|
w.Destroy()
|
|
}
|
|
|
|
func (w *Window) SetKeepAbove(top bool) {
|
|
C.gtk_window_set_keep_above(w.asGTKWindow(), gtkBool(top))
|
|
}
|
|
|
|
func (w *Window) SetResizable(resizable bool) {
|
|
C.gtk_window_set_resizable(w.asGTKWindow(), gtkBool(resizable))
|
|
}
|
|
|
|
func (w *Window) SetSize(width int, height int) {
|
|
C.gtk_window_resize(w.asGTKWindow(), C.gint(width), C.gint(height))
|
|
}
|
|
|
|
func (w *Window) SetDecorated(frameless bool) {
|
|
C.gtk_window_set_decorated(w.asGTKWindow(), gtkBool(frameless))
|
|
}
|
|
|
|
func (w *Window) SetTitle(title string) {
|
|
cTitle := C.CString(title)
|
|
defer C.free(unsafe.Pointer(cTitle))
|
|
C.gtk_window_set_title(w.asGTKWindow(), cTitle)
|
|
}
|
|
|
|
func (w *Window) ExecJS(js string) {
|
|
script := C.CString(js)
|
|
defer C.free(unsafe.Pointer(script))
|
|
C.webkit_web_view_run_javascript((*C.WebKitWebView)(w.webview), script, nil, nil, nil)
|
|
}
|
|
|
|
func (w *Window) StartDrag() {
|
|
C.startDrag(w.webview, w.asGTKWindow())
|
|
}
|
|
|
|
func (w *Window) Quit() {
|
|
C.gtk_main_quit()
|
|
}
|