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:
parent
ccceadbd24
commit
c3011813b0
694
v2/internal/frontend/desktop/linux/window.c
Normal file
694
v2/internal/frontend/desktop/linux/window.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
121
v2/internal/frontend/desktop/linux/window.h
Normal file
121
v2/internal/frontend/desktop/linux/window.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user