5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 06:19:43 +08:00

linux separate window c and go file (#2275)

This commit is contained in:
Zámbó, Levente 2023-01-07 06:20:11 +01:00 committed by GitHub
parent ccceadbd24
commit c3011813b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 830 additions and 656 deletions

View File

@ -0,0 +1,694 @@
#include <JavaScriptCore/JavaScript.h>
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include "window.h"
// These are the x,y,time & button of the last mouse down event
// It's used for window dragging
static float xroot = 0.0f;
static float yroot = 0.0f;
static int dragTime = -1;
static uint mouseButton = 0;
// casts
void ExecuteOnMainThread(void *f, gpointer jscallback)
{
g_idle_add((GSourceFunc)f, (gpointer)jscallback);
}
GtkWidget *GTKWIDGET(void *pointer)
{
return GTK_WIDGET(pointer);
}
GtkWindow *GTKWINDOW(void *pointer)
{
return GTK_WINDOW(pointer);
}
GtkContainer *GTKCONTAINER(void *pointer)
{
return GTK_CONTAINER(pointer);
}
GtkBox *GTKBOX(void *pointer)
{
return GTK_BOX(pointer);
}
extern void processMessage(char *);
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
WebKitJavascriptResult *result,
void *data)
{
#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);
}
static bool isNULLRectangle(GdkRectangle input)
{
return input.x == -1 && input.y == -1 && input.width == -1 && input.height == -1;
}
static GdkMonitor *getCurrentMonitor(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));
if (gdk_window == NULL)
{
return NULL;
}
GdkMonitor *monitor = gdk_display_get_monitor_at_window(display, gdk_window);
return GDK_MONITOR(monitor);
}
static GdkRectangle getCurrentMonitorGeometry(GtkWindow *window)
{
GdkMonitor *monitor = getCurrentMonitor(window);
GdkRectangle result;
if (monitor == NULL)
{
result.x = result.y = result.height = result.width = -1;
return result;
}
// Get the geometry of the monitor
gdk_monitor_get_geometry(monitor, &result);
return result;
}
static int getCurrentMonitorScaleFactor(GtkWindow *window)
{
GdkMonitor *monitor = getCurrentMonitor(window);
return gdk_monitor_get_scale_factor(monitor);
}
// window
ulong SetupInvokeSignal(void *contentManager)
{
return g_signal_connect((WebKitUserContentManager *)contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), NULL);
}
void SetWindowIcon(GtkWindow *window, const guchar *buf, gsize len)
{
GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
if (!loader)
{
return;
}
if (gdk_pixbuf_loader_write(loader, buf, len, NULL) && gdk_pixbuf_loader_close(loader, NULL))
{
GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
if (pixbuf)
{
gtk_window_set_icon(window, pixbuf);
}
}
g_object_unref(loader);
}
void SetWindowTransparency(GtkWidget *widget)
{
GdkScreen *screen = gtk_widget_get_screen(widget);
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
if (visual != NULL && gdk_screen_is_composited(screen))
{
gtk_widget_set_app_paintable(widget, true);
gtk_widget_set_visual(widget, visual);
}
}
void SetBackgroundColour(void *data)
{
RGBAOptions *options = (RGBAOptions *)data;
GdkRGBA colour = {options->r / 255.0, options->g / 255.0, options->b / 255.0, options->a / 255.0};
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(options->webview), &colour);
}
static gboolean setTitle(gpointer data)
{
SetTitleArgs *args = (SetTitleArgs *)data;
gtk_window_set_title(args->window, args->title);
free((void *)args->title);
free((void *)data);
return G_SOURCE_REMOVE;
}
void SetTitle(GtkWindow *window, char *title)
{
SetTitleArgs *args = malloc(sizeof(SetTitleArgs));
args->window = window;
args->title = title;
ExecuteOnMainThread(setTitle, (gpointer)args);
}
static gboolean setPosition(gpointer data)
{
SetPositionArgs *args = (SetPositionArgs *)data;
gtk_window_move((GtkWindow *)args->window, args->x, args->y);
free(args);
return G_SOURCE_REMOVE;
}
void SetPosition(void *window, int x, int y)
{
GdkRectangle monitorDimensions = getCurrentMonitorGeometry(window);
if (isNULLRectangle(monitorDimensions))
{
return;
}
SetPositionArgs *args = malloc(sizeof(SetPositionArgs));
args->window = window;
args->x = monitorDimensions.x + x;
args->y = monitorDimensions.y + y;
ExecuteOnMainThread(setPosition, (gpointer)args);
}
void SetMinMaxSize(GtkWindow *window, int min_width, int min_height, int max_width, int max_height)
{
GdkGeometry size;
size.min_width = size.min_height = size.max_width = size.max_height = 0;
GdkRectangle monitorSize = getCurrentMonitorGeometry(window);
if (isNULLRectangle(monitorSize))
{
return;
}
int flags = GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE;
size.max_height = (max_height == 0 ? monitorSize.height : max_height);
size.max_width = (max_width == 0 ? monitorSize.width : max_width);
size.min_height = min_height;
size.min_width = min_width;
gtk_window_set_geometry_hints(window, NULL, &size, flags);
}
// function to disable the context menu but propogate the event
static gboolean disableContextMenu(GtkWidget *widget, WebKitContextMenu *context_menu, GdkEvent *event, WebKitHitTestResult *hit_test_result, gpointer data)
{
// return true to disable the context menu
return TRUE;
}
void DisableContextMenu(void *webview)
{
// Disable the context menu but propogate the event
g_signal_connect(WEBKIT_WEB_VIEW(webview), "context-menu", G_CALLBACK(disableContextMenu), NULL);
}
static gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, void *dummy)
{
if (event == NULL)
{
xroot = yroot = 0.0f;
dragTime = -1;
return FALSE;
}
mouseButton = event->button;
if (event->button == 3)
{
return FALSE;
}
if (event->type == GDK_BUTTON_PRESS && event->button == 1)
{
xroot = event->x_root;
yroot = event->y_root;
dragTime = event->time;
}
return FALSE;
}
static 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);
}
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;
}
int IsMaximised(GtkWidget *widget)
{
GdkWindow *gdkwindow = gtk_widget_get_window(widget);
GdkWindowState state = gdk_window_get_state(GDK_WINDOW(gdkwindow));
return state & GDK_WINDOW_STATE_MAXIMIZED && !(state & GDK_WINDOW_STATE_FULLSCREEN);
}
int IsMinimised(GtkWidget *widget)
{
GdkWindow *gdkwindow = gtk_widget_get_window(widget);
GdkWindowState state = gdk_window_get_state(GDK_WINDOW(gdkwindow));
return state & GDK_WINDOW_STATE_ICONIFIED;
}
gboolean Center(gpointer data)
{
GtkWindow *window = (GtkWindow *)data;
// Get the geometry of the monitor
GdkRectangle m = getCurrentMonitorGeometry(window);
if (isNULLRectangle(m))
{
return G_SOURCE_REMOVE;
}
// 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);
return G_SOURCE_REMOVE;
}
gboolean Show(gpointer data)
{
gtk_widget_show((GtkWidget *)data);
return G_SOURCE_REMOVE;
}
gboolean Hide(gpointer data)
{
gtk_widget_hide((GtkWidget *)data);
return G_SOURCE_REMOVE;
}
gboolean Maximise(gpointer data)
{
gtk_window_maximize((GtkWindow *)data);
return G_SOURCE_REMOVE;
}
gboolean UnMaximise(gpointer data)
{
gtk_window_unmaximize((GtkWindow *)data);
return G_SOURCE_REMOVE;
}
gboolean Minimise(gpointer data)
{
gtk_window_iconify((GtkWindow *)data);
return G_SOURCE_REMOVE;
}
gboolean UnMinimise(gpointer data)
{
gtk_window_present((GtkWindow *)data);
return G_SOURCE_REMOVE;
}
gboolean Fullscreen(gpointer data)
{
GtkWindow *window = (GtkWindow *)data;
// Get the geometry of the monitor.
GdkRectangle m = getCurrentMonitorGeometry(window);
if (isNULLRectangle(m))
{
return G_SOURCE_REMOVE;
}
int scale = getCurrentMonitorScaleFactor(window);
SetMinMaxSize(window, 0, 0, m.width * scale, m.height * scale);
gtk_window_fullscreen(window);
return G_SOURCE_REMOVE;
}
gboolean UnFullscreen(gpointer data)
{
gtk_window_unfullscreen((GtkWindow *)data);
return G_SOURCE_REMOVE;
}
static void webviewLoadChanged(WebKitWebView *web_view, WebKitLoadEvent load_event, gpointer data)
{
if (load_event == WEBKIT_LOAD_FINISHED)
{
processMessage("DomReady");
}
}
extern void processURLRequest(void *request);
// This is called when the close button on the window is pressed
gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void *data)
{
processMessage("Q");
// since we handle the close in processMessage tell GTK to not invoke additional handlers - see:
// https://docs.gtk.org/gtk3/signal.Widget.delete-event.html
return TRUE;
}
// WebView
GtkWidget *SetupWebview(void *contentManager, GtkWindow *window, int hideWindowOnClose, int gpuPolicy)
{
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(webviewLoadChanged), 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);
}
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
webkit_settings_set_user_agent_with_application_details(settings, "wails.io", "");
switch (gpuPolicy)
{
case 0:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS);
break;
case 1:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND);
break;
case 2:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER);
break;
default:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND);
}
return webview;
}
void DevtoolsEnabled(void *webview, int enabled, bool showInspector)
{
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);
if (genabled && showInspector)
{
WebKitWebInspector *inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
webkit_web_inspector_show(WEBKIT_WEB_INSPECTOR(inspector));
}
}
void LoadIndex(void *webview, char *url)
{
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
}
static gboolean startDrag(gpointer data)
{
DragOptions *options = (DragOptions *)data;
// Ignore non-toplevel widgets
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(options->webview));
if (!GTK_IS_WINDOW(window))
{
free(data);
return G_SOURCE_REMOVE;
}
gtk_window_begin_move_drag(options->mainwindow, mouseButton, xroot, yroot, dragTime);
free(data);
return G_SOURCE_REMOVE;
}
void StartDrag(void *webview, GtkWindow *mainwindow)
{
DragOptions *data = malloc(sizeof(DragOptions));
data->webview = webview;
data->mainwindow = mainwindow;
ExecuteOnMainThread(startDrag, (gpointer)data);
}
static gboolean startResize(gpointer data)
{
ResizeOptions *options = (ResizeOptions *)data;
// Ignore non-toplevel widgets
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(options->webview));
if (!GTK_IS_WINDOW(window))
{
free(data);
return G_SOURCE_REMOVE;
}
gtk_window_begin_resize_drag(options->mainwindow, options->edge, mouseButton, xroot, yroot, dragTime);
free(data);
return G_SOURCE_REMOVE;
}
void StartResize(void *webview, GtkWindow *mainwindow, GdkWindowEdge edge)
{
ResizeOptions *data = malloc(sizeof(ResizeOptions));
data->webview = webview;
data->mainwindow = mainwindow;
data->edge = edge;
ExecuteOnMainThread(startResize, (gpointer)data);
}
void ExecuteJS(void *data)
{
struct JSCallback *js = data;
webkit_web_view_run_javascript(js->webview, js->script, NULL, NULL, NULL);
free(js->script);
}
void extern processMessageDialogResult(char *);
void MessageDialog(void *data)
{
GtkDialogFlags flags;
GtkMessageType messageType;
MessageDialogOptions *options = (MessageDialogOptions *)data;
if (options->messageType == 0)
{
messageType = GTK_MESSAGE_INFO;
flags = GTK_BUTTONS_OK;
}
else if (options->messageType == 1)
{
messageType = GTK_MESSAGE_ERROR;
flags = GTK_BUTTONS_OK;
}
else if (options->messageType == 2)
{
messageType = GTK_MESSAGE_QUESTION;
flags = GTK_BUTTONS_YES_NO;
}
else
{
messageType = GTK_MESSAGE_WARNING;
flags = GTK_BUTTONS_OK;
}
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(options->window),
GTK_DIALOG_DESTROY_WITH_PARENT,
messageType,
flags,
options->message, NULL);
gtk_window_set_title(GTK_WINDOW(dialog), options->title);
GtkResponseType result = gtk_dialog_run(GTK_DIALOG(dialog));
if (result == GTK_RESPONSE_YES)
{
processMessageDialogResult("Yes");
}
else if (result == GTK_RESPONSE_NO)
{
processMessageDialogResult("No");
}
else if (result == GTK_RESPONSE_OK)
{
processMessageDialogResult("OK");
}
else if (result == GTK_RESPONSE_CANCEL)
{
processMessageDialogResult("Cancel");
}
else
{
processMessageDialogResult("");
}
gtk_widget_destroy(dialog);
free(options->title);
free(options->message);
}
void extern processOpenFileResult(void *);
GtkFileFilter **AllocFileFilterArray(size_t ln)
{
return (GtkFileFilter **)malloc(ln * sizeof(GtkFileFilter *));
}
void freeFileFilterArray(GtkFileFilter **filters)
{
free(filters);
}
void Opendialog(void *data)
{
struct OpenFileDialogOptions *options = data;
char *label = "_Open";
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE)
{
label = "_Save";
}
GtkWidget *dlgWidget = gtk_file_chooser_dialog_new(options->title, options->window, options->action,
"_Cancel", GTK_RESPONSE_CANCEL,
label, GTK_RESPONSE_ACCEPT,
NULL);
GtkFileChooser *fc = GTK_FILE_CHOOSER(dlgWidget);
// filters
if (options->filters != 0)
{
int index = 0;
GtkFileFilter *thisFilter;
while (options->filters[index] != NULL)
{
thisFilter = options->filters[index];
gtk_file_chooser_add_filter(fc, thisFilter);
index++;
}
}
gtk_file_chooser_set_local_only(fc, FALSE);
if (options->multipleFiles == 1)
{
gtk_file_chooser_set_select_multiple(fc, TRUE);
}
gtk_file_chooser_set_do_overwrite_confirmation(fc, TRUE);
if (options->createDirectories == 1)
{
gtk_file_chooser_set_create_folders(fc, TRUE);
}
if (options->showHiddenFiles == 1)
{
gtk_file_chooser_set_show_hidden(fc, TRUE);
}
if (options->defaultDirectory != NULL)
{
gtk_file_chooser_set_current_folder(fc, options->defaultDirectory);
free(options->defaultDirectory);
}
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE)
{
if (options->defaultFilename != NULL)
{
gtk_file_chooser_set_current_name(fc, options->defaultFilename);
free(options->defaultFilename);
}
}
gint response = gtk_dialog_run(GTK_DIALOG(dlgWidget));
// Max 1024 files to select
char **result = calloc(1024, sizeof(char *));
int resultIndex = 0;
if (response == GTK_RESPONSE_ACCEPT)
{
GSList *filenames = gtk_file_chooser_get_filenames(fc);
GSList *iter = filenames;
while (iter)
{
result[resultIndex++] = (char *)iter->data;
iter = g_slist_next(iter);
if (resultIndex == 1024)
{
break;
}
}
processOpenFileResult(result);
iter = filenames;
while (iter)
{
g_free(iter->data);
iter = g_slist_next(iter);
}
}
else
{
processOpenFileResult(result);
}
free(result);
// Release filters
if (options->filters != NULL)
{
int index = 0;
GtkFileFilter *thisFilter;
while (options->filters[index] != 0)
{
thisFilter = options->filters[index];
g_object_unref(thisFilter);
index++;
}
freeFileFilterArray(options->filters);
}
gtk_widget_destroy(dlgWidget);
free(options->title);
}
GtkFileFilter *newFileFilter()
{
GtkFileFilter *result = gtk_file_filter_new();
g_object_ref(result);
return result;
}

View File

@ -7,653 +7,12 @@ package linux
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
#include <JavaScriptCore/JavaScript.h>
#include "gtk/gtk.h"
#include "webkit2/webkit2.h"
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>
#include <stdio.h>
#include <limits.h>
#include "stdint.h"
void ExecuteOnMainThread(void* f, gpointer jscallback) {
g_idle_add((GSourceFunc)f, (gpointer)jscallback);
}
static GtkWidget* GTKWIDGET(void *pointer) {
return GTK_WIDGET(pointer);
}
static GtkWindow* GTKWINDOW(void *pointer) {
return GTK_WINDOW(pointer);
}
static GtkContainer* GTKCONTAINER(void *pointer) {
return GTK_CONTAINER(pointer);
}
static GtkBox* GTKBOX(void *pointer) {
return GTK_BOX(pointer);
}
GdkMonitor* getCurrentMonitor(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));
if( gdk_window == NULL ) {
return NULL;
}
GdkMonitor *monitor = gdk_display_get_monitor_at_window(display, gdk_window);
return GDK_MONITOR(monitor);
}
bool isNULLRectangle(GdkRectangle input) {
return input.x == -1 && input.y == -1 && input.width == -1 && input.height == -1;
}
GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) {
GdkMonitor *monitor = getCurrentMonitor(window);
GdkRectangle result;
if( monitor == NULL ) {
result.x = result.y = result.height = result.width = -1;
return result;
}
// Get the geometry of the monitor
gdk_monitor_get_geometry (monitor,&result);
return result;
}
int getCurrentMonitorScaleFactor(GtkWindow *window) {
GdkMonitor *monitor = getCurrentMonitor(window);
return gdk_monitor_get_scale_factor(monitor);
}
static void SetMinMaxSize(GtkWindow* window, int min_width, int min_height, int max_width, int max_height) {
GdkGeometry size;
size.min_width = size.min_height = size.max_width = size.max_height = 0;
GdkRectangle monitorSize = getCurrentMonitorGeometry(window);
if( isNULLRectangle(monitorSize) ) {
return;
}
int flags = GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE;
size.max_height = (max_height == 0 ? monitorSize.height : max_height);
size.max_width = (max_width == 0 ? monitorSize.width : max_width);
size.min_height = min_height;
size.min_width = min_width;
gtk_window_set_geometry_hints(window, NULL, &size, flags);
}
gboolean Center(gpointer data) {
GtkWindow *window = (GtkWindow*)data;
// Get the geometry of the monitor
GdkRectangle m = getCurrentMonitorGeometry(window);
if( isNULLRectangle(m) ) {
return G_SOURCE_REMOVE;
}
// 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);
return G_SOURCE_REMOVE;
}
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;
}
int IsMaximised(GtkWidget *widget) {
GdkWindow *gdkwindow = gtk_widget_get_window(widget);
GdkWindowState state = gdk_window_get_state(GDK_WINDOW(gdkwindow));
return state & GDK_WINDOW_STATE_MAXIMIZED && !(state & GDK_WINDOW_STATE_FULLSCREEN);
}
int IsMinimised(GtkWidget *widget) {
GdkWindow *gdkwindow = gtk_widget_get_window(widget);
GdkWindowState state = gdk_window_get_state(GDK_WINDOW(gdkwindow));
return state & GDK_WINDOW_STATE_ICONIFIED;
}
extern void processMessage(char*);
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
WebKitJavascriptResult *result,
void* data)
{
#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);
}
static void webviewLoadChanged(WebKitWebView *web_view, WebKitLoadEvent load_event, gpointer data)
{
if (load_event == WEBKIT_LOAD_FINISHED) {
processMessage("DomReady");
}
}
ulong setupInvokeSignal(void* contentManager) {
return g_signal_connect((WebKitUserContentManager*)contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), NULL);
}
// These are the x,y,time & button of the last mouse down event
// It's used for window dragging
float xroot = 0.0f;
float yroot = 0.0f;
int dragTime = -1;
uint mouseButton = 0;
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, void* dummy)
{
if( event == NULL ) {
xroot = yroot = 0.0f;
dragTime = -1;
return FALSE;
}
mouseButton = event->button;
if( event->button == 3 ) {
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(void *request);
// This is called when the close button on the window is pressed
gboolean close_button_pressed(GtkWidget *widget, GdkEvent *event, void* data)
{
processMessage("Q");
// since we handle the close in processMessage tell GTK to not invoke additional handlers - see:
// https://docs.gtk.org/gtk3/signal.Widget.delete-event.html
return TRUE;
}
GtkWidget* setupWebview(void* contentManager, GtkWindow* window, int hideWindowOnClose, int gpuPolicy) {
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(webviewLoadChanged), 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);
}
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
webkit_settings_set_user_agent_with_application_details(settings, "wails.io", "");
switch (gpuPolicy) {
case 0:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS);
break;
case 1:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND);
break;
case 2:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER);
break;
default:
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND);
}
return webview;
}
void devtoolsEnabled(void* webview, int enabled, bool showInspector) {
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);
if (genabled && showInspector) {
WebKitWebInspector *inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
webkit_web_inspector_show(WEBKIT_WEB_INSPECTOR(inspector));
}
}
void loadIndex(void* webview, char* url) {
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
}
typedef struct DragOptions {
void *webview;
GtkWindow* mainwindow;
} DragOptions;
static gboolean startDrag(gpointer data) {
DragOptions* options = (DragOptions*)data;
// Ignore non-toplevel widgets
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(options->webview));
if (!GTK_IS_WINDOW(window)) {
free(data);
return G_SOURCE_REMOVE;
}
gtk_window_begin_move_drag(options->mainwindow, mouseButton, xroot, yroot, dragTime);
free(data);
return G_SOURCE_REMOVE;
}
static void StartDrag(void *webview, GtkWindow* mainwindow) {
DragOptions* data = malloc(sizeof(DragOptions));
data->webview = webview;
data->mainwindow = mainwindow;
ExecuteOnMainThread(startDrag, (gpointer)data);
}
typedef struct ResizeOptions {
void *webview;
GtkWindow* mainwindow;
GdkWindowEdge edge;
} ResizeOptions;
static gboolean startResize(gpointer data) {
ResizeOptions* options = (ResizeOptions*)data;
// Ignore non-toplevel widgets
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(options->webview));
if (!GTK_IS_WINDOW(window)) {
free(data);
return G_SOURCE_REMOVE;
}
gtk_window_begin_resize_drag(options->mainwindow, options->edge, mouseButton, xroot, yroot, dragTime);
free(data);
return G_SOURCE_REMOVE;
}
static void StartResize(void *webview, GtkWindow* mainwindow, GdkWindowEdge edge) {
ResizeOptions* data = malloc(sizeof(ResizeOptions));
data->webview = webview;
data->mainwindow = mainwindow;
data->edge = edge;
ExecuteOnMainThread(startResize, (gpointer)data);
}
typedef struct JSCallback {
void* webview;
char* script;
} JSCallback;
void executeJS(void *data) {
struct JSCallback *js = data;
webkit_web_view_run_javascript(js->webview, js->script, NULL, NULL, NULL);
free(js->script);
}
void extern processMessageDialogResult(char*);
typedef struct MessageDialogOptions {
void* window;
char* title;
char* message;
int messageType;
} MessageDialogOptions;
void messageDialog(void *data) {
GtkDialogFlags flags;
GtkMessageType messageType;
MessageDialogOptions *options = (MessageDialogOptions*) data;
if( options->messageType == 0 ) {
messageType = GTK_MESSAGE_INFO;
flags = GTK_BUTTONS_OK;
} else if( options->messageType == 1 ) {
messageType = GTK_MESSAGE_ERROR;
flags = GTK_BUTTONS_OK;
} else if( options->messageType == 2 ) {
messageType = GTK_MESSAGE_QUESTION;
flags = GTK_BUTTONS_YES_NO;
} else {
messageType = GTK_MESSAGE_WARNING;
flags = GTK_BUTTONS_OK;
}
GtkWidget *dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(options->window),
GTK_DIALOG_DESTROY_WITH_PARENT,
messageType,
flags,
options->message, NULL);
gtk_window_set_title(GTK_WINDOW(dialog), options->title);
GtkResponseType result = gtk_dialog_run(GTK_DIALOG(dialog));
if ( result == GTK_RESPONSE_YES ) {
processMessageDialogResult("Yes");
} else if ( result == GTK_RESPONSE_NO ) {
processMessageDialogResult("No");
} else if ( result == GTK_RESPONSE_OK ) {
processMessageDialogResult("OK");
} else if ( result == GTK_RESPONSE_CANCEL ) {
processMessageDialogResult("Cancel");
} else {
processMessageDialogResult("");
}
gtk_widget_destroy(dialog);
free(options->title);
free(options->message);
}
void extern processOpenFileResult(void*);
typedef struct OpenFileDialogOptions {
GtkWindow* window;
char* title;
char* defaultFilename;
char* defaultDirectory;
int createDirectories;
int multipleFiles;
int showHiddenFiles;
GtkFileChooserAction action;
GtkFileFilter** filters;
} OpenFileDialogOptions;
GtkFileFilter** allocFileFilterArray(size_t ln) {
return (GtkFileFilter**) malloc(ln * sizeof(GtkFileFilter*));
}
void freeFileFilterArray(GtkFileFilter** filters) {
free(filters);
}
void opendialog(void *data) {
struct OpenFileDialogOptions *options = data;
char *label = "_Open";
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) {
label = "_Save";
}
GtkWidget *dlgWidget = gtk_file_chooser_dialog_new(options->title, options->window, options->action,
"_Cancel", GTK_RESPONSE_CANCEL,
label, GTK_RESPONSE_ACCEPT,
NULL);
GtkFileChooser *fc = GTK_FILE_CHOOSER(dlgWidget);
// filters
if (options->filters != 0) {
int index = 0;
GtkFileFilter* thisFilter;
while(options->filters[index] != NULL) {
thisFilter = options->filters[index];
gtk_file_chooser_add_filter(fc, thisFilter);
index++;
}
}
gtk_file_chooser_set_local_only(fc, FALSE);
if (options->multipleFiles == 1) {
gtk_file_chooser_set_select_multiple(fc, TRUE);
}
gtk_file_chooser_set_do_overwrite_confirmation(fc, TRUE);
if (options->createDirectories == 1) {
gtk_file_chooser_set_create_folders(fc, TRUE);
}
if (options->showHiddenFiles == 1) {
gtk_file_chooser_set_show_hidden(fc, TRUE);
}
if (options->defaultDirectory != NULL) {
gtk_file_chooser_set_current_folder (fc, options->defaultDirectory);
free(options->defaultDirectory);
}
if (options->action == GTK_FILE_CHOOSER_ACTION_SAVE) {
if (options->defaultFilename != NULL) {
gtk_file_chooser_set_current_name(fc, options->defaultFilename);
free(options->defaultFilename);
}
}
gint response = gtk_dialog_run(GTK_DIALOG(dlgWidget));
// Max 1024 files to select
char** result = calloc(1024, sizeof(char*));
int resultIndex = 0;
if (response == GTK_RESPONSE_ACCEPT) {
GSList* filenames = gtk_file_chooser_get_filenames(fc);
GSList *iter = filenames;
while(iter) {
result[resultIndex++] = (char *)iter->data;
iter = g_slist_next(iter);
if (resultIndex == 1024) {
break;
}
}
processOpenFileResult(result);
iter = filenames;
while(iter) {
g_free(iter->data);
iter = g_slist_next(iter);
}
} else {
processOpenFileResult(result);
}
free(result);
// Release filters
if (options->filters != NULL) {
int index = 0;
GtkFileFilter* thisFilter;
while(options->filters[index] != 0) {
thisFilter = options->filters[index];
g_object_unref(thisFilter);
index++;
}
freeFileFilterArray(options->filters);
}
gtk_widget_destroy(dlgWidget);
free(options->title);
}
GtkFileFilter* newFileFilter() {
GtkFileFilter* result = gtk_file_filter_new();
g_object_ref(result);
return result;
}
typedef struct RGBAOptions {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
void *webview;
} RGBAOptions;
void setBackgroundColour(void* data) {
RGBAOptions* options = (RGBAOptions*)data;
GdkRGBA colour = {options->r / 255.0, options->g / 255.0, options->b / 255.0, options->a / 255.0};
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(options->webview), &colour);
}
typedef struct SetTitleArgs {
GtkWindow* window;
char* title;
} SetTitleArgs;
gboolean setTitle(gpointer data) {
SetTitleArgs* args = (SetTitleArgs*)data;
gtk_window_set_title(args->window, args->title);
free((void*)args->title);
free((void*)data);
return G_SOURCE_REMOVE;
}
void SetTitle(GtkWindow* window, char* title) {
SetTitleArgs* args = malloc(sizeof(SetTitleArgs));
args->window = window;
args->title = title;
ExecuteOnMainThread(setTitle, (gpointer)args);
}
typedef struct SetPositionArgs {
int x;
int y;
void* window;
} SetPositionArgs;
gboolean setPosition(gpointer data) {
SetPositionArgs* args = (SetPositionArgs*)data;
gtk_window_move((GtkWindow*)args->window, args->x, args->y);
free(args);
return G_SOURCE_REMOVE;
}
void SetPosition(void* window, int x, int y) {
GdkRectangle monitorDimensions = getCurrentMonitorGeometry(window);
if( isNULLRectangle(monitorDimensions) ) {
return;
}
SetPositionArgs* args = malloc(sizeof(SetPositionArgs));
args->window = window;
args->x = monitorDimensions.x + x;
args->y = monitorDimensions.y + y;
ExecuteOnMainThread(setPosition, (gpointer)args);
}
gboolean Show(gpointer data) {
gtk_widget_show((GtkWidget*)data);
return G_SOURCE_REMOVE;
}
gboolean Hide(gpointer data) {
gtk_widget_hide((GtkWidget*)data);
return G_SOURCE_REMOVE;
}
gboolean Maximise(gpointer data) {
gtk_window_maximize((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean UnMaximise(gpointer data) {
gtk_window_unmaximize((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean Minimise(gpointer data) {
gtk_window_iconify((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean UnMinimise(gpointer data) {
gtk_window_present((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
gboolean Fullscreen(gpointer data) {
GtkWindow* window = (GtkWindow*)data;
// Get the geometry of the monitor.
GdkRectangle m = getCurrentMonitorGeometry(window);
if( isNULLRectangle(m) ) {
return G_SOURCE_REMOVE;
}
int scale = getCurrentMonitorScaleFactor(window);
SetMinMaxSize(window, 0, 0, m.width * scale, m.height * scale);
gtk_window_fullscreen(window);
return G_SOURCE_REMOVE;
}
gboolean UnFullscreen(gpointer data) {
gtk_window_unfullscreen((GtkWindow*)data);
return G_SOURCE_REMOVE;
}
// function to disable the context menu but propogate the event
gboolean disableContextMenu(GtkWidget *widget, WebKitContextMenu *context_menu, GdkEvent *event, WebKitHitTestResult *hit_test_result, gpointer data) {
// return true to disable the context menu
return TRUE;
}
void DisableContextMenu(void* webview) {
// Disable the context menu but propogate the event
g_signal_connect(WEBKIT_WEB_VIEW(webview), "context-menu", G_CALLBACK(disableContextMenu), NULL);
}
void SetWindowIcon(GtkWindow* window, const guchar* buf, gsize len) {
GdkPixbufLoader* loader = gdk_pixbuf_loader_new();
if (!loader) {
return;
}
if (gdk_pixbuf_loader_write(loader, buf, len, NULL) && gdk_pixbuf_loader_close(loader, NULL)) {
GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
if (pixbuf) {
gtk_window_set_icon(window, pixbuf);
}
}
g_object_unref(loader);
}
static void SetWindowTransparency(GtkWidget *widget)
{
GdkScreen *screen = gtk_widget_get_screen(widget);
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_app_paintable(widget, true);
gtk_widget_set_visual(widget, visual);
}
}
#include <stdint.h>
#include "window.h"
*/
import "C"
@ -718,14 +77,14 @@ func NewWindow(appoptions *options.App, debug bool) *Window {
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)
C.SetupInvokeSignal(result.contentManager)
var webviewGpuPolicy int
if appoptions.Linux != nil {
webviewGpuPolicy = int(appoptions.Linux.WebviewGpuPolicy)
}
webview := C.setupWebview(
webview := C.SetupWebview(
result.contentManager,
result.asGTKWindow(),
bool2Cint(appoptions.HideWindowOnClose),
@ -734,10 +93,10 @@ func NewWindow(appoptions *options.App, debug bool) *Window {
result.webview = unsafe.Pointer(webview)
buttonPressedName := C.CString("button-press-event")
defer C.free(unsafe.Pointer(buttonPressedName))
C.connectButtons(unsafe.Pointer(webview))
C.ConnectButtons(unsafe.Pointer(webview))
if debug {
C.devtoolsEnabled(unsafe.Pointer(webview), C.int(1), C.bool(appoptions.Debug.OpenInspectorOnStartup))
C.DevtoolsEnabled(unsafe.Pointer(webview), C.int(1), C.bool(appoptions.Debug.OpenInspectorOnStartup))
} else {
C.DisableContextMenu(unsafe.Pointer(webview))
}
@ -909,7 +268,7 @@ func (w *Window) SetBackgroundColour(r uint8, g uint8, b uint8, a uint8) {
a: C.uchar(a),
webview: w.webview,
}
invokeOnMainThread(func() { C.setBackgroundColour(unsafe.Pointer(&data)) })
invokeOnMainThread(func() { C.SetBackgroundColour(unsafe.Pointer(&data)) })
}
@ -926,7 +285,7 @@ func (w *Window) Run(url string) {
}
C.gtk_box_pack_start(C.GTKBOX(unsafe.Pointer(w.vbox)), C.GTKWIDGET(w.webview), 1, 1, 0)
_url := C.CString(url)
C.loadIndex(w.webview, _url)
C.LoadIndex(w.webview, _url)
defer C.free(unsafe.Pointer(_url))
C.gtk_widget_show_all(w.asGTKWidget())
w.Center()
@ -966,7 +325,7 @@ func (w *Window) ExecJS(js string) {
webview: w.webview,
script: C.CString(js),
}
invokeOnMainThread(func() { C.executeJS(unsafe.Pointer(&jscallback)) })
invokeOnMainThread(func() { C.ExecuteJS(unsafe.Pointer(&jscallback)) })
}
func (w *Window) StartDrag() {
@ -994,7 +353,7 @@ func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multip
// Create filter array
mem := NewCalloc()
arraySize := len(dialogOptions.Filters) + 1
data.filters = C.allocFileFilterArray((C.size_t)(arraySize))
data.filters = C.AllocFileFilterArray((C.size_t)(arraySize))
filters := unsafe.Slice((**C.struct__GtkFileFilter)(unsafe.Pointer(data.filters)), arraySize)
for index, filter := range dialogOptions.Filters {
thisFilter := C.gtk_file_filter_new()
@ -1032,7 +391,7 @@ func (w *Window) OpenFileDialog(dialogOptions frontend.OpenDialogOptions, multip
data.defaultDirectory = C.CString(dialogOptions.DefaultDirectory)
}
invokeOnMainThread(func() { C.opendialog(unsafe.Pointer(&data)) })
invokeOnMainThread(func() { C.Opendialog(unsafe.Pointer(&data)) })
}
func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) {
@ -1052,7 +411,7 @@ func (w *Window) MessageDialog(dialogOptions frontend.MessageDialogOptions) {
case frontend.WarningDialog:
data.messageType = C.int(3)
}
invokeOnMainThread(func() { C.messageDialog(unsafe.Pointer(&data)) })
invokeOnMainThread(func() { C.MessageDialog(unsafe.Pointer(&data)) })
}
func (w *Window) ToggleMaximise() {
@ -1072,7 +431,7 @@ func showModalDialogAndExit(title, message string) {
messageType: C.int(1),
}
C.messageDialog(unsafe.Pointer(&data))
C.MessageDialog(unsafe.Pointer(&data))
}()
<-messageDialogResult

View File

@ -0,0 +1,121 @@
#ifndef window_h
#define window_h
#include <JavaScriptCore/JavaScript.h>
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
typedef struct DragOptions
{
void *webview;
GtkWindow *mainwindow;
} DragOptions;
typedef struct ResizeOptions
{
void *webview;
GtkWindow *mainwindow;
GdkWindowEdge edge;
} ResizeOptions;
typedef struct JSCallback
{
void *webview;
char *script;
} JSCallback;
typedef struct MessageDialogOptions
{
void *window;
char *title;
char *message;
int messageType;
} MessageDialogOptions;
typedef struct OpenFileDialogOptions
{
GtkWindow *window;
char *title;
char *defaultFilename;
char *defaultDirectory;
int createDirectories;
int multipleFiles;
int showHiddenFiles;
GtkFileChooserAction action;
GtkFileFilter **filters;
} OpenFileDialogOptions;
typedef struct RGBAOptions
{
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
void *webview;
} RGBAOptions;
typedef struct SetTitleArgs
{
GtkWindow *window;
char *title;
} SetTitleArgs;
typedef struct SetPositionArgs
{
int x;
int y;
void *window;
} SetPositionArgs;
void ExecuteOnMainThread(void *f, gpointer jscallback);
GtkWidget *GTKWIDGET(void *pointer);
GtkWindow *GTKWINDOW(void *pointer);
GtkContainer *GTKCONTAINER(void *pointer);
GtkBox *GTKBOX(void *pointer);
// window
ulong SetupInvokeSignal(void *contentManager);
void SetWindowIcon(GtkWindow *window, const guchar *buf, gsize len);
void SetWindowTransparency(GtkWidget *widget);
void SetBackgroundColour(void *data);
void SetTitle(GtkWindow *window, char *title);
void SetPosition(void *window, int x, int y);
void SetMinMaxSize(GtkWindow *window, int min_width, int min_height, int max_width, int max_height);
void DisableContextMenu(void *webview);
void ConnectButtons(void *webview);
int IsFullscreen(GtkWidget *widget);
int IsMaximised(GtkWidget *widget);
int IsMinimised(GtkWidget *widget);
gboolean Center(gpointer data);
gboolean Show(gpointer data);
gboolean Hide(gpointer data);
gboolean Maximise(gpointer data);
gboolean UnMaximise(gpointer data);
gboolean Minimise(gpointer data);
gboolean UnMinimise(gpointer data);
gboolean Fullscreen(gpointer data);
gboolean UnFullscreen(gpointer data);
// WebView
GtkWidget *SetupWebview(void *contentManager, GtkWindow *window, int hideWindowOnClose, int gpuPolicy);
void LoadIndex(void *webview, char *url);
void DevtoolsEnabled(void *webview, int enabled, bool showInspector);
void ExecuteJS(void *data);
// Drag
void StartDrag(void *webview, GtkWindow *mainwindow);
void StartResize(void *webview, GtkWindow *mainwindow, GdkWindowEdge edge);
// Dialog
void MessageDialog(void *data);
GtkFileFilter **AllocFileFilterArray(size_t ln);
void Opendialog(void *data);
#endif /* window_h */