mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-04 23:23:48 +08:00
1209 lines
38 KiB
Go
1209 lines
38 KiB
Go
//go:build linux && purego
|
|
|
|
package application
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"unsafe"
|
|
|
|
"github.com/ebitengine/purego"
|
|
"github.com/wailsapp/wails/v3/internal/assetserver/webview"
|
|
"github.com/wailsapp/wails/v3/pkg/events"
|
|
)
|
|
|
|
type windowPointer uintptr
|
|
type identifier uint
|
|
type pointer uintptr
|
|
|
|
// type GSList uintptr
|
|
type GSList struct {
|
|
data pointer
|
|
next *GSList
|
|
}
|
|
|
|
type GSListPointer *GSList
|
|
|
|
const (
|
|
nilPointer pointer = 0
|
|
)
|
|
|
|
const (
|
|
GSourceRemove int = 0
|
|
|
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkwindow.h#L121
|
|
GdkHintMinSize = 1 << 1
|
|
GdkHintMaxSize = 1 << 2
|
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gdk/gdkevents.h#L512
|
|
GdkWindowStateIconified = 1 << 1
|
|
GdkWindowStateMaximized = 1 << 2
|
|
GdkWindowStateFullscreen = 1 << 4
|
|
|
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/gtkmessagedialog.h#L87
|
|
GtkButtonsNone int = 0
|
|
GtkButtonsOk = 1
|
|
GtkButtonsClose = 2
|
|
GtkButtonsCancel = 3
|
|
GtkButtonsYesNo = 4
|
|
GtkButtonsOkCancel = 5
|
|
|
|
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/gtkdialog.h#L36
|
|
GtkDialogModal = 1 << 0
|
|
GtkDialogDestroyWithParent = 1 << 1
|
|
GtkDialogUseHeaderBar = 1 << 2 // actions in header bar instead of action area
|
|
|
|
GtkOrientationVertical = 1
|
|
|
|
// enum GtkMessageType
|
|
GtkMessageInfo = 0
|
|
GtkMessageWarning = 1
|
|
GtkMessageQuestion = 2
|
|
GtkMessageError = 3
|
|
)
|
|
|
|
type GdkGeometry struct {
|
|
minWidth int32
|
|
minHeight int32
|
|
maxWidth int32
|
|
maxHeight int32
|
|
baseWidth int32
|
|
baseHeight int32
|
|
widthInc int32
|
|
heightInc int32
|
|
padding int32
|
|
minAspect float64
|
|
maxAspect float64
|
|
GdkGravity int32
|
|
}
|
|
|
|
var (
|
|
nilRadioGroup GSListPointer = nil
|
|
gtkSignalHandlers map[pointer]uint = map[pointer]uint{}
|
|
gtkSignalToMenuItem map[pointer]*MenuItem = map[pointer]*MenuItem{}
|
|
mainThreadId uint64
|
|
)
|
|
|
|
const (
|
|
// TODO: map distro => so filename - with fallback?
|
|
gtk3 = "libgtk-3.so.0"
|
|
gtk4 = "libgtk-4.so.1"
|
|
webkit4 = "libwebkit2gtk-4.1.so.0"
|
|
)
|
|
|
|
var (
|
|
gtk uintptr
|
|
gtkVersion int
|
|
webkit uintptr
|
|
|
|
// function references
|
|
gApplicationHold func(pointer)
|
|
gApplicationQuit func(pointer)
|
|
gApplicationName func() string
|
|
gApplicationRelease func(pointer)
|
|
gApplicationRun func(pointer, int, []string) int
|
|
gBytesNewStatic func(uintptr, int) uintptr
|
|
gBytesUnref func(uintptr)
|
|
gFree func(pointer)
|
|
gIdleAdd func(uintptr)
|
|
gObjectRefSink func(pointer)
|
|
gObjectUnref func(pointer)
|
|
gSignalConnectData func(pointer, string, uintptr, pointer, bool, int) int
|
|
gSignalConnectObject func(pointer, string, pointer, pointer, int) uint
|
|
gSignalHandlerBlock func(pointer, uint)
|
|
gSignalHandlerUnblock func(pointer, uint)
|
|
gThreadSelf func() uint64
|
|
|
|
// gdk functions
|
|
gdkDisplayGetMonitor func(pointer, int) pointer
|
|
gdkDisplayGetMonitorAtWindow func(pointer, pointer) pointer
|
|
gdkDisplayGetNMonitors func(pointer) int
|
|
gdkMonitorGetGeometry func(pointer, pointer) pointer
|
|
gdkMonitorGetScaleFactor func(pointer) int
|
|
gdkMonitorIsPrimary func(pointer) int
|
|
gdkPixbufNewFromBytes func(uintptr, int, int, int, int, int, int) pointer
|
|
gdkRgbaParse func(pointer, string) bool
|
|
gdkScreenGetRgbaVisual func(pointer) pointer
|
|
gdkScreenIsComposited func(pointer) int
|
|
gdkWindowGetState func(pointer) int
|
|
gdkWindowGetDisplay func(pointer) pointer
|
|
|
|
// gtk functions
|
|
gtkApplicationNew func(string, uint) pointer
|
|
gtkApplicationGetActiveWindow func(pointer) pointer
|
|
gtkApplicationGetWindows func(pointer) *GList
|
|
gtkApplicationWindowNew func(pointer) pointer
|
|
gtkBoxNew func(int, int) pointer
|
|
gtkBoxPackStart func(pointer, pointer, int, int, int)
|
|
gtkCheckMenuItemGetActive func(pointer) int
|
|
gtkCheckMenuItemNewWithLabel func(string) pointer
|
|
gtkCheckMenuItemSetActive func(pointer, int)
|
|
gtkContainerAdd func(pointer, pointer)
|
|
gtkCSSProviderLoadFromData func(pointer, string, int, pointer)
|
|
gtkCSSProviderNew func() pointer
|
|
gtkDialogAddButton func(pointer, string, int)
|
|
gtkDialogGetContentArea func(pointer) pointer
|
|
gtkDialogRun func(pointer) int
|
|
gtkDialogSetDefaultResponse func(pointer, int)
|
|
gtkDragDestSet func(pointer, uint, pointer, uint, uint)
|
|
gtkFileChooserAddFilter func(pointer, pointer)
|
|
gtkFileChooserDialogNew func(string, pointer, int, string, int, string, int, pointer) pointer
|
|
gtkFileChooserGetFilenames func(pointer) *GSList
|
|
gtkFileChooserSetAction func(pointer, int)
|
|
gtkFileChooserSetCreateFolders func(pointer, bool)
|
|
gtkFileChooserSetCurrentFolder func(pointer, string)
|
|
gtkFileChooserSetSelectMultiple func(pointer, bool)
|
|
gtkFileChooserSetShowHidden func(pointer, bool)
|
|
gtkFileFilterAddPattern func(pointer, string)
|
|
gtkFileFilterNew func() pointer
|
|
gtkFileFilterSetName func(pointer, string)
|
|
gtkImageNewFromPixbuf func(pointer) pointer
|
|
gtkMenuBarNew func() pointer
|
|
gtkMenuItemNewWithLabel func(string) pointer
|
|
gtkMenuItemSetLabel func(pointer, string)
|
|
gtkMenuItemSetSubmenu func(pointer, pointer)
|
|
gtkMenuNew func() pointer
|
|
gtkMenuShellAppend func(pointer, pointer)
|
|
gtkMessageDialogNew func(pointer, int, int, int, string) pointer
|
|
gtkRadioMenuItemGetGroup func(pointer) GSListPointer
|
|
gtkRadioMenuItemNewWithLabel func(GSListPointer, string) pointer
|
|
gtkSeparatorMenuItemNew func() pointer
|
|
gtkStyleContextAddProvider func(pointer, pointer, int)
|
|
gtkTargetEntryFree func(pointer)
|
|
gtkTargetEntryNew func(string, int, uint) pointer
|
|
gtkWidgetDestroy func(pointer)
|
|
gtkWidgetGetDisplay func(pointer) pointer
|
|
gtkWidgetGetScreen func(pointer) pointer
|
|
gtkWidgetGetStyleContext func(pointer) pointer
|
|
gtkWidgetGetWindow func(pointer) pointer
|
|
gtkWidgetHide func(pointer)
|
|
gtkWidgetIsVisible func(pointer) bool
|
|
gtkWidgetShow func(pointer)
|
|
gtkWidgetShowAll func(pointer)
|
|
gtkWidgetSetAppPaintable func(pointer, int)
|
|
gtkWidgetSetName func(pointer, string)
|
|
gtkWidgetSetSensitive func(pointer, int)
|
|
gtkWidgetSetToolTipText func(pointer, string)
|
|
gtkWidgetSetVisual func(pointer, pointer)
|
|
gtkWindowClose func(pointer)
|
|
gtkWindowFullScreen func(pointer)
|
|
gtkWindowGetPosition func(pointer, *int, *int) bool
|
|
gtkWindowGetSize func(pointer, *int, *int)
|
|
gtkWindowHasToplevelFocus func(pointer) int
|
|
gtkWindowKeepAbove func(pointer, bool)
|
|
gtkWindowMaximize func(pointer)
|
|
gtkWindowMinimize func(pointer)
|
|
gtkWindowMove func(pointer, int, int)
|
|
gtkWindowPresent func(pointer)
|
|
gtkWindowResize func(pointer, int, int)
|
|
gtkWindowSetDecorated func(pointer, int)
|
|
gtkWindowSetGeometryHints func(pointer, pointer, pointer, int)
|
|
gtkWindowSetKeepAbove func(pointer, bool)
|
|
gtkWindowSetResizable func(pointer, bool)
|
|
gtkWindowSetTitle func(pointer, string)
|
|
gtkWindowUnfullscreen func(pointer)
|
|
gtkWindowUnmaximize func(pointer)
|
|
|
|
// webkit
|
|
webkitNewWithUserContentManager func(pointer) pointer
|
|
webkitRegisterUriScheme func(pointer, string, pointer, int, int)
|
|
webkitSettingsGetEnableDeveloperExtras func(pointer) bool
|
|
webkitSettingsSetHardwareAccelerationPolicy func(pointer, int)
|
|
webkitSettingsSetEnableDeveloperExtras func(pointer, bool)
|
|
webkitSettingsSetUserAgentWithApplicationDetails func(pointer, string, string)
|
|
webkitUserContentManagerNew func() pointer
|
|
webkitUserContentManagerRegisterScriptMessageHandler func(pointer, string)
|
|
webkitWebContextGetDefault func() pointer
|
|
webkitWebViewEvaluateJS func(pointer, string, int, pointer, string, pointer, pointer, pointer)
|
|
webkitWebViewGetSettings func(pointer) pointer
|
|
webkitWebViewGetZoom func(pointer) float64
|
|
webkitWebViewLoadAlternateHTML func(pointer, string, string, *string)
|
|
webkitWebViewLoadUri func(pointer, string)
|
|
webkitWebViewSetBackgroundColor func(pointer, pointer)
|
|
webkitWebViewSetSettings func(pointer, pointer)
|
|
webkitWebViewSetZoomLevel func(pointer, float64)
|
|
)
|
|
|
|
func init() {
|
|
// needed for GTK4 to function
|
|
_ = os.Setenv("GDK_BACKEND", "x11")
|
|
var err error
|
|
|
|
// gtk, err = purego.Dlopen(gtk4, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
|
// if err == nil {
|
|
// version = 4
|
|
// return
|
|
// }
|
|
|
|
// log.Println("Failed to open GTK4: Falling back to GTK3")
|
|
|
|
gtk, err = purego.Dlopen(gtk3, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
gtkVersion = 3
|
|
|
|
webkit, err = purego.Dlopen(webkit4, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Function registration
|
|
// GLib
|
|
purego.RegisterLibFunc(&gApplicationHold, gtk, "g_application_hold")
|
|
purego.RegisterLibFunc(&gApplicationName, gtk, "g_get_application_name")
|
|
purego.RegisterLibFunc(&gApplicationQuit, gtk, "g_application_quit")
|
|
purego.RegisterLibFunc(&gApplicationRelease, gtk, "g_application_release")
|
|
purego.RegisterLibFunc(&gApplicationRun, gtk, "g_application_run")
|
|
purego.RegisterLibFunc(&gBytesNewStatic, gtk, "g_bytes_new_static")
|
|
purego.RegisterLibFunc(&gBytesUnref, gtk, "g_bytes_unref")
|
|
purego.RegisterLibFunc(&gFree, gtk, "g_free")
|
|
purego.RegisterLibFunc(&gIdleAdd, gtk, "g_idle_add")
|
|
purego.RegisterLibFunc(&gObjectRefSink, gtk, "g_object_ref_sink")
|
|
purego.RegisterLibFunc(&gObjectUnref, gtk, "g_object_unref")
|
|
purego.RegisterLibFunc(&gSignalConnectData, gtk, "g_signal_connect_data")
|
|
purego.RegisterLibFunc(&gSignalConnectObject, gtk, "g_signal_connect_object")
|
|
purego.RegisterLibFunc(&gSignalHandlerBlock, gtk, "g_signal_handler_block")
|
|
purego.RegisterLibFunc(&gSignalHandlerUnblock, gtk, "g_signal_handler_unblock")
|
|
purego.RegisterLibFunc(&gThreadSelf, gtk, "g_thread_self")
|
|
|
|
// GDK
|
|
purego.RegisterLibFunc(&gdkDisplayGetMonitor, gtk, "gdk_display_get_monitor")
|
|
purego.RegisterLibFunc(&gdkDisplayGetMonitorAtWindow, gtk, "gdk_display_get_monitor_at_window")
|
|
purego.RegisterLibFunc(&gdkDisplayGetNMonitors, gtk, "gdk_display_get_n_monitors")
|
|
purego.RegisterLibFunc(&gdkMonitorGetGeometry, gtk, "gdk_monitor_get_geometry")
|
|
purego.RegisterLibFunc(&gdkMonitorGetScaleFactor, gtk, "gdk_monitor_get_scale_factor")
|
|
purego.RegisterLibFunc(&gdkMonitorIsPrimary, gtk, "gdk_monitor_is_primary")
|
|
purego.RegisterLibFunc(&gdkPixbufNewFromBytes, gtk, "gdk_pixbuf_new_from_bytes")
|
|
purego.RegisterLibFunc(&gdkRgbaParse, gtk, "gdk_rgba_parse")
|
|
purego.RegisterLibFunc(&gdkScreenGetRgbaVisual, gtk, "gdk_screen_get_rgba_visual")
|
|
purego.RegisterLibFunc(&gdkScreenIsComposited, gtk, "gdk_screen_is_composited")
|
|
purego.RegisterLibFunc(&gdkWindowGetDisplay, gtk, "gdk_window_get_display")
|
|
purego.RegisterLibFunc(&gdkWindowGetState, gtk, "gdk_window_get_state")
|
|
|
|
// GTK3
|
|
purego.RegisterLibFunc(>kApplicationNew, gtk, "gtk_application_new")
|
|
purego.RegisterLibFunc(>kApplicationGetActiveWindow, gtk, "gtk_application_get_active_window")
|
|
purego.RegisterLibFunc(>kApplicationGetWindows, gtk, "gtk_application_get_windows")
|
|
purego.RegisterLibFunc(>kApplicationWindowNew, gtk, "gtk_application_window_new")
|
|
purego.RegisterLibFunc(>kBoxNew, gtk, "gtk_box_new")
|
|
purego.RegisterLibFunc(>kBoxPackStart, gtk, "gtk_box_pack_start")
|
|
purego.RegisterLibFunc(>kCheckMenuItemGetActive, gtk, "gtk_check_menu_item_get_active")
|
|
purego.RegisterLibFunc(>kCheckMenuItemNewWithLabel, gtk, "gtk_check_menu_item_new_with_label")
|
|
purego.RegisterLibFunc(>kCheckMenuItemSetActive, gtk, "gtk_check_menu_item_set_active")
|
|
purego.RegisterLibFunc(>kContainerAdd, gtk, "gtk_container_add")
|
|
purego.RegisterLibFunc(>kCSSProviderLoadFromData, gtk, "gtk_css_provider_load_from_data")
|
|
purego.RegisterLibFunc(>kDialogAddButton, gtk, "gtk_dialog_add_button")
|
|
purego.RegisterLibFunc(>kDialogGetContentArea, gtk, "gtk_dialog_get_content_area")
|
|
purego.RegisterLibFunc(>kDialogRun, gtk, "gtk_dialog_run")
|
|
purego.RegisterLibFunc(>kDialogSetDefaultResponse, gtk, "gtk_dialog_set_default_response")
|
|
purego.RegisterLibFunc(>kDragDestSet, gtk, "gtk_drag_dest_set")
|
|
purego.RegisterLibFunc(>kFileChooserAddFilter, gtk, "gtk_file_chooser_add_filter")
|
|
purego.RegisterLibFunc(>kFileChooserDialogNew, gtk, "gtk_file_chooser_dialog_new")
|
|
purego.RegisterLibFunc(>kFileChooserGetFilenames, gtk, "gtk_file_chooser_get_filenames")
|
|
purego.RegisterLibFunc(>kFileChooserSetAction, gtk, "gtk_file_chooser_set_action")
|
|
purego.RegisterLibFunc(>kFileChooserSetCreateFolders, gtk, "gtk_file_chooser_set_create_folders")
|
|
purego.RegisterLibFunc(>kFileChooserSetCurrentFolder, gtk, "gtk_file_chooser_set_current_folder")
|
|
purego.RegisterLibFunc(>kFileChooserSetSelectMultiple, gtk, "gtk_file_chooser_set_select_multiple")
|
|
purego.RegisterLibFunc(>kFileChooserSetShowHidden, gtk, "gtk_file_chooser_set_show_hidden")
|
|
purego.RegisterLibFunc(>kFileFilterAddPattern, gtk, "gtk_file_filter_add_pattern")
|
|
purego.RegisterLibFunc(>kFileFilterNew, gtk, "gtk_file_filter_new")
|
|
purego.RegisterLibFunc(>kFileFilterSetName, gtk, "gtk_file_filter_set_name")
|
|
purego.RegisterLibFunc(>kImageNewFromPixbuf, gtk, "gtk_image_new_from_pixbuf")
|
|
purego.RegisterLibFunc(>kMenuItemSetLabel, gtk, "gtk_menu_item_set_label")
|
|
purego.RegisterLibFunc(>kMenuBarNew, gtk, "gtk_menu_bar_new")
|
|
purego.RegisterLibFunc(>kMenuItemNewWithLabel, gtk, "gtk_menu_item_new_with_label")
|
|
purego.RegisterLibFunc(>kMenuItemSetSubmenu, gtk, "gtk_menu_item_set_submenu")
|
|
purego.RegisterLibFunc(>kMenuNew, gtk, "gtk_menu_new")
|
|
purego.RegisterLibFunc(>kMenuShellAppend, gtk, "gtk_menu_shell_append")
|
|
purego.RegisterLibFunc(>kMessageDialogNew, gtk, "gtk_message_dialog_new")
|
|
purego.RegisterLibFunc(>kRadioMenuItemGetGroup, gtk, "gtk_radio_menu_item_get_group")
|
|
purego.RegisterLibFunc(>kRadioMenuItemNewWithLabel, gtk, "gtk_radio_menu_item_new_with_label")
|
|
purego.RegisterLibFunc(>kSeparatorMenuItemNew, gtk, "gtk_separator_menu_item_new")
|
|
purego.RegisterLibFunc(>kStyleContextAddProvider, gtk, "gtk_style_context_add_provider")
|
|
purego.RegisterLibFunc(>kTargetEntryFree, gtk, "gtk_target_entry_free")
|
|
purego.RegisterLibFunc(>kTargetEntryNew, gtk, "gtk_target_entry_new")
|
|
purego.RegisterLibFunc(>kWidgetDestroy, gtk, "gtk_widget_destroy")
|
|
purego.RegisterLibFunc(>kWidgetGetDisplay, gtk, "gtk_widget_get_display")
|
|
purego.RegisterLibFunc(>kWidgetGetScreen, gtk, "gtk_widget_get_screen")
|
|
purego.RegisterLibFunc(>kWidgetGetStyleContext, gtk, "gtk_widget_get_style_context")
|
|
purego.RegisterLibFunc(>kWidgetGetWindow, gtk, "gtk_widget_get_window")
|
|
purego.RegisterLibFunc(>kWidgetHide, gtk, "gtk_widget_hide")
|
|
purego.RegisterLibFunc(>kWidgetIsVisible, gtk, "gtk_widget_is_visible")
|
|
purego.RegisterLibFunc(>kWidgetSetAppPaintable, gtk, "gtk_widget_set_app_paintable")
|
|
purego.RegisterLibFunc(>kWidgetSetName, gtk, "gtk_widget_set_name")
|
|
purego.RegisterLibFunc(>kWidgetSetSensitive, gtk, "gtk_widget_set_sensitive")
|
|
purego.RegisterLibFunc(>kWidgetSetToolTipText, gtk, "gtk_widget_set_tooltip_text")
|
|
purego.RegisterLibFunc(>kWidgetSetVisual, gtk, "gtk_widget_set_visual")
|
|
purego.RegisterLibFunc(>kWidgetShow, gtk, "gtk_widget_show")
|
|
purego.RegisterLibFunc(>kWidgetShowAll, gtk, "gtk_widget_show_all")
|
|
purego.RegisterLibFunc(>kWindowFullScreen, gtk, "gtk_window_fullscreen")
|
|
purego.RegisterLibFunc(>kWindowClose, gtk, "gtk_window_close")
|
|
purego.RegisterLibFunc(>kWindowGetPosition, gtk, "gtk_window_get_position")
|
|
purego.RegisterLibFunc(>kWindowGetSize, gtk, "gtk_window_get_size")
|
|
purego.RegisterLibFunc(>kWindowMaximize, gtk, "gtk_window_maximize")
|
|
purego.RegisterLibFunc(>kWindowMove, gtk, "gtk_window_move")
|
|
purego.RegisterLibFunc(>kWindowPresent, gtk, "gtk_window_present")
|
|
//purego.RegisterLibFunc(>kWindowPresent, gtk, "gtk_window_unminimize") // gtk4
|
|
purego.RegisterLibFunc(>kWindowHasToplevelFocus, gtk, "gtk_window_has_toplevel_focus")
|
|
purego.RegisterLibFunc(>kWindowMinimize, gtk, "gtk_window_iconify") // gtk3
|
|
// purego.RegisterLibFunc(>kWindowMinimize, gtk, "gtk_window_minimize") // gtk4
|
|
purego.RegisterLibFunc(>kWindowResize, gtk, "gtk_window_resize")
|
|
purego.RegisterLibFunc(>kWindowSetGeometryHints, gtk, "gtk_window_set_geometry_hints")
|
|
purego.RegisterLibFunc(>kWindowSetDecorated, gtk, "gtk_window_set_decorated")
|
|
purego.RegisterLibFunc(>kWindowKeepAbove, gtk, "gtk_window_set_keep_above")
|
|
purego.RegisterLibFunc(>kWindowSetResizable, gtk, "gtk_window_set_resizable")
|
|
purego.RegisterLibFunc(>kWindowSetTitle, gtk, "gtk_window_set_title")
|
|
purego.RegisterLibFunc(>kWindowUnfullscreen, gtk, "gtk_window_unfullscreen")
|
|
purego.RegisterLibFunc(>kWindowUnmaximize, gtk, "gtk_window_unmaximize")
|
|
|
|
// webkit
|
|
purego.RegisterLibFunc(&webkitNewWithUserContentManager, webkit, "webkit_web_view_new_with_user_content_manager")
|
|
purego.RegisterLibFunc(&webkitRegisterUriScheme, webkit, "webkit_web_context_register_uri_scheme")
|
|
purego.RegisterLibFunc(&webkitSettingsGetEnableDeveloperExtras, webkit, "webkit_settings_get_enable_developer_extras")
|
|
purego.RegisterLibFunc(&webkitSettingsSetEnableDeveloperExtras, webkit, "webkit_settings_set_enable_developer_extras")
|
|
purego.RegisterLibFunc(&webkitSettingsSetHardwareAccelerationPolicy, webkit, "webkit_settings_set_hardware_acceleration_policy")
|
|
purego.RegisterLibFunc(&webkitSettingsSetUserAgentWithApplicationDetails, webkit, "webkit_settings_set_user_agent_with_application_details")
|
|
purego.RegisterLibFunc(&webkitUserContentManagerNew, webkit, "webkit_user_content_manager_new")
|
|
purego.RegisterLibFunc(&webkitUserContentManagerRegisterScriptMessageHandler, webkit, "webkit_user_content_manager_register_script_message_handler")
|
|
purego.RegisterLibFunc(&webkitWebContextGetDefault, webkit, "webkit_web_context_get_default")
|
|
purego.RegisterLibFunc(&webkitWebViewEvaluateJS, webkit, "webkit_web_view_evaluate_javascript")
|
|
purego.RegisterLibFunc(&webkitWebViewGetSettings, webkit, "webkit_web_view_get_settings")
|
|
purego.RegisterLibFunc(&webkitWebViewGetZoom, webkit, "webkit_web_view_get_zoom_level")
|
|
purego.RegisterLibFunc(&webkitWebViewLoadAlternateHTML, webkit, "webkit_web_view_load_alternate_html")
|
|
purego.RegisterLibFunc(&webkitWebViewLoadUri, webkit, "webkit_web_view_load_uri")
|
|
purego.RegisterLibFunc(&webkitWebViewSetBackgroundColor, webkit, "webkit_web_view_set_background_color")
|
|
purego.RegisterLibFunc(&webkitWebViewSetSettings, webkit, "webkit_web_view_set_settings")
|
|
purego.RegisterLibFunc(&webkitWebViewSetZoomLevel, webkit, "webkit_web_view_set_zoom_level")
|
|
}
|
|
|
|
// mainthread stuff
|
|
func dispatchOnMainThread(id uint) {
|
|
gIdleAdd(purego.NewCallback(func(pointer) int {
|
|
executeOnMainThread(id)
|
|
return GSourceRemove
|
|
}))
|
|
}
|
|
|
|
// implementation below
|
|
func appName() string {
|
|
return gApplicationName()
|
|
}
|
|
|
|
func appNew(name string) pointer {
|
|
GApplicationDefaultFlags := uint(0)
|
|
|
|
name = strings.ToLower(name)
|
|
if name == "" {
|
|
name = "undefined"
|
|
}
|
|
identifier := fmt.Sprintf("org.wails.%s", strings.Replace(name, " ", "-", -1))
|
|
|
|
return pointer(gtkApplicationNew(identifier, GApplicationDefaultFlags))
|
|
}
|
|
|
|
func appRun(application pointer) error {
|
|
mainThreadId = gThreadSelf()
|
|
fmt.Println("linux_purego: appRun threadID", mainThreadId)
|
|
|
|
app := pointer(application)
|
|
activate := func() {
|
|
// TODO: Do we care?
|
|
fmt.Println("linux.activated!")
|
|
gApplicationHold(app) // allow running without a window
|
|
}
|
|
gSignalConnectData(
|
|
application,
|
|
"activate",
|
|
purego.NewCallback(activate),
|
|
app,
|
|
false,
|
|
0)
|
|
|
|
status := gApplicationRun(app, 0, nil)
|
|
gApplicationRelease(app)
|
|
gObjectUnref(app)
|
|
|
|
var err error
|
|
if status != 0 {
|
|
err = fmt.Errorf("exit code: %d", status)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func appDestroy(application pointer) {
|
|
gApplicationQuit(pointer(application))
|
|
}
|
|
|
|
func getCurrentWindowID(application pointer, windows map[windowPointer]uint) uint {
|
|
// TODO: Add extra metadata to window and use it!
|
|
window := gtkApplicationGetActiveWindow(pointer(application))
|
|
identifier, ok := windows[windowPointer(window)]
|
|
if ok {
|
|
return identifier
|
|
}
|
|
// FIXME: Should we panic here if not found?
|
|
return uint(1)
|
|
}
|
|
|
|
type GList struct {
|
|
data pointer
|
|
next *GList
|
|
prev *GList
|
|
}
|
|
|
|
func getWindows(application pointer) []pointer {
|
|
result := []pointer{}
|
|
windows := gtkApplicationGetWindows(pointer(application))
|
|
// FIXME: Need to make a struct here to deal with response data
|
|
for {
|
|
result = append(result, pointer(windows.data))
|
|
windows = windows.next
|
|
if windows == nil {
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
|
|
func hideAllWindows(application pointer) {
|
|
for _, window := range getWindows(application) {
|
|
gtkWidgetHide(window)
|
|
}
|
|
}
|
|
|
|
func showAllWindows(application pointer) {
|
|
for _, window := range getWindows(application) {
|
|
gtkWidgetShowAll(window)
|
|
}
|
|
}
|
|
|
|
// Menu
|
|
func menuAddSeparator(menu *Menu) {
|
|
gtkMenuShellAppend(
|
|
pointer((menu.impl).(*linuxMenu).native),
|
|
gtkSeparatorMenuItemNew())
|
|
}
|
|
|
|
func menuAppend(parent *Menu, menu *MenuItem) {
|
|
// TODO: override this with the GTK4 version if needed - possibly rename to imply it's an alias
|
|
gtkMenuShellAppend(
|
|
pointer((parent.impl).(*linuxMenu).native),
|
|
pointer((menu.impl).(*linuxMenuItem).native))
|
|
|
|
/* gtk4
|
|
C.gtk_menu_item_set_submenu(
|
|
(*C.struct__GtkMenuItem)((menu.impl).(*linuxMenuItem).native),
|
|
(*C.struct__GtkWidget)((parent.impl).(*linuxMenu).native),
|
|
)
|
|
*/
|
|
}
|
|
|
|
func menuBarNew() pointer {
|
|
return pointer(gtkMenuBarNew())
|
|
}
|
|
|
|
func menuNew() pointer {
|
|
return pointer(gtkMenuNew())
|
|
}
|
|
|
|
func menuSetSubmenu(item *MenuItem, menu *Menu) {
|
|
// FIXME: How is this different than `menuAppend` above?
|
|
gtkMenuItemSetSubmenu(
|
|
pointer((item.impl).(*linuxMenuItem).native),
|
|
pointer((menu.impl).(*linuxMenu).native))
|
|
}
|
|
|
|
func menuGetRadioGroup(item *linuxMenuItem) *GSList {
|
|
return (*GSList)(gtkRadioMenuItemGetGroup(pointer(item.native)))
|
|
}
|
|
|
|
func attachMenuHandler(item *MenuItem) {
|
|
handleClick := func() {
|
|
item := item
|
|
switch item.itemType {
|
|
case text, checkbox:
|
|
menuItemClicked <- item.id
|
|
case radio:
|
|
menuItem := (item.impl).(*linuxMenuItem)
|
|
if menuItem.isChecked() {
|
|
menuItemClicked <- item.id
|
|
}
|
|
}
|
|
}
|
|
|
|
impl := (item.impl).(*linuxMenuItem)
|
|
widget := impl.native
|
|
flags := 0
|
|
handlerId := gSignalConnectObject(
|
|
pointer(widget),
|
|
"activate",
|
|
pointer(purego.NewCallback(handleClick)),
|
|
pointer(widget),
|
|
flags)
|
|
impl.handlerId = uint(handlerId)
|
|
}
|
|
|
|
// menuItem
|
|
func menuItemChecked(widget pointer) bool {
|
|
if gtkCheckMenuItemGetActive(widget) == 1 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func menuItemNew(label string) pointer {
|
|
return pointer(gtkMenuItemNewWithLabel(label))
|
|
}
|
|
|
|
func menuCheckItemNew(label string) pointer {
|
|
return pointer(gtkCheckMenuItemNewWithLabel(label))
|
|
}
|
|
|
|
func menuItemSetChecked(widget pointer, checked bool) {
|
|
value := 0
|
|
if checked {
|
|
value = 1
|
|
}
|
|
gtkCheckMenuItemSetActive(pointer(widget), value)
|
|
}
|
|
|
|
func menuItemSetDisabled(widget pointer, disabled bool) {
|
|
value := 1
|
|
if disabled {
|
|
value = 0
|
|
}
|
|
gtkWidgetSetSensitive(widget, value)
|
|
}
|
|
|
|
func menuItemSetLabel(widget pointer, label string) {
|
|
gtkMenuItemSetLabel(
|
|
pointer(widget),
|
|
label)
|
|
}
|
|
|
|
func menuItemSetToolTip(widget pointer, tooltip string) {
|
|
gtkWidgetSetToolTipText(
|
|
pointer(widget),
|
|
tooltip)
|
|
}
|
|
|
|
func menuItemSignalBlock(widget pointer, handlerId uint, block bool) {
|
|
if block {
|
|
gSignalHandlerBlock(widget, handlerId)
|
|
} else {
|
|
gSignalHandlerUnblock(widget, handlerId)
|
|
}
|
|
}
|
|
|
|
func menuRadioItemNew(group GSListPointer, label string) pointer {
|
|
return pointer(gtkRadioMenuItemNewWithLabel(group, label))
|
|
}
|
|
|
|
// screen related
|
|
|
|
func getScreenByIndex(display pointer, index int) *Screen {
|
|
monitor := gdkDisplayGetMonitor(display, index)
|
|
// TODO: Do we need to update Screen to contain current info?
|
|
// currentMonitor := C.gdk_display_get_monitor_at_window(display, window)
|
|
|
|
geometry := struct {
|
|
x int32
|
|
y int32
|
|
width int32
|
|
height int32
|
|
}{}
|
|
result := pointer(unsafe.Pointer(&geometry))
|
|
gdkMonitorGetGeometry(monitor, result)
|
|
|
|
primary := false
|
|
if gdkMonitorIsPrimary(monitor) == 1 {
|
|
primary = true
|
|
}
|
|
|
|
return &Screen{
|
|
IsPrimary: primary,
|
|
Scale: 1.0,
|
|
X: int(geometry.x),
|
|
Y: int(geometry.y),
|
|
Size: Size{
|
|
Height: int(geometry.height),
|
|
Width: int(geometry.width),
|
|
},
|
|
}
|
|
}
|
|
|
|
func getScreens(app pointer) ([]*Screen, error) {
|
|
var screens []*Screen
|
|
window := gtkApplicationGetActiveWindow(app)
|
|
display := gdkWindowGetDisplay(window)
|
|
count := gdkDisplayGetNMonitors(display)
|
|
for i := 0; i < int(count); i++ {
|
|
screens = append(screens, getScreenByIndex(display, i))
|
|
}
|
|
return screens, nil
|
|
}
|
|
|
|
// widgets
|
|
func widgetSetSensitive(widget pointer, enabled bool) {
|
|
value := 0
|
|
if enabled {
|
|
value = 1
|
|
}
|
|
gtkWidgetSetSensitive(widget, value)
|
|
}
|
|
|
|
func widgetSetVisible(widget pointer, hidden bool) {
|
|
if hidden {
|
|
gtkWidgetHide(widget)
|
|
} else {
|
|
gtkWidgetShow(widget)
|
|
}
|
|
}
|
|
|
|
// window related functions
|
|
func windowClose(window pointer) {
|
|
gtkWindowClose(window)
|
|
}
|
|
|
|
func windowEnableDND(id uint, webview pointer) {
|
|
targetentry := gtkTargetEntryNew("text/uri-list", 0, id)
|
|
defer gtkTargetEntryFree(targetentry)
|
|
|
|
GtkDestDefaultDrop := uint(0)
|
|
GdkActionCopy := uint(0) //?
|
|
gtkDragDestSet(webview, GtkDestDefaultDrop, targetentry, 1, GdkActionCopy)
|
|
|
|
// FIXME: enable and process
|
|
/* gSignalConnectData(webview,
|
|
"drag-data-received",
|
|
purego.NewCallback(onDragNDrop),
|
|
0,
|
|
false,
|
|
0)*/
|
|
}
|
|
|
|
func windowExecJS(webview pointer, js string) {
|
|
webkitWebViewEvaluateJS(
|
|
webview,
|
|
js,
|
|
len(js),
|
|
0,
|
|
"",
|
|
0,
|
|
0,
|
|
0)
|
|
}
|
|
|
|
func windowDestroy(window pointer) {
|
|
// Should this truly 'destroy' ?
|
|
gtkWindowClose(window)
|
|
}
|
|
|
|
func windowFullscreen(window pointer) {
|
|
gtkWindowFullScreen(window)
|
|
}
|
|
|
|
func windowGetAbsolutePosition(window pointer) (int, int) {
|
|
var x, y int
|
|
gtkWindowGetPosition(window, &x, &y)
|
|
return x, y
|
|
}
|
|
|
|
func windowGetCurrentMonitor(window pointer) pointer {
|
|
// Get the monitor that the window is currently on
|
|
display := gtkWidgetGetDisplay(window)
|
|
window = gtkWidgetGetWindow(window)
|
|
if window == 0 {
|
|
return 0
|
|
}
|
|
return gdkDisplayGetMonitorAtWindow(display, window)
|
|
}
|
|
|
|
func windowGetCurrentMonitorGeometry(window pointer) (x int, y int, width int, height int, scale int) {
|
|
monitor := windowGetCurrentMonitor(window)
|
|
if monitor == 0 {
|
|
return -1, -1, -1, -1, 1
|
|
}
|
|
|
|
result := struct {
|
|
x int32
|
|
y int32
|
|
width int32
|
|
height int32
|
|
}{}
|
|
gdkMonitorGetGeometry(monitor, pointer(unsafe.Pointer(&result)))
|
|
return int(result.x), int(result.y), int(result.width), int(result.height), gdkMonitorGetScaleFactor(monitor)
|
|
}
|
|
|
|
func windowGetRelativePosition(window pointer) (int, int) {
|
|
absX, absY := windowGetAbsolutePosition(window)
|
|
x, y, _, _, _ := windowGetCurrentMonitorGeometry(window)
|
|
|
|
relX := absX - x
|
|
relY := absY - y
|
|
|
|
// TODO: Scale based on DPI
|
|
return relX, relY
|
|
}
|
|
|
|
func windowGetSize(window pointer) (int, int) {
|
|
// TODO: dispatchOnMainThread?
|
|
var width, height int
|
|
gtkWindowGetSize(window, &width, &height)
|
|
return width, height
|
|
}
|
|
|
|
func windowGetPosition(window pointer) (int, int) {
|
|
// TODO: dispatchOnMainThread?
|
|
var x, y int
|
|
gtkWindowGetPosition(window, &x, &y)
|
|
return x, y
|
|
}
|
|
|
|
func windowHide(window pointer) {
|
|
gtkWidgetHide(window)
|
|
}
|
|
|
|
func windowIsFocused(window pointer) bool {
|
|
return gtkWindowHasToplevelFocus(window) == 1
|
|
}
|
|
|
|
func windowIsFullscreen(window pointer) bool {
|
|
gdkwindow := gtkWidgetGetWindow(window)
|
|
state := gdkWindowGetState(gdkwindow)
|
|
return state&GdkWindowStateFullscreen > 0
|
|
}
|
|
|
|
func windowIsMaximized(window pointer) bool {
|
|
gdkwindow := gtkWidgetGetWindow(window)
|
|
state := gdkWindowGetState(gdkwindow)
|
|
return state&GdkWindowStateMaximized > 0 && state&GdkWindowStateFullscreen == 0
|
|
}
|
|
|
|
func windowIsMinimized(window pointer) bool {
|
|
gdkwindow := gtkWidgetGetWindow(window)
|
|
state := gdkWindowGetState(gdkwindow)
|
|
return state&GdkWindowStateIconified > 0
|
|
}
|
|
|
|
func windowIsVisible(window pointer) bool {
|
|
// TODO: validate this works.. (used a `bool` in the registration)
|
|
return gtkWidgetIsVisible(window)
|
|
}
|
|
|
|
func windowMaximize(window pointer) {
|
|
gtkWindowMaximize(window)
|
|
}
|
|
|
|
func windowMinimize(window pointer) {
|
|
gtkWindowMinimize(window)
|
|
}
|
|
|
|
func windowNew(application pointer, menu pointer, windowId uint, gpuPolicy int) (pointer, pointer, pointer) {
|
|
window := gtkApplicationWindowNew(application)
|
|
gObjectRefSink(window)
|
|
webview := windowNewWebview(windowId, gpuPolicy)
|
|
vbox := gtkBoxNew(GtkOrientationVertical, 0)
|
|
gtkContainerAdd(window, vbox)
|
|
gtkWidgetSetName(vbox, "webview-box")
|
|
|
|
if menu != 0 {
|
|
gtkBoxPackStart(vbox, menu, 0, 0, 0)
|
|
}
|
|
gtkBoxPackStart(vbox, webview, 1, 1, 0)
|
|
return pointer(window), pointer(webview), pointer(vbox)
|
|
}
|
|
|
|
func windowNewWebview(parentId uint, gpuPolicy int) pointer {
|
|
manager := webkitUserContentManagerNew()
|
|
webkitUserContentManagerRegisterScriptMessageHandler(manager, "external")
|
|
wv := webkitNewWithUserContentManager(manager)
|
|
if !registered {
|
|
webkitRegisterUriScheme(
|
|
webkitWebContextGetDefault(),
|
|
"wails",
|
|
pointer(purego.NewCallback(func(request uintptr) {
|
|
webviewRequests <- &webViewAssetRequest{
|
|
Request: webview.NewRequest(request),
|
|
windowId: parentId,
|
|
windowName: globalApplication.getWindowForID(parentId).Name(),
|
|
}
|
|
})),
|
|
0,
|
|
0,
|
|
)
|
|
registered = true
|
|
}
|
|
|
|
settings := webkitWebViewGetSettings(wv)
|
|
webkitSettingsSetUserAgentWithApplicationDetails(
|
|
settings,
|
|
"wails.io",
|
|
"")
|
|
webkitSettingsSetHardwareAccelerationPolicy(settings, gpuPolicy)
|
|
webkitWebViewSetSettings(wv, settings)
|
|
return wv
|
|
}
|
|
|
|
func windowPresent(window pointer) {
|
|
gtkWindowPresent(pointer(window))
|
|
}
|
|
|
|
func windowReload(webview pointer, address string) {
|
|
webkitWebViewLoadUri(pointer(webview), address)
|
|
}
|
|
|
|
func windowResize(window pointer, width, height int) {
|
|
gtkWindowResize(window, width, height)
|
|
}
|
|
|
|
func windowShow(window pointer) {
|
|
gtkWidgetShowAll(pointer(window))
|
|
}
|
|
|
|
func windowSetBackgroundColour(vbox, webview pointer, colour RGBA) {
|
|
const GtkStyleProviderPriorityUser = 800
|
|
|
|
// FIXME: Use a struct!
|
|
rgba := make([]byte, 4*8) // C.sizeof_GdkRGBA == 32
|
|
rgbaPointer := pointer(unsafe.Pointer(&rgba[0]))
|
|
if !gdkRgbaParse(
|
|
rgbaPointer,
|
|
fmt.Sprintf("rgba(%v,%v,%v,%v)",
|
|
colour.Red,
|
|
colour.Green,
|
|
colour.Blue,
|
|
float32(colour.Alpha)/255.0,
|
|
)) {
|
|
return
|
|
}
|
|
webkitWebViewSetBackgroundColor(pointer(webview), rgbaPointer)
|
|
|
|
colour.Alpha = 255
|
|
css := fmt.Sprintf("#webview-box {background-color: rgba(%d, %d, %d, %1.1f);}", colour.Red, colour.Green, colour.Blue, float32(colour.Alpha)/255.0)
|
|
provider := gtkCSSProviderNew()
|
|
defer gObjectUnref(provider)
|
|
gtkStyleContextAddProvider(
|
|
gtkWidgetGetStyleContext(vbox),
|
|
provider,
|
|
GtkStyleProviderPriorityUser,
|
|
)
|
|
gtkCSSProviderLoadFromData(provider, css, -1, 0)
|
|
}
|
|
|
|
func windowSetGeometryHints(window pointer, minWidth, minHeight, maxWidth, maxHeight int) {
|
|
size := GdkGeometry{
|
|
minWidth: int32(minWidth),
|
|
minHeight: int32(minHeight),
|
|
maxWidth: int32(maxWidth),
|
|
maxHeight: int32(maxHeight),
|
|
}
|
|
gtkWindowSetGeometryHints(
|
|
pointer(window),
|
|
pointer(0),
|
|
pointer(unsafe.Pointer(&size)),
|
|
GdkHintMinSize|GdkHintMaxSize)
|
|
}
|
|
|
|
func windowSetFrameless(window pointer, frameless bool) {
|
|
decorated := 1
|
|
if frameless {
|
|
decorated = 0
|
|
}
|
|
gtkWindowSetDecorated(pointer(window), decorated)
|
|
|
|
// TODO: Deal with transparency for the titlebar if possible when !frameless
|
|
// Perhaps we just make it undecorated and add a menu bar inside?
|
|
}
|
|
|
|
// TODO: confirm this is working properly
|
|
func windowSetHTML(webview pointer, html string) {
|
|
webkitWebViewLoadAlternateHTML(webview, html, "wails://", nil)
|
|
}
|
|
|
|
func windowSetKeepAbove(window pointer, alwaysOnTop bool) {
|
|
gtkWindowKeepAbove(window, alwaysOnTop)
|
|
}
|
|
|
|
func windowSetResizable(window pointer, resizable bool) {
|
|
// FIXME: Does this work?
|
|
gtkWindowSetResizable(
|
|
pointer(window),
|
|
resizable,
|
|
)
|
|
}
|
|
|
|
func windowSetTitle(window pointer, title string) {
|
|
gtkWindowSetTitle(pointer(window), title)
|
|
}
|
|
|
|
func windowSetTransparent(window pointer) {
|
|
screen := gtkWidgetGetScreen(pointer(window))
|
|
visual := gdkScreenGetRgbaVisual(screen)
|
|
if visual == 0 {
|
|
return
|
|
}
|
|
if gdkScreenIsComposited(screen) == 1 {
|
|
gtkWidgetSetAppPaintable(pointer(window), 1)
|
|
gtkWidgetSetVisual(pointer(window), visual)
|
|
}
|
|
}
|
|
|
|
func windowSetURL(webview pointer, uri string) {
|
|
webkitWebViewLoadUri(webview, uri)
|
|
}
|
|
|
|
func windowSetupSignalHandlers(windowId uint, window, webview pointer, emit func(e events.WindowEventType)) {
|
|
handleDelete := purego.NewCallback(func(pointer) {
|
|
emit(events.Common.WindowClosing)
|
|
})
|
|
gSignalConnectData(window, "delete-event", handleDelete, 0, false, 0)
|
|
|
|
/*
|
|
event = C.CString("load-changed")
|
|
defer C.free(unsafe.Pointer(event))
|
|
C.signal_connect(webview, event, C.webviewLoadChanged, unsafe.Pointer(&w.parent.id))
|
|
*/
|
|
|
|
// TODO: Handle mouse button / drag events
|
|
/* id := C.uint(windowId)
|
|
event = C.CString("button-press-event")
|
|
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(webview)), event, C.onButtonEvent, unsafe.Pointer(&id))
|
|
C.free(unsafe.Pointer(event))
|
|
event = C.CString("button-release-event")
|
|
defer C.free(unsafe.Pointer(event))
|
|
C.signal_connect((*C.GtkWidget)(unsafe.Pointer(webview)), event, onButtonEvent, unsafe.Pointer(&id))
|
|
*/
|
|
}
|
|
|
|
func windowOpenDevTools(webview pointer) {
|
|
settings := webkitWebViewGetSettings(pointer(webview))
|
|
webkitSettingsSetEnableDeveloperExtras(
|
|
settings,
|
|
!webkitSettingsGetEnableDeveloperExtras(settings))
|
|
}
|
|
|
|
func windowUnfullscreen(window pointer) {
|
|
gtkWindowUnfullscreen(window)
|
|
}
|
|
|
|
func windowUnmaximize(window pointer) {
|
|
gtkWindowUnmaximize(window)
|
|
}
|
|
|
|
func windowZoom(webview pointer) float64 {
|
|
return webkitWebViewGetZoom(webview)
|
|
}
|
|
|
|
func windowZoomIn(webview pointer) {
|
|
ZoomInFactor := 1.10
|
|
windowZoomSet(webview, windowZoom(webview)*ZoomInFactor)
|
|
}
|
|
func windowZoomOut(webview pointer) {
|
|
ZoomOutFactor := -1.10
|
|
windowZoomSet(webview, windowZoom(webview)*ZoomOutFactor)
|
|
}
|
|
|
|
func windowZoomSet(webview pointer, zoom float64) {
|
|
if zoom < 1.0 { // 1.0 is the smallest allowable
|
|
zoom = 1.0
|
|
}
|
|
webkitWebViewSetZoomLevel(webview, zoom)
|
|
}
|
|
|
|
func windowMove(window pointer, x, y int) {
|
|
gtkWindowMove(window, x, y)
|
|
}
|
|
|
|
func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden bool, currentFolder, title string, action int, acceptLabel string, filters []FileFilter) ([]string, error) {
|
|
GtkResponseCancel := 0
|
|
GtkResponseAccept := 1
|
|
|
|
fc := gtkFileChooserDialogNew(
|
|
title,
|
|
window,
|
|
action,
|
|
"_Cancel",
|
|
GtkResponseCancel,
|
|
acceptLabel,
|
|
GtkResponseAccept,
|
|
0)
|
|
|
|
gtkFileChooserSetAction(fc, action)
|
|
|
|
gtkFilters := []pointer{}
|
|
for _, filter := range filters {
|
|
f := gtkFileFilterNew()
|
|
gtkFileFilterSetName(f, filter.DisplayName)
|
|
gtkFileFilterAddPattern(f, filter.Pattern)
|
|
gtkFileChooserAddFilter(fc, f)
|
|
gtkFilters = append(gtkFilters, f)
|
|
}
|
|
gtkFileChooserSetSelectMultiple(fc, allowMultiple)
|
|
gtkFileChooserSetCreateFolders(fc, createFolders)
|
|
gtkFileChooserSetShowHidden(fc, showHidden)
|
|
|
|
if currentFolder != "" {
|
|
gtkFileChooserSetCurrentFolder(fc, currentFolder)
|
|
}
|
|
|
|
buildStringAndFree := func(s pointer) string {
|
|
bytes := []byte{}
|
|
p := unsafe.Pointer(s)
|
|
for {
|
|
val := *(*byte)(p)
|
|
if val == 0 { // this is the null terminator
|
|
break
|
|
}
|
|
bytes = append(bytes, val)
|
|
p = unsafe.Add(p, 1)
|
|
}
|
|
gFree(s) // so we don't have to iterate a second time
|
|
return string(bytes)
|
|
}
|
|
|
|
response := gtkDialogRun(fc)
|
|
selections := []string{}
|
|
if response == GtkResponseAccept {
|
|
filenames := gtkFileChooserGetFilenames(fc)
|
|
iter := filenames
|
|
count := 0
|
|
for {
|
|
selections = append(selections, buildStringAndFree(iter.data))
|
|
iter = iter.next
|
|
if iter == nil || count == 1024 {
|
|
break
|
|
}
|
|
count++
|
|
}
|
|
}
|
|
defer gtkWidgetDestroy(fc)
|
|
return selections, nil
|
|
}
|
|
|
|
// dialog related
|
|
func runOpenFileDialog(dialog *OpenFileDialogStruct) ([]string, error) {
|
|
const GtkFileChooserActionOpen = 0
|
|
const GtkFileChooserActionSelectFolder = 2
|
|
|
|
var action int
|
|
|
|
if dialog.canChooseDirectories {
|
|
action = GtkFileChooserActionSelectFolder
|
|
} else {
|
|
action = GtkFileChooserActionOpen
|
|
}
|
|
|
|
window := pointer(0)
|
|
if dialog.window != nil {
|
|
window = (dialog.window.(*WebviewWindow).impl).(*linuxWebviewWindow).window
|
|
}
|
|
|
|
buttonText := dialog.buttonText
|
|
if buttonText == "" {
|
|
buttonText = "_Open"
|
|
}
|
|
|
|
return runChooserDialog(
|
|
window,
|
|
dialog.allowsMultipleSelection,
|
|
dialog.canCreateDirectories,
|
|
dialog.showHiddenFiles,
|
|
dialog.directory,
|
|
dialog.title,
|
|
GtkFileChooserActionOpen,
|
|
buttonText,
|
|
dialog.filters)
|
|
}
|
|
|
|
func runQuestionDialog(parent pointer, options *MessageDialog) int {
|
|
dType, ok := map[DialogType]int{
|
|
InfoDialogType: GtkMessageInfo,
|
|
WarningDialogType: GtkMessageWarning,
|
|
QuestionDialogType: GtkMessageQuestion,
|
|
}[options.DialogType]
|
|
if !ok {
|
|
// FIXME: Add logging here!
|
|
dType = GtkMessageInfo
|
|
}
|
|
buttonMask := GtkButtonsOk
|
|
if len(options.Buttons) > 0 {
|
|
buttonMask = GtkButtonsNone
|
|
}
|
|
|
|
dialog := gtkMessageDialogNew(
|
|
pointer(parent),
|
|
GtkDialogModal|GtkDialogDestroyWithParent,
|
|
dType,
|
|
buttonMask,
|
|
options.Message)
|
|
|
|
if options.Title != "" {
|
|
gtkWindowSetTitle(dialog, options.Title)
|
|
}
|
|
|
|
GdkColorspaceRGB := 0
|
|
|
|
if img, err := pngToImage(options.Icon); err == nil {
|
|
gbytes := gBytesNewStatic(uintptr(unsafe.Pointer(&img.Pix[0])), len(img.Pix))
|
|
|
|
defer gBytesUnref(gbytes)
|
|
pixBuf := gdkPixbufNewFromBytes(
|
|
gbytes,
|
|
GdkColorspaceRGB,
|
|
1, // has_alpha
|
|
8,
|
|
img.Bounds().Dx(),
|
|
img.Bounds().Dy(),
|
|
img.Stride,
|
|
)
|
|
image := gtkImageNewFromPixbuf(pixBuf)
|
|
widgetSetVisible(image, false)
|
|
contentArea := gtkDialogGetContentArea(dialog)
|
|
gtkContainerAdd(contentArea, image)
|
|
}
|
|
|
|
for i, button := range options.Buttons {
|
|
gtkDialogAddButton(
|
|
dialog,
|
|
button.Label,
|
|
i,
|
|
)
|
|
if button.IsDefault {
|
|
gtkDialogSetDefaultResponse(dialog, i)
|
|
}
|
|
}
|
|
defer gtkWidgetDestroy(dialog)
|
|
return gtkDialogRun(dialog)
|
|
}
|
|
|
|
func runSaveFileDialog(dialog *SaveFileDialogStruct) (string, error) {
|
|
const GtkFileChooserActionSave = 1
|
|
|
|
window := pointer(0)
|
|
buttonText := dialog.buttonText
|
|
if buttonText == "" {
|
|
buttonText = "_Save"
|
|
}
|
|
results, err := runChooserDialog(
|
|
window,
|
|
false, // multiple selection
|
|
dialog.canCreateDirectories,
|
|
dialog.showHiddenFiles,
|
|
dialog.directory,
|
|
dialog.title,
|
|
GtkFileChooserActionSave,
|
|
buttonText,
|
|
dialog.filters)
|
|
|
|
if err != nil || len(results) == 0 {
|
|
return "", err
|
|
}
|
|
|
|
return results[0], nil
|
|
}
|
|
|
|
func isOnMainThread() bool {
|
|
return mainThreadId == gThreadSelf()
|
|
}
|