From 01acf3142803e0fdec4cd1096579b75cfacbb0d9 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:00:03 +0100 Subject: [PATCH 01/20] Feature - [Android] Webview support --- src/unix/linux/android/Matoya.java | 164 ++++++++++++++++++++++++++++- src/unix/linux/android/webview.c | 130 ++++++++++++++++++++++- 2 files changed, 289 insertions(+), 5 deletions(-) diff --git a/src/unix/linux/android/Matoya.java b/src/unix/linux/android/Matoya.java index 19dc7719..5c99319d 100644 --- a/src/unix/linux/android/Matoya.java +++ b/src/unix/linux/android/Matoya.java @@ -18,9 +18,14 @@ import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.webkit.JavascriptInterface; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.webkit.WebMessage; import android.text.InputType; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; import android.content.Context; import android.content.Intent; import android.content.ClipData; @@ -31,9 +36,11 @@ import android.util.Log; import android.util.DisplayMetrics; import android.util.Base64; +import android.widget.FrameLayout; import android.widget.Scroller; import android.os.Vibrator; import android.net.Uri; +import java.nio.charset.StandardCharsets; public class Matoya extends SurfaceView implements SurfaceHolder.Callback, @@ -45,11 +52,13 @@ public class Matoya extends SurfaceView implements ClipboardManager.OnPrimaryClipChangedListener { Activity activity; + FrameLayout layout; KeyCharacterMap kbmap; PointerIcon cursor; PointerIcon invisCursor; GestureDetector detector; ScaleGestureDetector sdetector; + WebView webview; Scroller scroller; Vibrator vibrator; boolean hiddenCursor; @@ -78,6 +87,7 @@ public class Matoya extends SurfaceView implements native void app_axis(int deviceId, float hatX, float hatY, float lX, float lY, float rX, float rY, float lT, float rT, float lTalt, float rTalt); native void app_unhandled_touch(int action, float x, float y, int fingers); + native void webview_handle_event(long ctx, String str); static { System.loadLibrary("main"); @@ -105,7 +115,9 @@ public Matoya(Activity activity) { Bitmap bm = BitmapFactory.decodeByteArray(iCursorData, 0, iCursorData.length, null); this.invisCursor = PointerIcon.create(bm, 0, 0); - activity.setContentView(this); + this.layout = new FrameLayout(this.activity); + this.layout.addView(this); + activity.setContentView(this.layout); ClipboardManager clipboard = (ClipboardManager) this.activity.getSystemService(Context.CLIPBOARD_SERVICE); clipboard.addPrimaryClipChangedListener(this); @@ -541,6 +553,156 @@ public boolean getRelativeMouse() { return this.hasPointerCapture(); } + // Webview + + public void webviewCreate(final long ctx, final boolean debug) + { + final Matoya self = this; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + WebView webview = new WebView(self.activity); + webview.setBackgroundColor(Color.TRANSPARENT); + webview.getSettings().setDomStorageEnabled(true); + webview.getSettings().setJavaScriptEnabled(true); + + if (debug) + webview.setWebContentsDebuggingEnabled(true); + + webview.addJavascriptInterface(new Object() { + @JavascriptInterface + public void postMessage(String text) { + webview_handle_event(ctx, text); + } + }, "native"); + + webview.setWebViewClient(new WebViewClient() { + @Override + public void onPageStarted(WebView webview, String url, Bitmap favicon) { + String script = String.join("\n", + "const __MTY_MSGS = [];", + + "window.addEventListener('message', evt => {", + "if (window.MTY_NativeListener) {", + "window.MTY_NativeListener(evt.data);", + + "} else {", + "__MTY_MSGS.push(evt.data);", + "}", + "});", + + "window.MTY_NativeSendText = text => {", + "native.postMessage('T' + text);", + "};", + + "native.postMessage('R');", + + "const __MTY_INTERVAL = setInterval(() => {", + "if (window.MTY_NativeListener) {", + "for (let msg = __MTY_MSGS.shift(); msg; msg = __MTY_MSGS.shift())", + "window.MTY_NativeListener(msg);", + + "clearInterval(__MTY_INTERVAL);", + "}", + "}, 100);", + + "function __mty_key_to_json(evt) {", + "let mods = 0;", + + "if (evt.shiftKey) mods |= 0x01;", + "if (evt.ctrlKey) mods |= 0x02;", + "if (evt.altKey) mods |= 0x04;", + "if (evt.metaKey) mods |= 0x08;", + + "if (evt.getModifierState('CapsLock')) mods |= 0x10;", + "if (evt.getModifierState('NumLock')) mods |= 0x20;", + + "let cmd = evt.type == 'keydown' ? 'D' : 'U';", + "let json = JSON.stringify({'code':evt.code,'mods':mods});", + + "native.postMessage(cmd + json);", + "}", + + "document.addEventListener('keydown', __mty_key_to_json);", + "document.addEventListener('keyup', __mty_key_to_json);" + ); + + webview.evaluateJavascript(script, null); + } + }); + + self.layout.addView(webview); + self.webview = webview; + } + }); + } + + public void webviewDestroy() + { + final Matoya self = this; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + self.webview.destroy(); + self.webview = null; + } + }); + } + + public void webviewNavigate(final String source, final boolean url) + { + final Matoya self = this; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (url) { + self.webview.loadUrl(source, null); + + } else { + String encodedHtml = Base64.encodeToString(source.getBytes(StandardCharsets.US_ASCII), Base64.DEFAULT); + self.webview.loadData(encodedHtml, "text/html; charset=utf-8", "base64"); + } + } + }); + } + + public void webviewShow(final boolean show) + { + final Matoya self = this; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + self.webview.setVisibility(show ? View.VISIBLE : View.GONE); + } + }); + } + + public boolean webviewIsVisible() + { + return this.webview.getVisibility() == View.VISIBLE; + } + + public void webviewSendText(final String msg) + { + final Matoya self = this; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + self.webview.postWebMessage(new WebMessage(msg), Uri.parse("*")); + } + }); + } + + public void webviewReload() + { + final Matoya self = this; + this.activity.runOnUiThread(new Runnable() { + @Override + public void run() { + self.webview.reload(); + } + }); + } // Misc diff --git a/src/unix/linux/android/webview.c b/src/unix/linux/android/webview.c index 34bde07a..1bd69771 100644 --- a/src/unix/linux/android/webview.c +++ b/src/unix/linux/android/webview.c @@ -4,39 +4,101 @@ #include "webview.h" +#include +#include + +#include "matoya.h" +#include "app-os.h" +#include "jnih.h" +#include "web/keymap.h" + +struct webview { + MTY_App *app; + MTY_Window window; + WEBVIEW_READY ready_func; + WEBVIEW_TEXT text_func; + WEBVIEW_KEY key_func; + MTY_Hash *keys; + MTY_Queue *pushq; + bool ready; + bool passthrough; +}; + struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char *dir, bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func) { - return NULL; + struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); + + ctx->app = app; + ctx->window = window; + ctx->ready_func = ready_func; + ctx->text_func = text_func; + ctx->key_func = key_func; + + ctx->keys = web_keymap_hash(); + ctx->pushq = MTY_QueueCreate(50, 0); + + mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewCreate", "(JZ)V", (long) ctx, debug); + + return ctx; } void mty_webview_destroy(struct webview **webview) { + if (!webview || !*webview) + return; + + struct webview *ctx = *webview; + *webview = NULL; + + mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewDestroy", "()V"); + + if (ctx->pushq) + MTY_QueueFlush(ctx->pushq, MTY_Free); + + MTY_QueueDestroy(&ctx->pushq); + MTY_HashDestroy(&ctx->keys, NULL); + + MTY_Free(ctx); } void mty_webview_navigate(struct webview *ctx, const char *source, bool url) { + jstring jsource = mty_jni_strdup(MTY_GetJNIEnv(), source); + mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewNavigate", "(Ljava/lang/String;Z)V", jsource, url); + mty_jni_free(MTY_GetJNIEnv(), jsource); } void mty_webview_show(struct webview *ctx, bool show) { + mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewShow", "(Z)V", show); } bool mty_webview_is_visible(struct webview *ctx) { - return false; + return mty_jni_bool(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewIsVisible", "()Z"); } void mty_webview_send_text(struct webview *ctx, const char *msg) { + if (!ctx->ready) { + MTY_QueuePushPtr(ctx->pushq, MTY_Strdup(msg), 0); + + } else { + jstring jmsg = mty_jni_strdup(MTY_GetJNIEnv(), msg); + mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewSendText", "(Ljava/lang/String;)V", jmsg); + mty_jni_free(MTY_GetJNIEnv(), jmsg); + } } void mty_webview_reload(struct webview *ctx) { + mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewReload", "()V"); } void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { + ctx->passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) @@ -46,15 +108,17 @@ bool mty_webview_event(struct webview *ctx, MTY_Event *evt) void mty_webview_run(struct webview *ctx) { + } void mty_webview_render(struct webview *ctx) { + } bool mty_webview_is_focussed(struct webview *ctx) { - return false; + return true; } bool mty_webview_is_steam(void) @@ -64,5 +128,63 @@ bool mty_webview_is_steam(void) bool mty_webview_is_available(void) { - return false; + return true; +} + +JNIEXPORT void JNICALL Java_group_matoya_lib_Matoya_webview_1handle_1event(JNIEnv *env, jobject obj, jlong jctx, jstring jstr) +{ + struct webview *ctx = (struct webview *) jctx; + char *str = mty_jni_cstrmov(MTY_GetJNIEnv(), jstr); + + MTY_JSON *j = NULL; + + switch (str[0]) { + // MTY_EVENT_WEBVIEW_READY + case 'R': + ctx->ready = true; + + // Send any queued messages before the WebView became ready + for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { + mty_webview_send_text(ctx, msg); + MTY_Free(msg); + } + + ctx->ready_func(ctx->app, ctx->window); + break; + + // MTY_EVENT_WEBVIEW_TEXT + case 'T': + ctx->text_func(ctx->app, ctx->window, str + 1); + break; + + // MTY_EVENT_KEY + case 'D': + case 'U': + if (!ctx->passthrough) + break; + + j = MTY_JSONParse(str + 1); + if (!j) + break; + + const char *code = MTY_JSONObjGetStringPtr(j, "code"); + if (!code) + break; + + uint32_t jmods = 0; + if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) + break; + + MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; + if (key == MTY_KEY_NONE) + break; + + MTY_Mod mods = web_keymap_mods(jmods); + + ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); + break; + } + + MTY_JSONDestroy(&j); + MTY_Free(str); } From 812f8d7aaa689f13e7a6f39223ce53151e04a91c Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Fri, 1 Nov 2024 12:14:26 +0100 Subject: [PATCH 02/20] Feature - [Linux] Webview support --- GNUmakefile | 3 +- src/unix/linux/x11/webview.c | 361 ++++++++++++++++++++++++++++++++++- 2 files changed, 359 insertions(+), 5 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 4357f03e..5905ccf9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -163,7 +163,8 @@ DEFS := $(DEFS) \ INCLUDES := $(INCLUDES) \ -Isrc/unix/linux \ - -Isrc/unix/linux/x11 + -Isrc/unix/linux/x11 \ + $(shell pkg-config --cflags x11 gtk+-3.0 webkit2gtk-4.0) endif diff --git a/src/unix/linux/x11/webview.c b/src/unix/linux/x11/webview.c index 34bde07a..3872acf2 100644 --- a/src/unix/linux/x11/webview.c +++ b/src/unix/linux/x11/webview.c @@ -4,39 +4,392 @@ #include "webview.h" +#include +#include +#include +#include +#include + +#include "matoya.h" +#include "web/keymap.h" + +#define DISPATCH(func, param, should_free) g_idle_add(G_SOURCE_FUNC(func), mty_webview_create_event(ctx, (void *) (param), should_free)) + +struct webview { + MTY_App *app; + MTY_Window window; + WEBVIEW_READY ready_func; + WEBVIEW_TEXT text_func; + WEBVIEW_KEY key_func; + MTY_Hash *keys; + MTY_Queue *pushq; + bool ready; + bool passthrough; + bool debug; + + MTY_Thread *thread; + Display *display; + Window x11_window; + GtkWindow *gtk_window; + WebKitWebView *webview; +}; + +struct mty_webview_event { + struct webview *context; + void *data; + bool should_free; +}; + +struct xinfo { + Display *display; + XVisualInfo *vis; + Window window; +}; + +static struct mty_webview_event *mty_webview_create_event(struct webview *ctx, void *data, bool should_free) +{ + struct mty_webview_event *event = MTY_Alloc(1, sizeof(struct mty_webview_event)); + + event->context = ctx; + event->data = data; + event->should_free = should_free; + + return event; +} + +static void mty_webview_destroy_event(struct mty_webview_event **event) +{ + if (!event || !*event) + return; + + struct mty_webview_event *ev = *event; + + if (ev->should_free) + MTY_Free(ev->data); + + MTY_Free(ev); + *event = NULL; +} + +static void handle_script_message(WebKitUserContentManager *manager, WebKitJavascriptResult *result, void *opaque) +{ + struct webview *ctx = opaque; + + JSCValue *value = webkit_javascript_result_get_js_value(result); + char *str = jsc_value_to_string(value); + + MTY_JSON *j = NULL; + + switch (str[0]) { + // MTY_EVENT_WEBVIEW_READY + case 'R': + ctx->ready = true; + + // Send any queued messages before the WebView became ready + for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { + mty_webview_send_text(ctx, msg); + MTY_Free(msg); + } + + ctx->ready_func(ctx->app, ctx->window); + break; + + // MTY_EVENT_WEBVIEW_TEXT + case 'T': + ctx->text_func(ctx->app, ctx->window, str + 1); + break; + + // MTY_EVENT_KEY + case 'D': + case 'U': + if (!ctx->passthrough) + break; + + j = MTY_JSONParse(str + 1); + if (!j) + break; + + const char *code = MTY_JSONObjGetStringPtr(j, "code"); + if (!code) + break; + + uint32_t jmods = 0; + if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) + break; + + MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; + if (key == MTY_KEY_NONE) + break; + + MTY_Mod mods = web_keymap_mods(jmods); + + ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); + break; + } + + MTY_JSONDestroy(&j); + MTY_Free(str); +} + +static bool _mty_webview_resize(void *opaque) +{ + struct webview *ctx = opaque; + + XWindowAttributes attr = {0}; + XGetWindowAttributes(ctx->display, ctx->x11_window, &attr); + + int32_t width = 0, height = 0; + gtk_window_get_size(ctx->gtk_window, &width, &height); + + if (width != attr.width || height != attr.height) + gtk_window_resize(ctx->gtk_window, attr.width, attr.height); + + return true; +} + +static bool _mty_webview_create(struct mty_webview_event *event) +{ + struct webview *ctx = event->context; + + ctx->gtk_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP)); + gtk_widget_realize(GTK_WIDGET(ctx->gtk_window)); + + GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(ctx->gtk_window)); + XReparentWindow(GDK_WINDOW_XDISPLAY(gdk_window), GDK_WINDOW_XID(gdk_window), ctx->x11_window, 0, 0); + + ctx->webview = WEBKIT_WEB_VIEW(webkit_web_view_new()); + gtk_container_add(GTK_CONTAINER(ctx->gtk_window), GTK_WIDGET(ctx->webview)); + + gtk_widget_set_app_paintable(GTK_WIDGET(ctx->gtk_window), TRUE); + webkit_web_view_set_background_color(ctx->webview, & (GdkRGBA) {0}); + + WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(ctx->webview); + g_signal_connect(manager, "script-message-received::native", G_CALLBACK(handle_script_message), ctx); + webkit_user_content_manager_register_script_message_handler(manager, "native"); + + const char *javascript = + "const __MTY_MSGS = [];" + + "window.addEventListener('message', evt => {" + "if (window.MTY_NativeListener) {" + "window.MTY_NativeListener(evt.data);" + + "} else {" + "__MTY_MSGS.push(evt.data);" + "}" + "});" + + "window.MTY_NativeSendText = text => {" + "window.webkit.messageHandlers.native.postMessage('T' + text);" + "};" + + "window.webkit.messageHandlers.native.postMessage('R');" + + "const __MTY_INTERVAL = setInterval(() => {" + "if (window.MTY_NativeListener) {" + "for (let msg = __MTY_MSGS.shift(); msg; msg = __MTY_MSGS.shift())" + "window.MTY_NativeListener(msg);" + + "clearInterval(__MTY_INTERVAL);" + "}" + "}, 100);" + + "function __mty_key_to_json(evt) {" + "let mods = 0;" + + "if (evt.shiftKey) mods |= 0x01;" + "if (evt.ctrlKey) mods |= 0x02;" + "if (evt.altKey) mods |= 0x04;" + "if (evt.metaKey) mods |= 0x08;" + + "if (evt.getModifierState('CapsLock')) mods |= 0x10;" + "if (evt.getModifierState('NumLock')) mods |= 0x20;" + + "let cmd = evt.type == 'keydown' ? 'D' : 'U';" + "let json = JSON.stringify({'code':evt.code,'mods':mods});" + + "window.webkit.messageHandlers.native.postMessage(cmd + json);" + "}" + + "document.addEventListener('keydown', __mty_key_to_json);" + "document.addEventListener('keyup', __mty_key_to_json);"; + + WebKitUserContentInjectedFrames injected_frames = WEBKIT_USER_CONTENT_INJECT_TOP_FRAME; + WebKitUserScriptInjectionTime injection_time = WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START; + WebKitUserScript *script = webkit_user_script_new(javascript, injected_frames, injection_time, NULL, NULL); + webkit_user_content_manager_add_script(manager, script); + + WebKitSettings *settings = webkit_web_view_get_settings(event->context->webview); + webkit_settings_set_enable_developer_extras(settings, ctx->debug); + + g_idle_add(_mty_webview_resize, ctx); + + gtk_widget_show_all(GTK_WIDGET(ctx->gtk_window)); + + mty_webview_destroy_event(&event); + + return false; +} + +static void *mty_webview_thread_func(void *opaque) +{ + gtk_init_check(0, NULL); + gtk_main(); + + return NULL; +} + struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char *dir, bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func) { - return NULL; + struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); + + g_setenv("GDK_BACKEND", "x11", true); + + ctx->app = app; + ctx->window = window; + ctx->ready_func = ready_func; + ctx->text_func = text_func; + ctx->key_func = key_func; + ctx->debug = debug; + + ctx->keys = web_keymap_hash(); + ctx->pushq = MTY_QueueCreate(50, 0); + ctx->thread = MTY_ThreadCreate(mty_webview_thread_func, NULL); + + struct xinfo *info = MTY_WindowGetNative(ctx->app, ctx->window); + ctx->display = info->display; + ctx->x11_window = info->window; + + DISPATCH(_mty_webview_create, NULL, false); + + return ctx; +} + +static bool _mty_webview_destroy(struct mty_webview_event *event) +{ + struct webview *ctx = event->context; + + if (!ctx) + return false; + + gtk_window_close(ctx->gtk_window); + gtk_widget_destroy(GTK_WIDGET(ctx->webview)); + gtk_widget_destroy(GTK_WIDGET(ctx->gtk_window)); + gtk_main_quit(); + + MTY_Free(ctx); + + mty_webview_destroy_event(&event); + + return false; } void mty_webview_destroy(struct webview **webview) { + if (!webview || !*webview) + return; + + struct webview *ctx = *webview; + *webview = NULL; + + DISPATCH(_mty_webview_destroy, NULL, false); + + MTY_ThreadDestroy(&ctx->thread); + + if (ctx->pushq) + MTY_QueueFlush(ctx->pushq, MTY_Free); + + MTY_QueueDestroy(&ctx->pushq); + MTY_HashDestroy(&ctx->keys, NULL); + + MTY_Free(ctx); +} + +static bool _mty_webview_navigate_url(struct mty_webview_event *event) +{ + webkit_web_view_load_uri(event->context->webview, event->data); + mty_webview_destroy_event(&event); + + return false; +} + +static bool _mty_webview_navigate_html(struct mty_webview_event *event) +{ + webkit_web_view_load_html(event->context->webview, event->data, NULL); + mty_webview_destroy_event(&event); + + return false; +} + +static bool _mty_webview_show(struct mty_webview_event *event) +{ + GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(event->context->gtk_window)); + + if (event->data) { + gdk_window_show(window); + + } else { + gdk_window_hide(window); + } + + return false; +} + +static bool _mty_webview_send_text(struct mty_webview_event *event) +{ + // Need to escape backslash ! + MTY_JSON *json = MTY_JSONStringCreate(event->data); + char *text = MTY_JSONSerialize(json); + char *message = MTY_SprintfD("window.postMessage(%s, '*');", text); + webkit_web_view_run_javascript(event->context->webview, message, NULL, NULL, NULL); + MTY_Free(message); + MTY_Free(text); + MTY_JSONDestroy(&json); + + return false; +} + +static bool _mty_webview_reload(struct mty_webview_event *event) +{ + webkit_web_view_reload(event->context->webview); + + return false; } void mty_webview_navigate(struct webview *ctx, const char *source, bool url) { + if (url) { + DISPATCH(_mty_webview_navigate_url, MTY_Strdup(source), true); + + } else { + DISPATCH(_mty_webview_navigate_html, MTY_Strdup(source), true); + } } void mty_webview_show(struct webview *ctx, bool show) { + DISPATCH(_mty_webview_show, show, false); } bool mty_webview_is_visible(struct webview *ctx) { - return false; + return gdk_window_is_visible(gtk_widget_get_window(GTK_WIDGET(ctx->gtk_window))); } void mty_webview_send_text(struct webview *ctx, const char *msg) { + DISPATCH(_mty_webview_send_text, MTY_Strdup(msg), true); } void mty_webview_reload(struct webview *ctx) { + DISPATCH(_mty_webview_reload, NULL, false); } void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { + ctx->passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) @@ -54,7 +407,7 @@ void mty_webview_render(struct webview *ctx) bool mty_webview_is_focussed(struct webview *ctx) { - return false; + return true; } bool mty_webview_is_steam(void) @@ -64,5 +417,5 @@ bool mty_webview_is_steam(void) bool mty_webview_is_available(void) { - return false; + return true; } From 9fe89b70060d4caf928bc9529f0bdc6ddf3fb519 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:13:18 +0100 Subject: [PATCH 03/20] Feature - [WASM] Webview support --- src/unix/web/matoya-worker.js | 30 +++++++++ src/unix/web/matoya.js | 46 +++++++++++++ src/unix/web/webview.c | 123 ++++++++++++++++++++++++++++++++-- 3 files changed, 195 insertions(+), 4 deletions(-) diff --git a/src/unix/web/matoya-worker.js b/src/unix/web/matoya-worker.js index 1dff330e..cfe64bdb 100644 --- a/src/unix/web/matoya-worker.js +++ b/src/unix/web/matoya-worker.js @@ -815,6 +815,31 @@ const MTY_WEB_API = { setTimeout(step, 0); throw 'MTY_RunAndYield halted execution'; }, + web_webview_create: function(ctx) { + postMessage({type: 'wv-create', ctx}); + }, + web_webview_destroy: function() { + postMessage({type: 'wv-destroy'}); + }, + web_webview_navigate: function(csource, url) { + const source = mty_str_to_js(csource); + postMessage({type: 'wv-navigate', source, url}); + }, + web_webview_show: function(show) { + postMessage({type: 'wv-show', show}); + }, + web_webview_is_visible: function() { + postMessage({type: 'wv-is-visible', sync: MTY.sync, sab: MTY.sab}); + mty_wait(MTY.sync); + return MTY.sab[0] != 0; + }, + web_webview_send_text: function(cmessage) { + const message = mty_str_to_js(cmessage); + postMessage({type: 'wv-send-text', message}); + }, + web_webview_reload: function() { + postMessage({type: 'wv-reload'}); + }, }; @@ -1235,6 +1260,11 @@ onmessage = async (ev) => { mty_free(cmem); break; } + case 'wv-event': + const buf = mty_alloc(1, msg.message.length + 1); + mty_str_to_c(msg.message, buf, msg.message.length + 1); + MTY.exports.mty_webview_handle_event(msg.ctx, buf); + break; } }; diff --git a/src/unix/web/matoya.js b/src/unix/web/matoya.js index 90229262..3c802287 100644 --- a/src/unix/web/matoya.js +++ b/src/unix/web/matoya.js @@ -1082,5 +1082,51 @@ async function mty_thread_message(ev) { mty_signal(msg.sync); break; + case 'wv-create': + MTY.webview = document.createElement('iframe'); + + MTY.webview.style.visibility = 'hidden'; + MTY.webview.style.position = 'fixed'; + MTY.webview.style.border = 'none'; + MTY.webview.style.width = '100%'; + MTY.webview.style.height = '100%'; + MTY.webview.style.inset = '0'; + + MTY.webview.onload = () => { + setTimeout(() => MTY.webview.style.visibility = 'visible', 250); + }; + + window.addEventListener('message', function (message) { + MTY.mainThread.postMessage({type: 'wv-event', ctx: msg.ctx, message: message.data}); + }); + + document.body.appendChild(MTY.webview); + break; + case 'wv-destroy': + document.removeChild(MTY.webview); + delete MTY.webview; + break; + case 'wv-navigate': + if (msg.url) { + MTY.webview.src = msg.source; + + } else { + const blob = new Blob([msg.source], { type: 'text/html' }); + MTY.webview.src = URL.createObjectURL(blob); + } + break; + case 'wv-show': + MTY.webview.style.visibility = msg.show ? 'visible' : 'hidden'; + break; + case 'wv-is-visible': + msg.sab[0] = MTY.webview.style.visibility != 'visible'; + mty_signal(msg.sync); + break; + case 'wv-send-text': + MTY.webview.contentWindow.postMessage(msg.message, '*'); + break; + case 'wv-reload': + MTY.webview.contentWindow.location.reload(); + break; } } diff --git a/src/unix/web/webview.c b/src/unix/web/webview.c index 34bde07a..8c20fbde 100644 --- a/src/unix/web/webview.c +++ b/src/unix/web/webview.c @@ -4,39 +4,98 @@ #include "webview.h" +#include +#include + +#include "matoya.h" +#include "web/keymap.h" + +struct webview { + MTY_App *app; + MTY_Window window; + WEBVIEW_READY ready_func; + WEBVIEW_TEXT text_func; + WEBVIEW_KEY key_func; + MTY_Hash *keys; + MTY_Queue *pushq; + bool ready; + bool passthrough; +}; + +void web_webview_create(struct webview *ctx); +void web_webview_destroy(); +void web_webview_navigate(const char *source, bool url); +void web_webview_show(bool show); +bool web_webview_is_visible(); +void web_webview_send_text(const char *message); +void web_webview_reload(); + struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char *dir, bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func) { - return NULL; + struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); + + ctx->app = app; + ctx->window = window; + ctx->ready_func = ready_func; + ctx->text_func = text_func; + ctx->key_func = key_func; + + ctx->keys = web_keymap_hash(); + ctx->pushq = MTY_QueueCreate(50, 0); + + web_webview_create(ctx); + + return ctx; } void mty_webview_destroy(struct webview **webview) { + if (!webview || !*webview) + return; + + struct webview *ctx = *webview; + *webview = NULL; + + web_webview_destroy(); + + if (ctx->pushq) + MTY_QueueFlush(ctx->pushq, MTY_Free); + + MTY_QueueDestroy(&ctx->pushq); + MTY_HashDestroy(&ctx->keys, NULL); + + MTY_Free(ctx); } void mty_webview_navigate(struct webview *ctx, const char *source, bool url) { + web_webview_navigate(source, url); } void mty_webview_show(struct webview *ctx, bool show) { + web_webview_show(show); } bool mty_webview_is_visible(struct webview *ctx) { - return false; + return web_webview_is_visible(); } void mty_webview_send_text(struct webview *ctx, const char *msg) { + web_webview_send_text(msg); } void mty_webview_reload(struct webview *ctx) { + web_webview_reload(); } void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { + ctx->passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) @@ -54,7 +113,7 @@ void mty_webview_render(struct webview *ctx) bool mty_webview_is_focussed(struct webview *ctx) { - return false; + return true; } bool mty_webview_is_steam(void) @@ -64,5 +123,61 @@ bool mty_webview_is_steam(void) bool mty_webview_is_available(void) { - return false; + return true; +} + +__attribute__((export_name("mty_webview_handle_event"))) +void mty_webview_handle_event(struct webview *ctx, char *str) +{ + MTY_JSON *j = NULL; + + switch (str[0]) { + // MTY_EVENT_WEBVIEW_READY + case 'R': + ctx->ready = true; + + // Send any queued messages before the WebView became ready + for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { + mty_webview_send_text(ctx, msg); + MTY_Free(msg); + } + + ctx->ready_func(ctx->app, ctx->window); + break; + + // MTY_EVENT_WEBVIEW_TEXT + case 'T': + ctx->text_func(ctx->app, ctx->window, str + 1); + break; + + // MTY_EVENT_KEY + case 'D': + case 'U': + if (!ctx->passthrough) + break; + + j = MTY_JSONParse(str + 1); + if (!j) + break; + + const char *code = MTY_JSONObjGetStringPtr(j, "code"); + if (!code) + break; + + uint32_t jmods = 0; + if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) + break; + + MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; + if (key == MTY_KEY_NONE) + break; + + MTY_Mod mods = web_keymap_mods(jmods); + + ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); + break; + } + + MTY_JSONDestroy(&j); + MTY_Free(str); } From 9057e41221c68e4a4c09c5b015e80c7385c8cf85 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:00:43 +0100 Subject: [PATCH 04/20] Custom script moved to `parsec-ui` --- src/unix/apple/webview.m | 59 ++++-------------------------- src/unix/linux/android/Matoya.java | 50 +------------------------ src/unix/linux/x11/webview.c | 49 +------------------------ src/windows/webview.c | 49 +------------------------ 4 files changed, 11 insertions(+), 196 deletions(-) diff --git a/src/unix/apple/webview.m b/src/unix/apple/webview.m index cea9189f..8862ec7d 100644 --- a/src/unix/apple/webview.m +++ b/src/unix/apple/webview.m @@ -186,52 +186,8 @@ static bool mty_webview_supported(void) [ctx->webview.configuration.userContentController addScriptMessageHandler:handler name:@"native"]; // MTY javascript shim - WKUserScript *script = [[WKUserScript alloc] initWithSource: - @"const __MTY_MSGS = [];" - - @"let __MTY_WEBVIEW = b64 => {" - @"__MTY_MSGS.push(b64);" - @"};" - - @"window.MTY_NativeSendText = text => {" - @"window.webkit.messageHandlers.native.postMessage('T' + text);" - @"};" - - @"window.webkit.messageHandlers.native.postMessage('R');" - - @"const __MTY_INTERVAL = setInterval(() => {" - @"if (window.MTY_NativeListener) {" - @"__MTY_WEBVIEW = b64 => {window.MTY_NativeListener(atob(b64));};" - - @"for (let msg = __MTY_MSGS.shift(); msg; msg = __MTY_MSGS.shift())" - @"__MTY_WEBVIEW(msg);" - - @"clearInterval(__MTY_INTERVAL);" - @"}" - @"}, 100);" - - @"function __mty_key_to_json(evt) {" - @"let mods = 0;" - - @"if (evt.shiftKey) mods |= 0x01;" - @"if (evt.ctrlKey) mods |= 0x02;" - @"if (evt.altKey) mods |= 0x04;" - @"if (evt.metaKey) mods |= 0x08;" - - @"if (evt.getModifierState('CapsLock')) mods |= 0x10;" - @"if (evt.getModifierState('NumLock')) mods |= 0x20;" - - @"let cmd = evt.type == 'keydown' ? 'D' : 'U';" - @"let json = JSON.stringify({'code':evt.code,'mods':mods});" - - @"window.webkit.messageHandlers.native.postMessage(cmd + json);" - @"}" - - @"document.addEventListener('keydown', __mty_key_to_json);" - @"document.addEventListener('keyup', __mty_key_to_json);" - - injectionTime:WKUserScriptInjectionTimeAtDocumentStart - forMainFrameOnly:YES + WKUserScript *script = [[WKUserScript alloc] initWithSource:@"window.parent = window.webkit.messageHandlers.native;" + injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES ]; [ctx->webview.configuration.userContentController addUserScript:script]; @@ -309,14 +265,15 @@ void mty_webview_send_text(struct webview *ctx, const char *msg) MTY_QueuePushPtr(ctx->pushq, MTY_Strdup(msg), 0); } else { - __block NSString *omsg = [NSString stringWithUTF8String:msg]; - - NSString *b64 = [[omsg dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0]; - NSString *wrapped = [NSString stringWithFormat:@"__MTY_WEBVIEW('%@');", b64]; + MTY_JSON *json = MTY_JSONStringCreate(msg); + char *text = MTY_JSONSerialize(json); + NSString *wrapped = [NSString stringWithFormat:@"window.postMessage(%@, '*');", [NSString stringWithUTF8String:text]]; + MTY_Free(text); + MTY_JSONDestroy(&json); [ctx->webview evaluateJavaScript:wrapped completionHandler:^(id object, NSError *error) { if (error) - NSLog(@"'WKWebView:evaluateJavaScript' failed to evaluate string '%@'", omsg); + MTY_Log("'WKWebView:evaluateJavaScript' failed to evaluate string '%s'", text); }]; } } diff --git a/src/unix/linux/android/Matoya.java b/src/unix/linux/android/Matoya.java index 5c99319d..98f50729 100644 --- a/src/unix/linux/android/Matoya.java +++ b/src/unix/linux/android/Matoya.java @@ -579,55 +579,7 @@ public void postMessage(String text) { webview.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView webview, String url, Bitmap favicon) { - String script = String.join("\n", - "const __MTY_MSGS = [];", - - "window.addEventListener('message', evt => {", - "if (window.MTY_NativeListener) {", - "window.MTY_NativeListener(evt.data);", - - "} else {", - "__MTY_MSGS.push(evt.data);", - "}", - "});", - - "window.MTY_NativeSendText = text => {", - "native.postMessage('T' + text);", - "};", - - "native.postMessage('R');", - - "const __MTY_INTERVAL = setInterval(() => {", - "if (window.MTY_NativeListener) {", - "for (let msg = __MTY_MSGS.shift(); msg; msg = __MTY_MSGS.shift())", - "window.MTY_NativeListener(msg);", - - "clearInterval(__MTY_INTERVAL);", - "}", - "}, 100);", - - "function __mty_key_to_json(evt) {", - "let mods = 0;", - - "if (evt.shiftKey) mods |= 0x01;", - "if (evt.ctrlKey) mods |= 0x02;", - "if (evt.altKey) mods |= 0x04;", - "if (evt.metaKey) mods |= 0x08;", - - "if (evt.getModifierState('CapsLock')) mods |= 0x10;", - "if (evt.getModifierState('NumLock')) mods |= 0x20;", - - "let cmd = evt.type == 'keydown' ? 'D' : 'U';", - "let json = JSON.stringify({'code':evt.code,'mods':mods});", - - "native.postMessage(cmd + json);", - "}", - - "document.addEventListener('keydown', __mty_key_to_json);", - "document.addEventListener('keyup', __mty_key_to_json);" - ); - - webview.evaluateJavascript(script, null); + webview.evaluateJavascript("window.parent = native;", null); } }); diff --git a/src/unix/linux/x11/webview.c b/src/unix/linux/x11/webview.c index 3872acf2..a020ac3e 100644 --- a/src/unix/linux/x11/webview.c +++ b/src/unix/linux/x11/webview.c @@ -167,53 +167,7 @@ static bool _mty_webview_create(struct mty_webview_event *event) g_signal_connect(manager, "script-message-received::native", G_CALLBACK(handle_script_message), ctx); webkit_user_content_manager_register_script_message_handler(manager, "native"); - const char *javascript = - "const __MTY_MSGS = [];" - - "window.addEventListener('message', evt => {" - "if (window.MTY_NativeListener) {" - "window.MTY_NativeListener(evt.data);" - - "} else {" - "__MTY_MSGS.push(evt.data);" - "}" - "});" - - "window.MTY_NativeSendText = text => {" - "window.webkit.messageHandlers.native.postMessage('T' + text);" - "};" - - "window.webkit.messageHandlers.native.postMessage('R');" - - "const __MTY_INTERVAL = setInterval(() => {" - "if (window.MTY_NativeListener) {" - "for (let msg = __MTY_MSGS.shift(); msg; msg = __MTY_MSGS.shift())" - "window.MTY_NativeListener(msg);" - - "clearInterval(__MTY_INTERVAL);" - "}" - "}, 100);" - - "function __mty_key_to_json(evt) {" - "let mods = 0;" - - "if (evt.shiftKey) mods |= 0x01;" - "if (evt.ctrlKey) mods |= 0x02;" - "if (evt.altKey) mods |= 0x04;" - "if (evt.metaKey) mods |= 0x08;" - - "if (evt.getModifierState('CapsLock')) mods |= 0x10;" - "if (evt.getModifierState('NumLock')) mods |= 0x20;" - - "let cmd = evt.type == 'keydown' ? 'D' : 'U';" - "let json = JSON.stringify({'code':evt.code,'mods':mods});" - - "window.webkit.messageHandlers.native.postMessage(cmd + json);" - "}" - - "document.addEventListener('keydown', __mty_key_to_json);" - "document.addEventListener('keyup', __mty_key_to_json);"; - + const char *javascript = "window.parent = window.webkit.messageHandlers.native;"; WebKitUserContentInjectedFrames injected_frames = WEBKIT_USER_CONTENT_INJECT_TOP_FRAME; WebKitUserScriptInjectionTime injection_time = WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START; WebKitUserScript *script = webkit_user_script_new(javascript, injected_frames, injection_time, NULL, NULL); @@ -338,7 +292,6 @@ static bool _mty_webview_show(struct mty_webview_event *event) static bool _mty_webview_send_text(struct mty_webview_event *event) { - // Need to escape backslash ! MTY_JSON *json = MTY_JSONStringCreate(event->data); char *text = MTY_JSONSerialize(json); char *message = MTY_SprintfD("window.postMessage(%s, '*');", text); diff --git a/src/windows/webview.c b/src/windows/webview.c index e3ee5ec2..37d6832d 100644 --- a/src/windows/webview.c +++ b/src/windows/webview.c @@ -283,54 +283,7 @@ static HRESULT STDMETHODCALLTYPE h1_Invoke(ICoreWebView2CreateCoreWebView2Contro ICoreWebView2_add_WebMessageReceived(ctx->webview, (ICoreWebView2WebMessageReceivedEventHandler *) &ctx->handler2, &token); - const WCHAR *script = - L"const __MTY_MSGS = [];" - - L"window.chrome.webview.addEventListener('message', evt => {" - L"if (window.MTY_NativeListener) {" - L"window.MTY_NativeListener(evt.data);" - - L"} else {" - L"__MTY_MSGS.push(evt.data);" - L"}" - L"});" - - L"window.MTY_NativeSendText = text => {" - L"window.chrome.webview.postMessage('T' + text);" - L"};" - - L"window.chrome.webview.postMessage('R');" - - L"const __MTY_INTERVAL = setInterval(() => {" - L"if (window.MTY_NativeListener) {" - L"for (let msg = __MTY_MSGS.shift(); msg; msg = __MTY_MSGS.shift())" - L"window.MTY_NativeListener(msg);" - - L"clearInterval(__MTY_INTERVAL);" - L"}" - L"}, 100);" - - L"function __mty_key_to_json(evt) {" - L"let mods = 0;" - - L"if (evt.shiftKey) mods |= 0x01;" - L"if (evt.ctrlKey) mods |= 0x02;" - L"if (evt.altKey) mods |= 0x04;" - L"if (evt.metaKey) mods |= 0x08;" - - L"if (evt.getModifierState('CapsLock')) mods |= 0x10;" - L"if (evt.getModifierState('NumLock')) mods |= 0x20;" - - L"let cmd = evt.type == 'keydown' ? 'D' : 'U';" - L"let json = JSON.stringify({'code':evt.code,'mods':mods});" - - L"window.chrome.webview.postMessage(cmd + json);" - L"}" - - L"document.addEventListener('keydown', __mty_key_to_json);" - L"document.addEventListener('keyup', __mty_key_to_json);"; - - ICoreWebView2_AddScriptToExecuteOnDocumentCreated(ctx->webview, script, NULL); + ICoreWebView2_AddScriptToExecuteOnDocumentCreated(ctx->webview, L"window.parent = window.chrome.webview;", NULL); if (ctx->source) webview_navigate(ctx, ctx->source, ctx->url); From 0dab25fd7b7422c5ab99a3757eb392e150c9b6f9 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:10:46 +0100 Subject: [PATCH 05/20] Mostly remove duplicated code --- Android.mk | 1 + GNUmakefile | 1 + makefile | 1 + src/unix/apple/webview.m | 86 +++---------------------- src/unix/linux/android/webview.c | 86 +++---------------------- src/unix/linux/x11/webview.c | 89 +++----------------------- src/unix/web/webview.c | 88 ++++---------------------- src/webview.c | 80 ++++++++++++++++++++++++ src/webview.h | 20 ++++++ src/windows/webview.c | 104 +++++-------------------------- 10 files changed, 155 insertions(+), 401 deletions(-) create mode 100644 src/webview.c diff --git a/Android.mk b/Android.mk index 38a9ee6c..7091f917 100644 --- a/Android.mk +++ b/Android.mk @@ -57,6 +57,7 @@ LOCAL_SRC_FILES := \ src/thread.c \ src/tlocal.c \ src/version.c \ + src/webview.c \ src/gfx/gl/gl.c \ src/gfx/gl/gl-ui.c \ src/gfx/vk/vk.c \ diff --git a/GNUmakefile b/GNUmakefile index 5905ccf9..8f3e91e4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -38,6 +38,7 @@ OBJS = \ src/thread.o \ src/tlocal.o \ src/version.o \ + src/webview.o \ src/hid/utils.o \ src/unix/compress.o \ src/unix/file.o \ diff --git a/makefile b/makefile index cd4789d4..70ef88ff 100644 --- a/makefile +++ b/makefile @@ -41,6 +41,7 @@ OBJS = \ src\thread.obj \ src\tlocal.obj \ src\version.obj \ + src\webview.obj \ src\gfx\vk\vk.obj \ src\gfx\vk\vk-ctx.obj \ src\gfx\vk\vk-ui.obj \ diff --git a/src/unix/apple/webview.m b/src/unix/apple/webview.m index 8862ec7d..7d901cb2 100644 --- a/src/unix/apple/webview.m +++ b/src/unix/apple/webview.m @@ -7,20 +7,12 @@ #include #include "objc.h" -#include "web/keymap.h" struct webview { - MTY_App *app; - MTY_Window window; - MTY_Hash *keys; - MTY_Time ts; - WEBVIEW_READY ready_func; - WEBVIEW_TEXT text_func; - WEBVIEW_KEY key_func; - MTY_Queue *pushq; + struct webview_base base; + WKWebView *webview; - bool ready; - bool passthrough; + MTY_Time ts; }; @@ -55,56 +47,7 @@ static void msg_handler_userContentController_didReceiveScriptMessage(id self, S struct webview *ctx = OBJC_CTX(); const char *str = [message.body UTF8String]; - MTY_JSON *j = NULL; - - switch (str[0]) { - // MTY_EVENT_WEBVIEW_READY - case 'R': - ctx->ready = true; - - // Send any queued messages before the WebView became ready - for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { - mty_webview_send_text(ctx, msg); - MTY_Free(msg); - } - - ctx->ready_func(ctx->app, ctx->window); - break; - - // MTY_EVENT_WEBVIEW_TEXT - case 'T': - ctx->text_func(ctx->app, ctx->window, str + 1); - break; - - // MTY_EVENT_KEY - case 'D': - case 'U': - if (!ctx->passthrough) - break; - - j = MTY_JSONParse(str + 1); - if (!j) - break; - - const char *code = MTY_JSONObjGetStringPtr(j, "code"); - if (!code) - break; - - uint32_t jmods = 0; - if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) - break; - - MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; - if (key == MTY_KEY_NONE) - break; - - MTY_Mod mods = web_keymap_mods(jmods); - - ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); - break; - } - - MTY_JSONDestroy(&j); + mty_webview_base_handle_event(&ctx->base, str); } static Class msg_handler_class(void) @@ -145,14 +88,7 @@ static bool mty_webview_supported(void) struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); - ctx->app = app; - ctx->window = window; - ctx->ready_func = ready_func; - ctx->text_func = text_func; - ctx->key_func = key_func; - - ctx->keys = web_keymap_hash(); - ctx->pushq = MTY_QueueCreate(50, 0); + mty_webview_base_create(&ctx->base, app, window, dir, debug, ready_func, text_func, key_func); // WKWebView creation, start hidden #if TARGET_OS_OSX @@ -207,11 +143,7 @@ void mty_webview_destroy(struct webview **webview) ctx->webview = nil; } - if (ctx->pushq) - MTY_QueueFlush(ctx->pushq, MTY_Free); - - MTY_QueueDestroy(&ctx->pushq); - MTY_HashDestroy(&ctx->keys, NULL); + mty_webview_base_destroy(&ctx->base); MTY_Free(ctx); *webview = NULL; @@ -261,8 +193,8 @@ bool mty_webview_is_visible(struct webview *ctx) void mty_webview_send_text(struct webview *ctx, const char *msg) { - if (!ctx->ready) { - MTY_QueuePushPtr(ctx->pushq, MTY_Strdup(msg), 0); + if (!ctx->base.ready) { + MTY_QueuePushPtr(ctx->base.pushq, MTY_Strdup(msg), 0); } else { MTY_JSON *json = MTY_JSONStringCreate(msg); @@ -285,7 +217,7 @@ void mty_webview_reload(struct webview *ctx) void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { - ctx->passthrough = passthrough; + ctx->base.passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) diff --git a/src/unix/linux/android/webview.c b/src/unix/linux/android/webview.c index 1bd69771..24c836bc 100644 --- a/src/unix/linux/android/webview.c +++ b/src/unix/linux/android/webview.c @@ -10,18 +10,9 @@ #include "matoya.h" #include "app-os.h" #include "jnih.h" -#include "web/keymap.h" struct webview { - MTY_App *app; - MTY_Window window; - WEBVIEW_READY ready_func; - WEBVIEW_TEXT text_func; - WEBVIEW_KEY key_func; - MTY_Hash *keys; - MTY_Queue *pushq; - bool ready; - bool passthrough; + struct webview_base base; }; struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char *dir, @@ -29,15 +20,7 @@ struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char * { struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); - ctx->app = app; - ctx->window = window; - ctx->ready_func = ready_func; - ctx->text_func = text_func; - ctx->key_func = key_func; - - ctx->keys = web_keymap_hash(); - ctx->pushq = MTY_QueueCreate(50, 0); - + mty_webview_base_create(&ctx->base, app, window, dir, debug, ready_func, text_func, key_func); mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewCreate", "(JZ)V", (long) ctx, debug); return ctx; @@ -52,12 +35,7 @@ void mty_webview_destroy(struct webview **webview) *webview = NULL; mty_jni_void(MTY_GetJNIEnv(), mty_app_get_obj(), "webviewDestroy", "()V"); - - if (ctx->pushq) - MTY_QueueFlush(ctx->pushq, MTY_Free); - - MTY_QueueDestroy(&ctx->pushq); - MTY_HashDestroy(&ctx->keys, NULL); + mty_webview_base_destroy(&ctx->base); MTY_Free(ctx); } @@ -81,8 +59,8 @@ bool mty_webview_is_visible(struct webview *ctx) void mty_webview_send_text(struct webview *ctx, const char *msg) { - if (!ctx->ready) { - MTY_QueuePushPtr(ctx->pushq, MTY_Strdup(msg), 0); + if (!ctx->base.ready) { + MTY_QueuePushPtr(ctx->base.pushq, MTY_Strdup(msg), 0); } else { jstring jmsg = mty_jni_strdup(MTY_GetJNIEnv(), msg); @@ -98,7 +76,7 @@ void mty_webview_reload(struct webview *ctx) void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { - ctx->passthrough = passthrough; + ctx->base.passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) @@ -135,56 +113,6 @@ JNIEXPORT void JNICALL Java_group_matoya_lib_Matoya_webview_1handle_1event(JNIEn { struct webview *ctx = (struct webview *) jctx; char *str = mty_jni_cstrmov(MTY_GetJNIEnv(), jstr); - - MTY_JSON *j = NULL; - - switch (str[0]) { - // MTY_EVENT_WEBVIEW_READY - case 'R': - ctx->ready = true; - - // Send any queued messages before the WebView became ready - for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { - mty_webview_send_text(ctx, msg); - MTY_Free(msg); - } - - ctx->ready_func(ctx->app, ctx->window); - break; - - // MTY_EVENT_WEBVIEW_TEXT - case 'T': - ctx->text_func(ctx->app, ctx->window, str + 1); - break; - - // MTY_EVENT_KEY - case 'D': - case 'U': - if (!ctx->passthrough) - break; - - j = MTY_JSONParse(str + 1); - if (!j) - break; - - const char *code = MTY_JSONObjGetStringPtr(j, "code"); - if (!code) - break; - - uint32_t jmods = 0; - if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) - break; - - MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; - if (key == MTY_KEY_NONE) - break; - - MTY_Mod mods = web_keymap_mods(jmods); - - ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); - break; - } - - MTY_JSONDestroy(&j); + mty_webview_base_handle_event(&ctx->base, str); MTY_Free(str); } diff --git a/src/unix/linux/x11/webview.c b/src/unix/linux/x11/webview.c index a020ac3e..35c951f6 100644 --- a/src/unix/linux/x11/webview.c +++ b/src/unix/linux/x11/webview.c @@ -11,21 +11,11 @@ #include #include "matoya.h" -#include "web/keymap.h" #define DISPATCH(func, param, should_free) g_idle_add(G_SOURCE_FUNC(func), mty_webview_create_event(ctx, (void *) (param), should_free)) struct webview { - MTY_App *app; - MTY_Window window; - WEBVIEW_READY ready_func; - WEBVIEW_TEXT text_func; - WEBVIEW_KEY key_func; - MTY_Hash *keys; - MTY_Queue *pushq; - bool ready; - bool passthrough; - bool debug; + struct webview_base base; MTY_Thread *thread; Display *display; @@ -77,61 +67,11 @@ static void handle_script_message(WebKitUserContentManager *manager, WebKitJavas JSCValue *value = webkit_javascript_result_get_js_value(result); char *str = jsc_value_to_string(value); - - MTY_JSON *j = NULL; - - switch (str[0]) { - // MTY_EVENT_WEBVIEW_READY - case 'R': - ctx->ready = true; - - // Send any queued messages before the WebView became ready - for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { - mty_webview_send_text(ctx, msg); - MTY_Free(msg); - } - - ctx->ready_func(ctx->app, ctx->window); - break; - - // MTY_EVENT_WEBVIEW_TEXT - case 'T': - ctx->text_func(ctx->app, ctx->window, str + 1); - break; - - // MTY_EVENT_KEY - case 'D': - case 'U': - if (!ctx->passthrough) - break; - - j = MTY_JSONParse(str + 1); - if (!j) - break; - - const char *code = MTY_JSONObjGetStringPtr(j, "code"); - if (!code) - break; - - uint32_t jmods = 0; - if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) - break; - - MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; - if (key == MTY_KEY_NONE) - break; - - MTY_Mod mods = web_keymap_mods(jmods); - - ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); - break; - } - - MTY_JSONDestroy(&j); + mty_webview_base_handle_event(&ctx->base, str); MTY_Free(str); } -static bool _mty_webview_resize(void *opaque) +static int32_t _mty_webview_resize(void *opaque) { struct webview *ctx = opaque; @@ -174,7 +114,7 @@ static bool _mty_webview_create(struct mty_webview_event *event) webkit_user_content_manager_add_script(manager, script); WebKitSettings *settings = webkit_web_view_get_settings(event->context->webview); - webkit_settings_set_enable_developer_extras(settings, ctx->debug); + webkit_settings_set_enable_developer_extras(settings, ctx->base.debug); g_idle_add(_mty_webview_resize, ctx); @@ -200,18 +140,11 @@ struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char * g_setenv("GDK_BACKEND", "x11", true); - ctx->app = app; - ctx->window = window; - ctx->ready_func = ready_func; - ctx->text_func = text_func; - ctx->key_func = key_func; - ctx->debug = debug; + mty_webview_base_create(&ctx->base, app, window, dir, debug, ready_func, text_func, key_func); - ctx->keys = web_keymap_hash(); - ctx->pushq = MTY_QueueCreate(50, 0); ctx->thread = MTY_ThreadCreate(mty_webview_thread_func, NULL); - struct xinfo *info = MTY_WindowGetNative(ctx->app, ctx->window); + struct xinfo *info = MTY_WindowGetNative(ctx->base.app, ctx->base.window); ctx->display = info->display; ctx->x11_window = info->window; @@ -251,11 +184,7 @@ void mty_webview_destroy(struct webview **webview) MTY_ThreadDestroy(&ctx->thread); - if (ctx->pushq) - MTY_QueueFlush(ctx->pushq, MTY_Free); - - MTY_QueueDestroy(&ctx->pushq); - MTY_HashDestroy(&ctx->keys, NULL); + mty_webview_base_destroy(&ctx->base); MTY_Free(ctx); } @@ -295,7 +224,7 @@ static bool _mty_webview_send_text(struct mty_webview_event *event) MTY_JSON *json = MTY_JSONStringCreate(event->data); char *text = MTY_JSONSerialize(json); char *message = MTY_SprintfD("window.postMessage(%s, '*');", text); - webkit_web_view_run_javascript(event->context->webview, message, NULL, NULL, NULL); + webkit_web_view_evaluate_javascript(event->context->webview, message, -1, NULL, NULL, NULL, NULL, NULL); MTY_Free(message); MTY_Free(text); MTY_JSONDestroy(&json); @@ -342,7 +271,7 @@ void mty_webview_reload(struct webview *ctx) void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { - ctx->passthrough = passthrough; + ctx->base.passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) diff --git a/src/unix/web/webview.c b/src/unix/web/webview.c index 8c20fbde..72adc8c3 100644 --- a/src/unix/web/webview.c +++ b/src/unix/web/webview.c @@ -8,18 +8,9 @@ #include #include "matoya.h" -#include "web/keymap.h" struct webview { - MTY_App *app; - MTY_Window window; - WEBVIEW_READY ready_func; - WEBVIEW_TEXT text_func; - WEBVIEW_KEY key_func; - MTY_Hash *keys; - MTY_Queue *pushq; - bool ready; - bool passthrough; + struct webview_base base; }; void web_webview_create(struct webview *ctx); @@ -35,15 +26,7 @@ struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char * { struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); - ctx->app = app; - ctx->window = window; - ctx->ready_func = ready_func; - ctx->text_func = text_func; - ctx->key_func = key_func; - - ctx->keys = web_keymap_hash(); - ctx->pushq = MTY_QueueCreate(50, 0); - + mty_webview_base_create(&ctx->base, app, window, dir, debug, ready_func, text_func, key_func); web_webview_create(ctx); return ctx; @@ -58,12 +41,7 @@ void mty_webview_destroy(struct webview **webview) *webview = NULL; web_webview_destroy(); - - if (ctx->pushq) - MTY_QueueFlush(ctx->pushq, MTY_Free); - - MTY_QueueDestroy(&ctx->pushq); - MTY_HashDestroy(&ctx->keys, NULL); + mty_webview_base_destroy(&ctx->base); MTY_Free(ctx); } @@ -85,7 +63,12 @@ bool mty_webview_is_visible(struct webview *ctx) void mty_webview_send_text(struct webview *ctx, const char *msg) { - web_webview_send_text(msg); + if (!ctx->base.ready) { + MTY_QueuePushPtr(ctx->base.pushq, MTY_Strdup(msg), 0); + + } else { + web_webview_send_text(msg); + } } void mty_webview_reload(struct webview *ctx) @@ -95,7 +78,7 @@ void mty_webview_reload(struct webview *ctx) void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { - ctx->passthrough = passthrough; + ctx->base.passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) @@ -129,55 +112,6 @@ bool mty_webview_is_available(void) __attribute__((export_name("mty_webview_handle_event"))) void mty_webview_handle_event(struct webview *ctx, char *str) { - MTY_JSON *j = NULL; - - switch (str[0]) { - // MTY_EVENT_WEBVIEW_READY - case 'R': - ctx->ready = true; - - // Send any queued messages before the WebView became ready - for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { - mty_webview_send_text(ctx, msg); - MTY_Free(msg); - } - - ctx->ready_func(ctx->app, ctx->window); - break; - - // MTY_EVENT_WEBVIEW_TEXT - case 'T': - ctx->text_func(ctx->app, ctx->window, str + 1); - break; - - // MTY_EVENT_KEY - case 'D': - case 'U': - if (!ctx->passthrough) - break; - - j = MTY_JSONParse(str + 1); - if (!j) - break; - - const char *code = MTY_JSONObjGetStringPtr(j, "code"); - if (!code) - break; - - uint32_t jmods = 0; - if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) - break; - - MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; - if (key == MTY_KEY_NONE) - break; - - MTY_Mod mods = web_keymap_mods(jmods); - - ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); - break; - } - - MTY_JSONDestroy(&j); + mty_webview_base_handle_event(&ctx->base, str); MTY_Free(str); } diff --git a/src/webview.c b/src/webview.c new file mode 100644 index 00000000..1b26eca7 --- /dev/null +++ b/src/webview.c @@ -0,0 +1,80 @@ +#include "webview.h" + +#include "web/keymap.h" + +void mty_webview_base_create(struct webview_base *ctx, MTY_App *app, MTY_Window window, const char *dir, + bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func) +{ + ctx->app = app; + ctx->window = window; + ctx->ready_func = ready_func; + ctx->text_func = text_func; + ctx->key_func = key_func; + ctx->keys = web_keymap_hash(); + ctx->pushq = MTY_QueueCreate(50, 0); + ctx->debug = debug; + ctx->dir = dir; +} + +void mty_webview_base_destroy(struct webview_base *ctx) +{ + if (ctx->pushq) + MTY_QueueFlush(ctx->pushq, MTY_Free); + + MTY_QueueDestroy(&ctx->pushq); + MTY_HashDestroy(&ctx->keys, NULL); +} + +void mty_webview_base_handle_event(struct webview_base *ctx, const char *str) +{ + MTY_JSON *j = NULL; + + switch (str[0]) { + // MTY_EVENT_WEBVIEW_READY + case 'R': + ctx->ready = true; + + // Send any queued messages before the WebView became ready + for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { + mty_webview_send_text((struct webview *) ctx, msg); + MTY_Free(msg); + } + + ctx->ready_func(ctx->app, ctx->window); + break; + + // MTY_EVENT_WEBVIEW_TEXT + case 'T': + ctx->text_func(ctx->app, ctx->window, str + 1); + break; + + // MTY_EVENT_KEY + case 'D': + case 'U': + if (!ctx->passthrough) + break; + + j = MTY_JSONParse(str + 1); + if (!j) + break; + + const char *code = MTY_JSONObjGetStringPtr(j, "code"); + if (!code) + break; + + uint32_t jmods = 0; + if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) + break; + + MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; + if (key == MTY_KEY_NONE) + break; + + MTY_Mod mods = web_keymap_mods(jmods); + + ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); + break; + } + + MTY_JSONDestroy(&j); +} diff --git a/src/webview.h b/src/webview.h index e1bea375..ca64b618 100644 --- a/src/webview.h +++ b/src/webview.h @@ -12,6 +12,26 @@ typedef void (*WEBVIEW_READY)(MTY_App *app, MTY_Window window); typedef void (*WEBVIEW_TEXT)(MTY_App *app, MTY_Window window, const char *text); typedef void (*WEBVIEW_KEY)(MTY_App *app, MTY_Window window, bool pressed, MTY_Key key, MTY_Mod mods); +struct webview_base { + MTY_App *app; + MTY_Window window; + WEBVIEW_READY ready_func; + WEBVIEW_TEXT text_func; + WEBVIEW_KEY key_func; + MTY_Queue *pushq; + MTY_Hash *keys; + const char *dir; + bool ready; + bool passthrough; + bool focussed; + bool debug; +}; + +void mty_webview_base_create(struct webview_base *ctx, MTY_App *app, MTY_Window window, const char *dir, + bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func); +void mty_webview_base_destroy(struct webview_base *ctx); +void mty_webview_base_handle_event(struct webview_base *ctx, const char *str); + struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char *dir, bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func); void mty_webview_destroy(struct webview **webview); diff --git a/src/windows/webview.c b/src/windows/webview.c index 37d6832d..f9b82e13 100644 --- a/src/windows/webview.c +++ b/src/windows/webview.c @@ -13,8 +13,6 @@ #define COBJMACROS #include "webview2.h" -#include "unix/web/keymap.h" - // https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#detect-if-a-webview2-runtime-is-already-installed #define WEBVIEW_REG_PATH L"Software\\Microsoft\\EdgeUpdate\\%s\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" @@ -50,13 +48,8 @@ struct webview_handler3 { }; struct webview { - MTY_App *app; - MTY_Window window; - MTY_Hash *keys; - WEBVIEW_READY ready_func; - WEBVIEW_TEXT text_func; - WEBVIEW_KEY key_func; - MTY_Queue *pushq; + struct webview_base base; + HMODULE lib; ICoreWebView2Controller2 *controller; ICoreWebView2 *webview; @@ -68,10 +61,6 @@ struct webview { ICoreWebView2EnvironmentOptions opts; WCHAR *source; bool url; - bool debug; - bool passthrough; - bool focussed; - bool ready; }; @@ -112,9 +101,9 @@ static HRESULT STDMETHODCALLTYPE h3_Invoke_GotFocus(ICoreWebView2FocusChangedEve struct webview_handler3 *handler = (struct webview_handler3 *) This; struct webview *ctx = handler->opaque; - ctx->focussed = true; + ctx->base.focussed = true; if (mty_webview_is_visible(ctx)) - PostMessage(MTY_WindowGetNative(ctx->app, ctx->window), WM_SETFOCUS, 0, 0); + PostMessage(MTY_WindowGetNative(ctx->base.app, ctx->base.window), WM_SETFOCUS, 0, 0); return S_OK; } @@ -125,9 +114,9 @@ static HRESULT STDMETHODCALLTYPE h3_Invoke_LostFocus(ICoreWebView2FocusChangedEv struct webview_handler3 *handler = (struct webview_handler3 *) This; struct webview *ctx = handler->opaque; - ctx->focussed = false; + ctx->base.focussed = false; if (mty_webview_is_visible(ctx)) - PostMessage(MTY_WindowGetNative(ctx->app, ctx->window), WM_KILLFOCUS, 0, 0); + PostMessage(MTY_WindowGetNative(ctx->base.app, ctx->base.window), WM_KILLFOCUS, 0, 0); return S_OK; } @@ -157,56 +146,7 @@ static HRESULT STDMETHODCALLTYPE h2_Invoke(ICoreWebView2WebMessageReceivedEventH if (e == S_OK) { char *str = MTY_WideToMultiD(wstr); - MTY_JSON *j = NULL; - - switch (str[0]) { - // MTY_EVENT_WEBVIEW_READY - case 'R': - ctx->ready = true; - - // Send any queued messages before the WebView became ready - for (WCHAR *wmsg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, &wmsg, NULL);) { - ICoreWebView2_PostWebMessageAsString(ctx->webview, wmsg); - MTY_Free(wmsg); - } - - ctx->ready_func(ctx->app, ctx->window); - break; - - // MTY_EVENT_WEBVIEW_TEXT - case 'T': - ctx->text_func(ctx->app, ctx->window, str + 1); - break; - - // MTY_EVENT_KEY - case 'D': - case 'U': - if (!ctx->passthrough) - break; - - j = MTY_JSONParse(str + 1); - if (!j) - break; - - const char *code = MTY_JSONObjGetStringPtr(j, "code"); - if (!code) - break; - - uint32_t jmods = 0; - if (!MTY_JSONObjGetInt(j, "mods", (int32_t *) &jmods)) - break; - - MTY_Key key = (MTY_Key) (uintptr_t) MTY_HashGet(ctx->keys, code) & 0xFFFF; - if (key == MTY_KEY_NONE) - break; - - MTY_Mod mods = web_keymap_mods(jmods); - - ctx->key_func(ctx->app, ctx->window, str[0] == 'D', key, mods); - break; - } - - MTY_JSONDestroy(&j); + mty_webview_base_handle_event(&ctx->base, str); MTY_Free(str); } @@ -229,7 +169,7 @@ static HRESULT STDMETHODCALLTYPE h1_query_interface(void *This, static void webview_update_size(struct webview *ctx) { - MTY_Size size = MTY_WindowGetSize(ctx->app, ctx->window); + MTY_Size size = MTY_WindowGetSize(ctx->base.app, ctx->base.window); RECT bounds = {0}; bounds.right = size.w; @@ -269,8 +209,8 @@ static HRESULT STDMETHODCALLTYPE h1_Invoke(ICoreWebView2CreateCoreWebView2Contro ICoreWebView2Settings *settings = NULL; ICoreWebView2_get_Settings(ctx->webview, &settings); - ICoreWebView2Settings_put_AreDevToolsEnabled(settings, ctx->debug); - ICoreWebView2Settings_put_AreDefaultContextMenusEnabled(settings, ctx->debug); + ICoreWebView2Settings_put_AreDevToolsEnabled(settings, ctx->base.debug); + ICoreWebView2Settings_put_AreDefaultContextMenusEnabled(settings, ctx->base.debug); ICoreWebView2Settings_put_IsZoomControlEnabled(settings, FALSE); ICoreWebView2Settings_Release(settings); @@ -311,7 +251,7 @@ static HRESULT STDMETHODCALLTYPE h0_Invoke(ICoreWebView2CreateCoreWebView2Enviro struct webview_handler0 *handler = (struct webview_handler0 *) This; struct webview *ctx = handler->opaque; - HWND hwnd = MTY_WindowGetNative(ctx->app, ctx->window); + HWND hwnd = MTY_WindowGetNative(ctx->base.app, ctx->base.window); return ICoreWebView2Environment_CreateCoreWebView2Controller(env, hwnd, (ICoreWebView2CreateCoreWebView2ControllerCompletedHandler *) &ctx->handler1); @@ -548,15 +488,7 @@ struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char * { struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); - ctx->app = app; - ctx->window = window; - ctx->debug = debug; - ctx->ready_func = ready_func; - ctx->text_func = text_func; - ctx->key_func = key_func; - - ctx->keys = web_keymap_hash(); - ctx->pushq = MTY_QueueCreate(50, 0); + mty_webview_base_create(&ctx->base, app, window, dir, debug, ready_func, text_func, key_func); ctx->handler0.handler.lpVtbl = &VTBL0; ctx->handler0.opaque = ctx; @@ -606,11 +538,7 @@ void mty_webview_destroy(struct webview **webview) if (ctx->lib) FreeLibrary(ctx->lib); - if (ctx->pushq) - MTY_QueueFlush(ctx->pushq, MTY_Free); - - MTY_QueueDestroy(&ctx->pushq); - MTY_HashDestroy(&ctx->keys, NULL); + mty_webview_base_destroy(&ctx->base); MTY_Free(ctx->source); @@ -657,8 +585,8 @@ bool mty_webview_is_visible(struct webview *ctx) void mty_webview_send_text(struct webview *ctx, const char *msg) { - if (!ctx->ready) { - MTY_QueuePushPtr(ctx->pushq, MTY_MultiToWideD(msg), 0); + if (!ctx->base.ready) { + MTY_QueuePushPtr(ctx->base.pushq, MTY_Strdup(msg), 0); } else { WCHAR *wmsg = MTY_MultiToWideD(msg); @@ -677,7 +605,7 @@ void mty_webview_reload(struct webview *ctx) void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { - ctx->passthrough = passthrough; + ctx->base.passthrough = passthrough; } bool mty_webview_event(struct webview *ctx, MTY_Event *evt) From f290db58c4c74ebfe35e230db50233da73259209 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:16:02 +0100 Subject: [PATCH 06/20] Woops --- src/windows/webview.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows/webview.c b/src/windows/webview.c index f9b82e13..3c914b35 100644 --- a/src/windows/webview.c +++ b/src/windows/webview.c @@ -626,7 +626,7 @@ void mty_webview_render(struct webview *ctx) bool mty_webview_is_focussed(struct webview *ctx) { - return ctx && ctx->focussed; + return ctx && ctx->base.focussed; } bool mty_webview_is_steam(void) From 1d444192f537bb3abaf063552b128c8e3e33969e Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:30:11 +0100 Subject: [PATCH 07/20] Remove duplicate flags --- GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GNUmakefile b/GNUmakefile index 8f3e91e4..d30f586b 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -165,7 +165,7 @@ DEFS := $(DEFS) \ INCLUDES := $(INCLUDES) \ -Isrc/unix/linux \ -Isrc/unix/linux/x11 \ - $(shell pkg-config --cflags x11 gtk+-3.0 webkit2gtk-4.0) + $(sort $(shell pkg-config --cflags x11 gtk+-3.0 webkit2gtk-4.0)) endif From 5851a6cd355dad27c8eb8156fa91f0ef514a91dc Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:36:42 +0100 Subject: [PATCH 08/20] Fix include --- src/webview.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webview.c b/src/webview.c index 1b26eca7..23d8d333 100644 --- a/src/webview.c +++ b/src/webview.c @@ -1,6 +1,6 @@ #include "webview.h" -#include "web/keymap.h" +#include "unix/web/keymap.h" void mty_webview_base_create(struct webview_base *ctx, MTY_App *app, MTY_Window window, const char *dir, bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func) From 8ac93e530f61d2b0018d3e23db3b2382099d24d0 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 5 Nov 2024 20:43:02 +0100 Subject: [PATCH 09/20] Fix makefile --- makefile | 2 +- src/windows/{webview.c => webview-win.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/windows/{webview.c => webview-win.c} (100%) diff --git a/makefile b/makefile index 70ef88ff..9b7b6c53 100644 --- a/makefile +++ b/makefile @@ -19,7 +19,7 @@ NAME = matoya !IFDEF STEAM WEBVIEW_OBJ = src\swebview.obj !ELSE -WEBVIEW_OBJ = src\windows\webview.obj +WEBVIEW_OBJ = src\windows\webview-win.obj !ENDIF OBJS = \ diff --git a/src/windows/webview.c b/src/windows/webview-win.c similarity index 100% rename from src/windows/webview.c rename to src/windows/webview-win.c From f5b8cf742fa8c504586a3b5885464e7e007732e0 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:02:08 +0100 Subject: [PATCH 10/20] Steam webview cleanup (I guess) --- src/swebview.c | 102 +++++++++++-------------------------------------- 1 file changed, 23 insertions(+), 79 deletions(-) diff --git a/src/swebview.c b/src/swebview.c index eb9b9b26..25848109 100644 --- a/src/swebview.c +++ b/src/swebview.c @@ -35,21 +35,14 @@ struct sbase { }; struct webview { - MTY_App *app; - MTY_Window window; - WEBVIEW_READY ready_func; - WEBVIEW_TEXT text_func; - WEBVIEW_KEY key_func; - MTY_Queue *pushq; + struct webview_base base; + MTY_Mutex *mutex; ISteamHTMLSurface *surface; EHTMLKeyModifiers smods; HHTMLBrowser browser; char *source; - bool debug; - bool ready; bool visible; - bool passthrough; void *bgra; bool bgra_dirty; @@ -68,7 +61,7 @@ struct webview { static void webview_update_size(struct webview *ctx) { - MTY_Size size = MTY_WindowGetSize(ctx->app, ctx->window); + MTY_Size size = MTY_WindowGetSize(ctx->base.app, ctx->base.window); SteamAPI_ISteamHTMLSurface_SetSize(ctx->surface, ctx->browser, size.w, size.h); } @@ -87,7 +80,7 @@ static void webview_on_browser_ready(struct webview *ctx, HTML_BrowserReady_t *p if (ctx->source) SteamAPI_ISteamHTMLSurface_LoadURL(ctx->surface, ctx->browser, ctx->source, NULL); - if (ctx->debug) + if (ctx->base.debug) SteamAPI_ISteamHTMLSurface_OpenDeveloperTools(ctx->surface, ctx->browser); } @@ -180,30 +173,7 @@ static void finished_request_run0(void *This, void *pvParam) struct webview *ctx = base->ctx; HTML_FinishedRequest_t *params = pvParam; - const char *script = - "const __MTY_MSGS = [];" - - "let __MTY_WEBVIEW = b64 => {" - "__MTY_MSGS.push(b64);" - "};" - - "window.MTY_NativeSendText = text => {" - "alert('T' + text);" - "};" - - "alert('R');" - - "const __MTY_INTERVAL = setInterval(() => {" - "if (window.MTY_NativeListener) {" - "__MTY_WEBVIEW = b64 => {window.MTY_NativeListener(atob(b64));};" - - "for (let msg = __MTY_MSGS.shift(); msg; msg = __MTY_MSGS.shift())" - "__MTY_WEBVIEW(msg);" - - "clearInterval(__MTY_INTERVAL);" - "}" - "}, 100);"; - + const char *script = "window.parent = { postMessage: alert };"; SteamAPI_ISteamHTMLSurface_ExecuteJavascript(ctx->surface, params->unBrowserHandle, script); } @@ -235,26 +205,7 @@ static void js_alert_run0(void *This, void *pvParam) SteamAPI_ISteamHTMLSurface_JSDialogResponse(ctx->surface, ctx->browser, true); const char *str = params->pchMessage; - - switch (str[0]) { - // MTY_EVENT_WEBVIEW_READY - case 'R': - ctx->ready = true; - - // Send any queued messages before the WebView became ready - for (char *msg = NULL; MTY_QueuePopPtr(ctx->pushq, 0, (void **) &msg, NULL);) { - mty_webview_send_text(ctx, msg); - MTY_Free(msg); - } - - ctx->ready_func(ctx->app, ctx->window); - break; - - // MTY_EVENT_WEBVIEW_TEXT - case 'T': - ctx->text_func(ctx->app, ctx->window, str + 1); - break; - } + mty_webview_base_handle_event(&ctx->base, str); } static void js_alert_run1(void *This, void *pvParam, bool _dummy0, SteamAPICall_t _dummy1) @@ -283,10 +234,10 @@ static void set_cursor_run0(void *This, void *pvParam) HTML_SetCursor_t *params = pvParam; switch (params->eMouseCursor) { - case dc_hand: MTY_AppSetCursor(ctx->app, MTY_CURSOR_HAND); break; - case dc_ibeam: MTY_AppSetCursor(ctx->app, MTY_CURSOR_IBEAM); break; + case dc_hand: MTY_AppSetCursor(ctx->base.app, MTY_CURSOR_HAND); break; + case dc_ibeam: MTY_AppSetCursor(ctx->base.app, MTY_CURSOR_IBEAM); break; default: - MTY_AppSetCursor(ctx->app, MTY_CURSOR_ARROW); + MTY_AppSetCursor(ctx->base.app, MTY_CURSOR_ARROW); break; } } @@ -330,17 +281,12 @@ struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char * bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func) { struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); - ctx->app = app; - ctx->window = window; - ctx->mutex = MTY_MutexCreate(); - ctx->ready_func = ready_func; - ctx->text_func = text_func; - ctx->key_func = key_func; - ctx->debug = debug; + + mty_webview_base_create(&ctx->base, app, window, dir, debug, ready_func, text_func, key_func); steam_global_init(dir ? dir : "."); - ctx->pushq = MTY_QueueCreate(50, 0); + ctx->mutex = MTY_MutexCreate(); bool r = SteamAPI_InitSafe(); if (!r) { @@ -393,16 +339,14 @@ void mty_webview_destroy(struct webview **webview) SteamAPI_Shutdown(); - if (ctx->pushq) - MTY_QueueFlush(ctx->pushq, MTY_Free); - - MTY_QueueDestroy(&ctx->pushq); MTY_MutexDestroy(&ctx->mutex); MTY_Free(ctx->source); MTY_Free(ctx->bgra); steam_global_destroy(); + mty_webview_base_destroy(&ctx->base); + MTY_Free(ctx); *webview = NULL; } @@ -426,10 +370,10 @@ void mty_webview_show(struct webview *ctx, bool show) ctx->visible = show; if (show) { - MTY_AppShowCursor(ctx->app, true); + MTY_AppShowCursor(ctx->base.app, true); } else { - MTY_AppSetCursor(ctx->app, MTY_CURSOR_NONE); + MTY_AppSetCursor(ctx->base.app, MTY_CURSOR_NONE); } SteamAPI_ISteamHTMLSurface_SetKeyFocus(ctx->surface, ctx->browser, show); @@ -443,8 +387,8 @@ bool mty_webview_is_visible(struct webview *ctx) void mty_webview_send_text(struct webview *ctx, const char *msg) { - if (!ctx->ready) { - MTY_QueuePushPtr(ctx->pushq, MTY_Strdup(msg), 0); + if (!ctx->base.ready) { + MTY_QueuePushPtr(ctx->base.pushq, MTY_Strdup(msg), 0); } else { if (ctx->browser == 0) @@ -476,7 +420,7 @@ void mty_webview_reload(struct webview *ctx) void mty_webview_set_input_passthrough(struct webview *ctx, bool passthrough) { - ctx->passthrough = passthrough; + ctx->base.passthrough = passthrough; } static EHTMLKeyModifiers webview_mods(MTY_Mod mods) @@ -547,10 +491,10 @@ bool mty_webview_event(struct webview *ctx, MTY_Event *evt) evt->key.vkey, ctx->smods); } - if (ctx->passthrough) + if (ctx->base.passthrough) evt->type = MTY_EVENT_WEBVIEW_KEY; - return !ctx->passthrough; + return !ctx->base.passthrough; } case MTY_EVENT_TEXT: { wchar_t codepoint[8] = {0}; @@ -568,7 +512,7 @@ bool mty_webview_event(struct webview *ctx, MTY_Event *evt) void mty_webview_run(struct webview *ctx) { - if (!ctx->ready || ctx->visible) + if (!ctx->base.ready || ctx->visible) SteamAPI_RunCallbacks(); } @@ -585,7 +529,7 @@ void mty_webview_render(struct webview *ctx) ctx->desc.format = MTY_COLOR_FORMAT_UNKNOWN; } - MTY_WindowDrawQuad(ctx->app, ctx->window, ctx->bgra, &ctx->desc); + MTY_WindowDrawQuad(ctx->base.app, ctx->base.window, ctx->bgra, &ctx->desc); MTY_MutexUnlock(ctx->mutex); } From 124462fd8a1d092ab07794a6afcc1b019d3dae63 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Wed, 6 Nov 2024 12:37:50 +0100 Subject: [PATCH 11/20] `native` instead of `parent` --- src/swebview.c | 5 ++++- src/unix/apple/webview.m | 6 +++++- src/unix/linux/android/Matoya.java | 8 +++++++- src/unix/linux/x11/webview.c | 5 ++++- src/windows/webview-win.c | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/swebview.c b/src/swebview.c index 25848109..9cca4767 100644 --- a/src/swebview.c +++ b/src/swebview.c @@ -173,7 +173,10 @@ static void finished_request_run0(void *This, void *pvParam) struct webview *ctx = base->ctx; HTML_FinishedRequest_t *params = pvParam; - const char *script = "window.parent = { postMessage: alert };"; + const char *script = "window.native = {" + "postMessage: (message) => alert(message)," + "addEventListener: (listener) => window.addEventListener('message', listener)," + "}"; SteamAPI_ISteamHTMLSurface_ExecuteJavascript(ctx->surface, params->unBrowserHandle, script); } diff --git a/src/unix/apple/webview.m b/src/unix/apple/webview.m index 7d901cb2..a9ac33fc 100644 --- a/src/unix/apple/webview.m +++ b/src/unix/apple/webview.m @@ -122,7 +122,11 @@ static bool mty_webview_supported(void) [ctx->webview.configuration.userContentController addScriptMessageHandler:handler name:@"native"]; // MTY javascript shim - WKUserScript *script = [[WKUserScript alloc] initWithSource:@"window.parent = window.webkit.messageHandlers.native;" + const char *script = "window.native = {" + "postMessage: (message) => window.webkit.messageHandlers.native.postMessage(message)," + "addEventListener: (listener) => window.addEventListener('message', listener)," + "}"; + WKUserScript *script = [[WKUserScript alloc] initWithSource:[NSString stringWithUTF8String:script] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES ]; diff --git a/src/unix/linux/android/Matoya.java b/src/unix/linux/android/Matoya.java index 98f50729..2868bb9f 100644 --- a/src/unix/linux/android/Matoya.java +++ b/src/unix/linux/android/Matoya.java @@ -579,7 +579,13 @@ public void postMessage(String text) { webview.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView webview, String url, Bitmap favicon) { - webview.evaluateJavascript("window.parent = native;", null); + String script = String.join('\n', + "window.native = {", + "postMessage: (message) => native.postMessage(message),", + "addEventListener: (listener) => window.addEventListener('message', listener),", + "}" + ); + webview.evaluateJavascript(script, null); } }); diff --git a/src/unix/linux/x11/webview.c b/src/unix/linux/x11/webview.c index 35c951f6..0d57b9b4 100644 --- a/src/unix/linux/x11/webview.c +++ b/src/unix/linux/x11/webview.c @@ -107,7 +107,10 @@ static bool _mty_webview_create(struct mty_webview_event *event) g_signal_connect(manager, "script-message-received::native", G_CALLBACK(handle_script_message), ctx); webkit_user_content_manager_register_script_message_handler(manager, "native"); - const char *javascript = "window.parent = window.webkit.messageHandlers.native;"; + const char *javascript = "window.native = {" + "postMessage: (message) => window.webkit.messageHandlers.native.postMessage(message)," + "addEventListener: (listener) => window.addEventListener('message', listener)," + "}"; WebKitUserContentInjectedFrames injected_frames = WEBKIT_USER_CONTENT_INJECT_TOP_FRAME; WebKitUserScriptInjectionTime injection_time = WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START; WebKitUserScript *script = webkit_user_script_new(javascript, injected_frames, injection_time, NULL, NULL); diff --git a/src/windows/webview-win.c b/src/windows/webview-win.c index 3c914b35..840f7d01 100644 --- a/src/windows/webview-win.c +++ b/src/windows/webview-win.c @@ -223,7 +223,11 @@ static HRESULT STDMETHODCALLTYPE h1_Invoke(ICoreWebView2CreateCoreWebView2Contro ICoreWebView2_add_WebMessageReceived(ctx->webview, (ICoreWebView2WebMessageReceivedEventHandler *) &ctx->handler2, &token); - ICoreWebView2_AddScriptToExecuteOnDocumentCreated(ctx->webview, L"window.parent = window.chrome.webview;", NULL); + const WCHAR *script = L"window.native = {" + L"postMessage: (message) => window.chrome.webview.postMessage(message)," + L"addEventListener: (listener) => window.chrome.webview.addEventListener('message', listener)," + L"};"; + ICoreWebView2_AddScriptToExecuteOnDocumentCreated(ctx->webview, script, NULL); if (ctx->source) webview_navigate(ctx, ctx->source, ctx->url); From 1281d703b28ea79c7f43c21e4b04008f4aa57a12 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:58:13 +0100 Subject: [PATCH 12/20] Woops --- src/unix/linux/android/Matoya.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/linux/android/Matoya.java b/src/unix/linux/android/Matoya.java index 2868bb9f..5a095e5b 100644 --- a/src/unix/linux/android/Matoya.java +++ b/src/unix/linux/android/Matoya.java @@ -579,7 +579,7 @@ public void postMessage(String text) { webview.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView webview, String url, Bitmap favicon) { - String script = String.join('\n', + String script = String.join("\n", "window.native = {", "postMessage: (message) => native.postMessage(message),", "addEventListener: (listener) => window.addEventListener('message', listener),", From ae748cd6284238c5651c655e81df4fcad3cb1145 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:54:15 +0100 Subject: [PATCH 13/20] Fix Android --- src/unix/linux/android/Matoya.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unix/linux/android/Matoya.java b/src/unix/linux/android/Matoya.java index 5a095e5b..7a8c1b7c 100644 --- a/src/unix/linux/android/Matoya.java +++ b/src/unix/linux/android/Matoya.java @@ -574,14 +574,14 @@ public void run() { public void postMessage(String text) { webview_handle_event(ctx, text); } - }, "native"); + }, "webview"); webview.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView webview, String url, Bitmap favicon) { String script = String.join("\n", "window.native = {", - "postMessage: (message) => native.postMessage(message),", + "postMessage: (message) => webview.postMessage(message),", "addEventListener: (listener) => window.addEventListener('message', listener),", "}" ); From bce2f2515648648ae42e2ba3282931aee83fafa7 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Wed, 6 Nov 2024 18:47:17 +0100 Subject: [PATCH 14/20] Fix macOS --- src/unix/apple/webview.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unix/apple/webview.m b/src/unix/apple/webview.m index a9ac33fc..6fdcc63d 100644 --- a/src/unix/apple/webview.m +++ b/src/unix/apple/webview.m @@ -122,11 +122,11 @@ static bool mty_webview_supported(void) [ctx->webview.configuration.userContentController addScriptMessageHandler:handler name:@"native"]; // MTY javascript shim - const char *script = "window.native = {" + const char *javascript = "window.native = {" "postMessage: (message) => window.webkit.messageHandlers.native.postMessage(message)," "addEventListener: (listener) => window.addEventListener('message', listener)," "}"; - WKUserScript *script = [[WKUserScript alloc] initWithSource:[NSString stringWithUTF8String:script] + WKUserScript *script = [[WKUserScript alloc] initWithSource:[NSString stringWithUTF8String:javascript] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES ]; From bf1675d60ac80f7efd0eb0541f93f0afdbc83302 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:44:55 +0100 Subject: [PATCH 15/20] Fix double-free corruption --- src/unix/linux/x11/webview.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/unix/linux/x11/webview.c b/src/unix/linux/x11/webview.c index 0d57b9b4..e1703e6f 100644 --- a/src/unix/linux/x11/webview.c +++ b/src/unix/linux/x11/webview.c @@ -168,8 +168,6 @@ static bool _mty_webview_destroy(struct mty_webview_event *event) gtk_widget_destroy(GTK_WIDGET(ctx->gtk_window)); gtk_main_quit(); - MTY_Free(ctx); - mty_webview_destroy_event(&event); return false; From 72f1a268086eeeda504bb6b57ef5efdd09665e1b Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:26:49 +0100 Subject: [PATCH 16/20] More performant mty_webview_send_text on some platforms --- src/swebview.c | 12 +----------- src/unix/apple/webview.m | 10 +++------- src/unix/linux/x11/webview.c | 10 +++------- src/webview.c | 18 ++++++++++++++++++ src/webview.h | 1 + 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/swebview.c b/src/swebview.c index 9cca4767..6192fe7a 100644 --- a/src/swebview.c +++ b/src/swebview.c @@ -397,17 +397,7 @@ void mty_webview_send_text(struct webview *ctx, const char *msg) if (ctx->browser == 0) return; - size_t msg_size = strlen(msg); - size_t size = msg_size * 4 + 64; - char *script = MTY_Alloc(size, 1); - - memcpy(script, "__MTY_WEBVIEW('", 15); - - MTY_BytesToBase64(msg, msg_size, script + 15, size - 15); - - size_t end = strlen(script); - memcpy(script + end, "');", 3); - + char *script = mty_webview_base_format_text(msg); SteamAPI_ISteamHTMLSurface_ExecuteJavascript(ctx->surface, ctx->browser, script); MTY_Free(script); } diff --git a/src/unix/apple/webview.m b/src/unix/apple/webview.m index 6fdcc63d..f8d54aa2 100644 --- a/src/unix/apple/webview.m +++ b/src/unix/apple/webview.m @@ -201,16 +201,12 @@ void mty_webview_send_text(struct webview *ctx, const char *msg) MTY_QueuePushPtr(ctx->base.pushq, MTY_Strdup(msg), 0); } else { - MTY_JSON *json = MTY_JSONStringCreate(msg); - char *text = MTY_JSONSerialize(json); - NSString *wrapped = [NSString stringWithFormat:@"window.postMessage(%@, '*');", [NSString stringWithUTF8String:text]]; - MTY_Free(text); - MTY_JSONDestroy(&json); - - [ctx->webview evaluateJavaScript:wrapped completionHandler:^(id object, NSError *error) { + char *script = mty_webview_base_format_text(msg); + [ctx->webview evaluateJavaScript:[NSString stringWithUTF8String:script] completionHandler:^(id object, NSError *error) { if (error) MTY_Log("'WKWebView:evaluateJavaScript' failed to evaluate string '%s'", text); }]; + MTY_Free(script); } } diff --git a/src/unix/linux/x11/webview.c b/src/unix/linux/x11/webview.c index e1703e6f..2cc8a45b 100644 --- a/src/unix/linux/x11/webview.c +++ b/src/unix/linux/x11/webview.c @@ -222,13 +222,9 @@ static bool _mty_webview_show(struct mty_webview_event *event) static bool _mty_webview_send_text(struct mty_webview_event *event) { - MTY_JSON *json = MTY_JSONStringCreate(event->data); - char *text = MTY_JSONSerialize(json); - char *message = MTY_SprintfD("window.postMessage(%s, '*');", text); - webkit_web_view_evaluate_javascript(event->context->webview, message, -1, NULL, NULL, NULL, NULL, NULL); - MTY_Free(message); - MTY_Free(text); - MTY_JSONDestroy(&json); + char *script = mty_webview_base_format_text(event->data); + webkit_web_view_evaluate_javascript(event->context->webview, script, -1, NULL, NULL, NULL, NULL, NULL); + MTY_Free(script); return false; } diff --git a/src/webview.c b/src/webview.c index 23d8d333..69a4a78c 100644 --- a/src/webview.c +++ b/src/webview.c @@ -1,5 +1,8 @@ #include "webview.h" +#include +#include + #include "unix/web/keymap.h" void mty_webview_base_create(struct webview_base *ctx, MTY_App *app, MTY_Window window, const char *dir, @@ -78,3 +81,18 @@ void mty_webview_base_handle_event(struct webview_base *ctx, const char *str) MTY_JSONDestroy(&j); } + +char *mty_webview_base_format_text(const char *msg) +{ + uint32_t msg_len = strlen(msg); + uint32_t b64_len = msg_len * 4; + char *b64 = MTY_Alloc(b64_len, 1); + + MTY_BytesToBase64(msg, msg_len, b64, b64_len); + + char *script = MTY_SprintfD("window.postMessage(atob('%s'));", b64); + + MTY_Free(b64); + + return script; +} diff --git a/src/webview.h b/src/webview.h index ca64b618..cf0a2ead 100644 --- a/src/webview.h +++ b/src/webview.h @@ -31,6 +31,7 @@ void mty_webview_base_create(struct webview_base *ctx, MTY_App *app, MTY_Window bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func); void mty_webview_base_destroy(struct webview_base *ctx); void mty_webview_base_handle_event(struct webview_base *ctx, const char *str); +char *mty_webview_base_format_text(const char *msg); struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char *dir, bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func); From f199a001f19757c9e819df159af221128e8c09c1 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:20:24 +0100 Subject: [PATCH 17/20] Runtime linking with GTK on Linux --- GNUmakefile | 3 +- src/gfx/vk/vkproc.h | 1 + src/unix/linux/x11/app.c | 2 +- src/unix/linux/x11/dl/libgtk.h | 226 +++++++++++++++++++++++++++ src/unix/linux/x11/dl/libx11.c | 263 -------------------------------- src/unix/linux/x11/dl/libx11.h | 262 +++++++++++++++++++++++++++++++ src/unix/linux/x11/gfx/gl-ctx.c | 2 +- src/unix/linux/x11/webview.c | 53 +++---- 8 files changed, 519 insertions(+), 293 deletions(-) create mode 100644 src/unix/linux/x11/dl/libgtk.h delete mode 100644 src/unix/linux/x11/dl/libx11.c diff --git a/GNUmakefile b/GNUmakefile index d30f586b..62c18010 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -164,8 +164,7 @@ DEFS := $(DEFS) \ INCLUDES := $(INCLUDES) \ -Isrc/unix/linux \ - -Isrc/unix/linux/x11 \ - $(sort $(shell pkg-config --cflags x11 gtk+-3.0 webkit2gtk-4.0)) + -Isrc/unix/linux/x11 endif diff --git a/src/gfx/vk/vkproc.h b/src/gfx/vk/vkproc.h index bd86cc3f..c89dbdcd 100644 --- a/src/gfx/vk/vkproc.h +++ b/src/gfx/vk/vkproc.h @@ -19,6 +19,7 @@ ((e) == VK_ERROR_OUT_OF_DATE_KHR || (e) == VK_SUBOPTIMAL_KHR) #elif defined(MTY_VK_XLIB) + #define LIBX11_NO_SYMBOLS #include "dl/libx11.h" #include "vulkan/vulkan_xlib.h" diff --git a/src/unix/linux/x11/app.c b/src/unix/linux/x11/app.c index 86c87392..84b4c021 100644 --- a/src/unix/linux/x11/app.c +++ b/src/unix/linux/x11/app.c @@ -14,7 +14,7 @@ #include -#include "dl/libx11.c" +#include "dl/libx11.h" #include "hid/utils.h" #include "evdev.h" #include "keymap.h" diff --git a/src/unix/linux/x11/dl/libgtk.h b/src/unix/linux/x11/dl/libgtk.h new file mode 100644 index 00000000..00b31958 --- /dev/null +++ b/src/unix/linux/x11/dl/libgtk.h @@ -0,0 +1,226 @@ +#pragma once + +#include "dl/sym.h" +#include "matoya.h" + +#include "dl/libx11.h" + + +// glib-2.0 + +#define G_SOURCE_FUNC(f) ((GSourceFunc)(void (*)(void))(f)) + +typedef char gchar; +typedef int gint; +typedef unsigned int guint; +typedef unsigned long gulong; +typedef double gdouble; +typedef signed long gssize; +typedef void *gpointer; +typedef bool gboolean; +typedef void GObject; +typedef void GClosure; +typedef void GCancellable; +typedef void GAsyncResult; + +typedef void (*GCallback)(void); +typedef gboolean (*GSourceFunc)(gpointer user_data); +typedef void (*GClosureNotify)(gpointer data, GClosure *closure); +typedef void (*GAsyncReadyCallback)(GObject *source_object, GAsyncResult *res, gpointer user_data); + +typedef enum { + G_CONNECT_AFTER = 1 << 0, + G_CONNECT_SWAPPED = 1 << 1 +} GConnectFlags; + +static guint (*g_idle_add)(GSourceFunc function, gpointer data); +static gulong (*g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags); +static gboolean (*g_setenv)(const gchar *variable, const gchar *value, gboolean overwrite); + + +// gdk-3 + +typedef void GdkDisplay; +typedef void GdkWindow; + +typedef struct { + gdouble red; + gdouble green; + gdouble blue; + gdouble alpha; +} GdkRGBA; + +static Display *(*gdk_x11_display_get_xdisplay)(GdkDisplay *display); +static GdkDisplay *(*gdk_window_get_display)(GdkWindow *window); +static Window (*gdk_x11_window_get_xid)(GdkWindow *window); +static void (*gdk_window_show)(GdkWindow *window); +static void (*gdk_window_hide)(GdkWindow *window); +static gboolean (*gdk_window_is_visible)(GdkWindow *window); + + +// gtk-3 + +typedef void GtkContainer; +typedef void GtkWidget; +typedef void GtkWindow; + +typedef enum { GTK_WINDOW_TOPLEVEL, GTK_WINDOW_POPUP } GtkWindowType; + +static GtkWidget *(*gtk_window_new)(GtkWindowType type); +static void (*gtk_widget_realize)(GtkWidget *widget); +static GdkWindow *(*gtk_widget_get_window)(GtkWidget *widget); +static void (*gtk_window_get_size)(GtkWindow *window, gint *width, gint *height); +static void (*gtk_window_resize)(GtkWindow *window, gint width, gint height); +static void (*gtk_container_add)(GtkContainer *container, GtkWidget *widget); +static void (*gtk_widget_set_app_paintable)(GtkWidget *widget, gboolean app_paintable); +static void (*gtk_widget_show_all)(GtkWidget *widget); +static gboolean (*gtk_init_check)(int *argc, char ***argv); +static void (*gtk_main)(void); +static void (*gtk_window_close)(GtkWindow *window); +static void (*gtk_widget_destroy)(GtkWidget *widget); +static void (*gtk_main_quit)(void); + + +// libjavascriptcoregtk-4.0 + +typedef void JSCValue; + +static char *(*jsc_value_to_string)(JSCValue *value); + + +// libwebkit2gtk-4.0 + +typedef void WebKitSettings; +typedef void WebKitWebView; +typedef void WebKitUserContentManager; +typedef void WebKitJavascriptResult; +typedef void WebKitUserScript; + +typedef enum { + WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, + WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, +} WebKitUserContentInjectedFrames; + +typedef enum { + WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, + WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, +} WebKitUserScriptInjectionTime; + +static JSCValue *(*webkit_javascript_result_get_js_value)(WebKitJavascriptResult *js_result); +static GtkWidget *(*webkit_web_view_new)(void); +static void (*webkit_web_view_set_background_color)(WebKitWebView *web_view, const GdkRGBA *rgba); +static WebKitUserContentManager *(*webkit_web_view_get_user_content_manager)(WebKitWebView *web_view); +static gboolean (*webkit_user_content_manager_register_script_message_handler)(WebKitUserContentManager *manager, const gchar *name); +static WebKitUserScript *(*webkit_user_script_new)(const gchar *source, WebKitUserContentInjectedFrames injected_frames, + WebKitUserScriptInjectionTime injection_time, const gchar *const *allow_list, const gchar *const *block_list); +static void (*webkit_user_content_manager_add_script)(WebKitUserContentManager *manager, WebKitUserScript *script); +static WebKitSettings *(*webkit_web_view_get_settings)(WebKitWebView *web_view); +static void (*webkit_settings_set_enable_developer_extras)(WebKitSettings *settings, gboolean enabled); +static void (*webkit_web_view_load_uri)(WebKitWebView *web_view, const gchar *uri); +static void (*webkit_web_view_load_html)(WebKitWebView *web_view, const gchar *content, const gchar *base_uri); +static void (*webkit_web_view_evaluate_javascript)(WebKitWebView *web_view, const char *script, gssize length, const char *world_name, + const char *source_uri, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); +static void (*webkit_web_view_reload)(WebKitWebView *web_view); + + +// Runtime open + +static MTY_Atomic32 LIBGTK_LOCK; +static MTY_SO *LIBGOBJECT_SO; +static MTY_SO *LIBGLIB_SO; +static MTY_SO *LIBDGK_SO; +static MTY_SO *LIBGTK_SO; +static MTY_SO *LIBJAVASCRIPT_SO; +static MTY_SO *LIBWEBKIT2_SO; +static bool LIBGTK_INIT; + +static void libgtk_global_destroy_lockfree(void) { + MTY_SOUnload(&LIBWEBKIT2_SO); + MTY_SOUnload(&LIBJAVASCRIPT_SO); + MTY_SOUnload(&LIBGTK_SO); + MTY_SOUnload(&LIBDGK_SO); + MTY_SOUnload(&LIBGLIB_SO); + MTY_SOUnload(&LIBGOBJECT_SO); + LIBGTK_INIT = false; +} + +static void __attribute__((destructor)) libgtk_global_destroy(void) { + MTY_GlobalLock(&LIBGTK_LOCK); + + libgtk_global_destroy_lockfree(); + + MTY_GlobalUnlock(&LIBGTK_LOCK); +} + +static bool libgtk_global_init(void) { + MTY_GlobalLock(&LIBGTK_LOCK); + + if (!LIBGTK_INIT) { + bool r = true; + + LIBGOBJECT_SO = MTY_SOLoad("libgobject-2.0.so"); + LIBGLIB_SO = MTY_SOLoad("libglib-2.0.so"); + LIBDGK_SO = MTY_SOLoad("libgdk-3.so"); + LIBGTK_SO = MTY_SOLoad("libgtk-3.so"); + LIBJAVASCRIPT_SO = MTY_SOLoad("libjavascriptcoregtk-4.0.so"); + LIBWEBKIT2_SO = MTY_SOLoad("libwebkit2gtk-4.0.so"); + if (!LIBGOBJECT_SO || !LIBGLIB_SO || !LIBDGK_SO || !LIBGTK_SO || !LIBJAVASCRIPT_SO || !LIBWEBKIT2_SO) { + r = false; + goto except; + } + + LOAD_SYM(LIBGOBJECT_SO, g_signal_connect_data); + + LOAD_SYM(LIBGLIB_SO, g_idle_add); + LOAD_SYM(LIBGLIB_SO, g_setenv); + + LOAD_SYM(LIBDGK_SO, gdk_x11_display_get_xdisplay); + LOAD_SYM(LIBDGK_SO, gdk_window_get_display); + LOAD_SYM(LIBDGK_SO, gdk_x11_window_get_xid); + LOAD_SYM(LIBDGK_SO, gdk_window_show); + LOAD_SYM(LIBDGK_SO, gdk_window_hide); + LOAD_SYM(LIBDGK_SO, gdk_window_is_visible); + + LOAD_SYM(LIBGTK_SO, gtk_window_new); + LOAD_SYM(LIBGTK_SO, gtk_widget_realize); + LOAD_SYM(LIBGTK_SO, gtk_widget_get_window); + LOAD_SYM(LIBGTK_SO, gtk_window_get_size); + LOAD_SYM(LIBGTK_SO, gtk_window_resize); + LOAD_SYM(LIBGTK_SO, gtk_container_add); + LOAD_SYM(LIBGTK_SO, gtk_widget_set_app_paintable); + LOAD_SYM(LIBGTK_SO, gtk_widget_show_all); + LOAD_SYM(LIBGTK_SO, gtk_init_check); + LOAD_SYM(LIBGTK_SO, gtk_main); + LOAD_SYM(LIBGTK_SO, gtk_window_close); + LOAD_SYM(LIBGTK_SO, gtk_widget_destroy); + LOAD_SYM(LIBGTK_SO, gtk_main_quit); + + LOAD_SYM(LIBJAVASCRIPT_SO, jsc_value_to_string); + + LOAD_SYM(LIBWEBKIT2_SO, webkit_javascript_result_get_js_value); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_new); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_set_background_color); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_get_user_content_manager); + LOAD_SYM(LIBWEBKIT2_SO, webkit_user_content_manager_register_script_message_handler); + LOAD_SYM(LIBWEBKIT2_SO, webkit_user_script_new); + LOAD_SYM(LIBWEBKIT2_SO, webkit_user_content_manager_add_script); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_get_settings); + LOAD_SYM(LIBWEBKIT2_SO, webkit_settings_set_enable_developer_extras); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_load_uri); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_load_html); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_evaluate_javascript); + LOAD_SYM(LIBWEBKIT2_SO, webkit_web_view_reload); + + except: + + if (!r) + libgtk_global_destroy_lockfree(); + + LIBGTK_INIT = r; + } + + MTY_GlobalUnlock(&LIBGTK_LOCK); + + return LIBGTK_INIT; +} diff --git a/src/unix/linux/x11/dl/libx11.c b/src/unix/linux/x11/dl/libx11.c deleted file mode 100644 index b32db6d9..00000000 --- a/src/unix/linux/x11/dl/libx11.c +++ /dev/null @@ -1,263 +0,0 @@ -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT License was not distributed with this file, -// You can obtain one at https://spdx.org/licenses/MIT.html. - -#pragma once - -#include "matoya.h" -#include "libx11.h" - -#include "sym.h" - - -// X interface - -// Reference: https://code.woboq.org/qt5/include/X11/ - -static Display *(*XOpenDisplay)(const char *display_name); -static Screen *(*XScreenOfDisplay)(Display *display, int screen_number); -static Screen *(*XDefaultScreenOfDisplay)(Display *display); -static int (*XScreenNumberOfScreen)(Screen *screen); -static int (*XCloseDisplay)(Display *display); -static Window (*XDefaultRootWindow)(Display *display); -static Window (*XRootWindowOfScreen)(Screen *screen); -static Colormap (*XCreateColormap)(Display *display, Window w, Visual *visual, int alloc); -static Window (*XCreateWindow)(Display *display, Window parent, int x, int y, unsigned int width, - unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, - unsigned long valuemask, XSetWindowAttributes *attributes); -static int (*XWithdrawWindow)(Display *display, Window w); -static int (*XMapRaised)(Display *display, Window w); -static int (*XSetInputFocus)(Display *display, Window focus, int revert_to, Time time); -static int (*XStoreName)(Display *display, Window w, const char *window_name); -static Status (*XGetWindowAttributes)(Display *display, Window w, XWindowAttributes *window_attributes_return); -static Bool (*XTranslateCoordinates)(Display *display, Window src_w, Window dest_w, int src_x, int src_y, - int *dest_x_return, int *dest_y_return, Window *child_return); -static KeySym (*XLookupKeysym)(XKeyEvent *key_event, int index); -static Status (*XSetWMProtocols)(Display *display, Window w, Atom *protocols, int count); -static Atom (*XInternAtom)(Display *display, const char *atom_name, Bool only_if_exists); -static int (*XNextEvent)(Display *display, XEvent *event_return); -static int (*XEventsQueued)(Display *display, int mode); -static int (*XMoveWindow)(Display *display, Window w, int x, int y); -static int (*XMoveResizeWindow)(Display *display, Window w, int x, int y, unsigned int width, unsigned int height); -static int (*XChangeProperty)(Display *display, Window w, Atom property, Atom type, int format, int mode, const unsigned char *data, int nelements); -static int (*XGetInputFocus)(Display *display, Window *focus_return, int *revert_to_return); -static char *(*XGetDefault)(Display *display, const char *program, const char *option); -static int (*XWidthOfScreen)(Screen *screen); -static int (*XHeightOfScreen)(Screen *screen); -static int (*XDestroyWindow)(Display *display, Window w); -static int (*XFree)(void *data); -static Status (*XInitThreads)(void); -static int (*Xutf8LookupString)(XIC ic, XKeyPressedEvent *event, char *buffer_return, int bytes_buffer, - KeySym *keysym_return, Status *status_return); -static XIM (*XOpenIM)(Display *dpy, struct _XrmHashBucketRec *rdb, char *res_name, char *res_class); -static Status (*XCloseIM)(XIM im); -static XIC (*XCreateIC)(XIM im, ...); -static void (*XDestroyIC)(XIC ic); -static Bool (*XGetEventData)(Display *dpy, XGenericEventCookie *cookie); -static int (*XGrabPointer)(Display *display, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, - int keyboard_mode, Window confine_to, Cursor cursor, Time time); -static int (*XUngrabPointer)(Display *display, Time time); -static int (*XGrabKeyboard)(Display *display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time); -static int (*XUngrabKeyboard)(Display *display, Time time); -static int (*XWarpPointer)(Display *display, Window src_w, Window dest_w, int src_x, int src_y, unsigned int src_width, - unsigned int src_height, int dest_x, int dest_y); -static int (*XSync)(Display *display, Bool discard); -static Pixmap (*XCreateBitmapFromData)(Display *display, Drawable d, _Xconst char *data, unsigned int width, unsigned int height); -static Cursor (*XCreatePixmapCursor)(Display *display, Pixmap source, Pixmap mask, XColor *foreground_color, - XColor *background_color, unsigned int x, unsigned int y); -static Cursor (*XCreateFontCursor)(Display *display, unsigned int shape); -static int (*XFreePixmap)(Display *display, Pixmap pixmap); -static int (*XDefineCursor)(Display *display, Window w, Cursor cursor); -static int (*XFreeCursor)(Display *display, Cursor cursor); -static Window (*XGetSelectionOwner)(Display *display, Atom selection); -static int (*XSetSelectionOwner)(Display *display, Atom selection, Window owner, Time time); -static char *(*XKeysymToString)(KeySym keysym); -static void (*XConvertCase)(KeySym keysym, KeySym *lower_return, KeySym *upper_return); -static Bool (*XQueryPointer)(Display *display, Window w, Window *root_return, Window *child_return, - int *root_x_return, int *root_y_return, int *win_x_return, int *win_y_return, unsigned int *mask_return); -static int (*XGetWindowProperty)(Display *display, Window w, Atom property, long long_offset, long long_length, - Bool delete, Atom req_type, Atom *actual_type_return, int *actual_format_return, unsigned long *nitems_return, - unsigned long *bytes_after_return, unsigned char **prop_return); -static Status (*XSendEvent)(Display *display, Window w, Bool propagate, long event_mask, XEvent *event_send); -static int (*XConvertSelection)(Display *display, Atom selection, Atom target, Atom property, Window requestor, Time time); -static void (*XSetWMProperties)(Display *display, Window w, XTextProperty *window_name, XTextProperty *icon_name, char **argv, - int argc, XSizeHints *normal_hints, XWMHints *wm_hints, XClassHint *class_hints); -static XSizeHints *(*XAllocSizeHints)(void); -static XWMHints *(*XAllocWMHints)(void); -static XClassHint *(*XAllocClassHint)(void); -static int (*XResetScreenSaver)(Display *display); - -// Xfixes interface - -// Reference: https://code.woboq.org/kde/include/X11/extensions/ - -static Bool (*XFixesQueryExtension)(Display *dpy, int *event_base_return, int *error_base_return); -static void (*XFixesSelectSelectionInput)(Display *dpy, Window win, Atom selection, unsigned long eventMask); - - -// XKB interface (part of libX11 in modern times) - -static Bool (*XkbSetDetectableAutoRepeat)(Display *dpy, Bool detectable, Bool *supported); - - -// XI2 interface - -// Reference: https://code.woboq.org/kde/include/X11/extensions/ - -static int (*XISelectEvents)(Display *dpy, Window win, XIEventMask *masks, int num_masks); - - -// Xcursor interface - -static XcursorImage *(*XcursorImageCreate)(int width, int height); -static Cursor (*XcursorImageLoadCursor)(Display *dpy, const XcursorImage *image); -static void (*XcursorImageDestroy)(XcursorImage *image); - - -// GLX interface - -// Reference: https://code.woboq.org/qt5/include/GL/ - -static void *(*glXGetProcAddress)(const GLubyte *procName); -static void (*glXSwapBuffers)(Display *dpy, GLXDrawable drawable); -static XVisualInfo *(*glXChooseVisual)(Display *dpy, int screen, int *attribList); -static GLXContext (*glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct); -static Bool (*glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx); -static void (*glXDestroyContext)(Display *dpy, GLXContext ctx); -static GLXContext (*glXGetCurrentContext)(void); - -static MTY_Atomic32 LIBX11_LOCK; -static MTY_SO *LIBX11_SO; -static MTY_SO *LIBXFIXES_SO; -static MTY_SO *LIBXI_SO; -static MTY_SO *LIBXCURSOR_SO; -static MTY_SO *LIBGL_SO; -static bool LIBX11_INIT; - -static void __attribute__((destructor)) libX11_global_destroy(void) -{ - MTY_GlobalLock(&LIBX11_LOCK); - - MTY_SOUnload(&LIBGL_SO); - MTY_SOUnload(&LIBXCURSOR_SO); - MTY_SOUnload(&LIBXI_SO); - MTY_SOUnload(&LIBXFIXES_SO); - MTY_SOUnload(&LIBX11_SO); - LIBX11_INIT = false; - - MTY_GlobalUnlock(&LIBX11_LOCK); -} - -static bool libX11_global_init(void) -{ - MTY_GlobalLock(&LIBX11_LOCK); - - if (!LIBX11_INIT) { - bool r = true; - - LIBX11_SO = MTY_SOLoad("libX11.so.6"); - LIBXFIXES_SO = MTY_SOLoad("libXfixes.so.3"); - LIBXI_SO = MTY_SOLoad("libXi.so.6"); - LIBXCURSOR_SO = MTY_SOLoad("libXcursor.so.1"); - LIBGL_SO = MTY_SOLoad("libGL.so.1"); - - if (!LIBX11_SO || !LIBGL_SO || !LIBXI_SO || !LIBXCURSOR_SO) { - r = false; - goto except; - } - - LOAD_SYM(LIBX11_SO, XOpenDisplay); - LOAD_SYM(LIBX11_SO, XScreenOfDisplay); - LOAD_SYM(LIBX11_SO, XDefaultScreenOfDisplay); - LOAD_SYM(LIBX11_SO, XScreenNumberOfScreen); - LOAD_SYM(LIBX11_SO, XCloseDisplay); - LOAD_SYM(LIBX11_SO, XDefaultRootWindow); - LOAD_SYM(LIBX11_SO, XRootWindowOfScreen); - LOAD_SYM(LIBX11_SO, XCreateColormap); - LOAD_SYM(LIBX11_SO, XCreateWindow); - LOAD_SYM(LIBX11_SO, XWithdrawWindow); - LOAD_SYM(LIBX11_SO, XMapRaised); - LOAD_SYM(LIBX11_SO, XSetInputFocus); - LOAD_SYM(LIBX11_SO, XStoreName); - LOAD_SYM(LIBX11_SO, XGetWindowAttributes); - LOAD_SYM(LIBX11_SO, XTranslateCoordinates); - LOAD_SYM(LIBX11_SO, XLookupKeysym); - LOAD_SYM(LIBX11_SO, XSetWMProtocols); - LOAD_SYM(LIBX11_SO, XInternAtom); - LOAD_SYM(LIBX11_SO, XNextEvent); - LOAD_SYM(LIBX11_SO, XEventsQueued); - LOAD_SYM(LIBX11_SO, XMoveWindow); - LOAD_SYM(LIBX11_SO, XMoveResizeWindow); - LOAD_SYM(LIBX11_SO, XChangeProperty); - LOAD_SYM(LIBX11_SO, XGetInputFocus); - LOAD_SYM(LIBX11_SO, XGetDefault); - LOAD_SYM(LIBX11_SO, XWidthOfScreen); - LOAD_SYM(LIBX11_SO, XHeightOfScreen); - LOAD_SYM(LIBX11_SO, XDestroyWindow); - LOAD_SYM(LIBX11_SO, XFree); - LOAD_SYM(LIBX11_SO, XInitThreads); - LOAD_SYM(LIBX11_SO, Xutf8LookupString); - LOAD_SYM(LIBX11_SO, XOpenIM); - LOAD_SYM(LIBX11_SO, XCloseIM); - LOAD_SYM(LIBX11_SO, XCreateIC); - LOAD_SYM(LIBX11_SO, XDestroyIC); - LOAD_SYM(LIBX11_SO, XGetEventData); - LOAD_SYM(LIBX11_SO, XGrabPointer); - LOAD_SYM(LIBX11_SO, XUngrabPointer); - LOAD_SYM(LIBX11_SO, XGrabKeyboard); - LOAD_SYM(LIBX11_SO, XUngrabKeyboard); - LOAD_SYM(LIBX11_SO, XWarpPointer); - LOAD_SYM(LIBX11_SO, XSync); - LOAD_SYM(LIBX11_SO, XCreateBitmapFromData); - LOAD_SYM(LIBX11_SO, XCreatePixmapCursor); - LOAD_SYM(LIBX11_SO, XCreateFontCursor); - LOAD_SYM(LIBX11_SO, XFreePixmap); - LOAD_SYM(LIBX11_SO, XDefineCursor); - LOAD_SYM(LIBX11_SO, XFreeCursor); - LOAD_SYM(LIBX11_SO, XGetSelectionOwner); - LOAD_SYM(LIBX11_SO, XSetSelectionOwner); - LOAD_SYM(LIBX11_SO, XKeysymToString); - LOAD_SYM(LIBX11_SO, XConvertCase); - LOAD_SYM(LIBX11_SO, XQueryPointer); - LOAD_SYM(LIBX11_SO, XGetWindowProperty); - LOAD_SYM(LIBX11_SO, XSendEvent); - LOAD_SYM(LIBX11_SO, XConvertSelection); - LOAD_SYM(LIBX11_SO, XSetWMProperties); - LOAD_SYM(LIBX11_SO, XAllocSizeHints); - LOAD_SYM(LIBX11_SO, XAllocWMHints); - LOAD_SYM(LIBX11_SO, XAllocClassHint); - LOAD_SYM(LIBX11_SO, XResetScreenSaver); - - if (LIBXFIXES_SO) { - LOAD_SYM_OPT(LIBXFIXES_SO, XFixesQueryExtension); - LOAD_SYM_OPT(LIBXFIXES_SO, XFixesSelectSelectionInput); - } - - LOAD_SYM_OPT(LIBX11_SO, XkbSetDetectableAutoRepeat); - - LOAD_SYM(LIBXI_SO, XISelectEvents); - - LOAD_SYM(LIBXCURSOR_SO, XcursorImageCreate); - LOAD_SYM(LIBXCURSOR_SO, XcursorImageLoadCursor); - LOAD_SYM(LIBXCURSOR_SO, XcursorImageDestroy); - - LOAD_SYM(LIBGL_SO, glXGetProcAddress); - LOAD_SYM(LIBGL_SO, glXSwapBuffers); - LOAD_SYM(LIBGL_SO, glXChooseVisual); - LOAD_SYM(LIBGL_SO, glXCreateContext); - LOAD_SYM(LIBGL_SO, glXMakeCurrent); - LOAD_SYM(LIBGL_SO, glXDestroyContext); - LOAD_SYM(LIBGL_SO, glXGetCurrentContext); - - except: - - if (!r) - libX11_global_destroy(); - - LIBX11_INIT = r; - } - - MTY_GlobalUnlock(&LIBX11_LOCK); - - return LIBX11_INIT; -} diff --git a/src/unix/linux/x11/dl/libx11.h b/src/unix/linux/x11/dl/libx11.h index 33bf1aad..6bf9a685 100644 --- a/src/unix/linux/x11/dl/libx11.h +++ b/src/unix/linux/x11/dl/libx11.h @@ -4,8 +4,11 @@ #pragma once +#include "matoya.h" #include "glcorearb.h" +#include "sym.h" + // X interface @@ -568,3 +571,262 @@ struct xinfo { XVisualInfo *vis; Window window; }; + + +#if !defined(LIBX11_NO_SYMBOLS) + +// X interface + +// Reference: https://code.woboq.org/qt5/include/X11/ + +static Display *(*XOpenDisplay)(const char *display_name); +static Screen *(*XScreenOfDisplay)(Display *display, int screen_number); +static Screen *(*XDefaultScreenOfDisplay)(Display *display); +static int (*XScreenNumberOfScreen)(Screen *screen); +static int (*XCloseDisplay)(Display *display); +static Window (*XDefaultRootWindow)(Display *display); +static Window (*XRootWindowOfScreen)(Screen *screen); +static Colormap (*XCreateColormap)(Display *display, Window w, Visual *visual, int alloc); +static Window (*XCreateWindow)(Display *display, Window parent, int x, int y, unsigned int width, + unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, + unsigned long valuemask, XSetWindowAttributes *attributes); +static int (*XWithdrawWindow)(Display *display, Window w); +static int (*XMapRaised)(Display *display, Window w); +static int (*XSetInputFocus)(Display *display, Window focus, int revert_to, Time time); +static int (*XStoreName)(Display *display, Window w, const char *window_name); +static Status (*XGetWindowAttributes)(Display *display, Window w, XWindowAttributes *window_attributes_return); +static Bool (*XTranslateCoordinates)(Display *display, Window src_w, Window dest_w, int src_x, int src_y, + int *dest_x_return, int *dest_y_return, Window *child_return); +static KeySym (*XLookupKeysym)(XKeyEvent *key_event, int index); +static Status (*XSetWMProtocols)(Display *display, Window w, Atom *protocols, int count); +static Atom (*XInternAtom)(Display *display, const char *atom_name, Bool only_if_exists); +static int (*XNextEvent)(Display *display, XEvent *event_return); +static int (*XEventsQueued)(Display *display, int mode); +static int (*XMoveWindow)(Display *display, Window w, int x, int y); +static int (*XMoveResizeWindow)(Display *display, Window w, int x, int y, unsigned int width, unsigned int height); +static int (*XChangeProperty)(Display *display, Window w, Atom property, Atom type, int format, int mode, const unsigned char *data, int nelements); +static int (*XGetInputFocus)(Display *display, Window *focus_return, int *revert_to_return); +static char *(*XGetDefault)(Display *display, const char *program, const char *option); +static int (*XWidthOfScreen)(Screen *screen); +static int (*XHeightOfScreen)(Screen *screen); +static int (*XDestroyWindow)(Display *display, Window w); +static int (*XFree)(void *data); +static Status (*XInitThreads)(void); +static int (*Xutf8LookupString)(XIC ic, XKeyPressedEvent *event, char *buffer_return, int bytes_buffer, + KeySym *keysym_return, Status *status_return); +static XIM (*XOpenIM)(Display *dpy, struct _XrmHashBucketRec *rdb, char *res_name, char *res_class); +static Status (*XCloseIM)(XIM im); +static XIC (*XCreateIC)(XIM im, ...); +static void (*XDestroyIC)(XIC ic); +static Bool (*XGetEventData)(Display *dpy, XGenericEventCookie *cookie); +static int (*XGrabPointer)(Display *display, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, + int keyboard_mode, Window confine_to, Cursor cursor, Time time); +static int (*XUngrabPointer)(Display *display, Time time); +static int (*XGrabKeyboard)(Display *display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time); +static int (*XUngrabKeyboard)(Display *display, Time time); +static int (*XWarpPointer)(Display *display, Window src_w, Window dest_w, int src_x, int src_y, unsigned int src_width, + unsigned int src_height, int dest_x, int dest_y); +static int (*XSync)(Display *display, Bool discard); +static Pixmap (*XCreateBitmapFromData)(Display *display, Drawable d, _Xconst char *data, unsigned int width, unsigned int height); +static Cursor (*XCreatePixmapCursor)(Display *display, Pixmap source, Pixmap mask, XColor *foreground_color, + XColor *background_color, unsigned int x, unsigned int y); +static Cursor (*XCreateFontCursor)(Display *display, unsigned int shape); +static int (*XFreePixmap)(Display *display, Pixmap pixmap); +static int (*XDefineCursor)(Display *display, Window w, Cursor cursor); +static int (*XFreeCursor)(Display *display, Cursor cursor); +static Window (*XGetSelectionOwner)(Display *display, Atom selection); +static int (*XSetSelectionOwner)(Display *display, Atom selection, Window owner, Time time); +static char *(*XKeysymToString)(KeySym keysym); +static void (*XConvertCase)(KeySym keysym, KeySym *lower_return, KeySym *upper_return); +static Bool (*XQueryPointer)(Display *display, Window w, Window *root_return, Window *child_return, + int *root_x_return, int *root_y_return, int *win_x_return, int *win_y_return, unsigned int *mask_return); +static int (*XGetWindowProperty)(Display *display, Window w, Atom property, long long_offset, long long_length, + Bool delete, Atom req_type, Atom *actual_type_return, int *actual_format_return, unsigned long *nitems_return, + unsigned long *bytes_after_return, unsigned char **prop_return); +static Status (*XSendEvent)(Display *display, Window w, Bool propagate, long event_mask, XEvent *event_send); +static int (*XConvertSelection)(Display *display, Atom selection, Atom target, Atom property, Window requestor, Time time); +static void (*XSetWMProperties)(Display *display, Window w, XTextProperty *window_name, XTextProperty *icon_name, char **argv, + int argc, XSizeHints *normal_hints, XWMHints *wm_hints, XClassHint *class_hints); +static XSizeHints *(*XAllocSizeHints)(void); +static XWMHints *(*XAllocWMHints)(void); +static XClassHint *(*XAllocClassHint)(void); +static int (*XResetScreenSaver)(Display *display); +static int (*XReparentWindow)(Display *display, Window w, Window parent, int x, int y); + +// Xfixes interface + +// Reference: https://code.woboq.org/kde/include/X11/extensions/ + +static Bool (*XFixesQueryExtension)(Display *dpy, int *event_base_return, int *error_base_return); +static void (*XFixesSelectSelectionInput)(Display *dpy, Window win, Atom selection, unsigned long eventMask); + + +// XKB interface (part of libX11 in modern times) + +static Bool (*XkbSetDetectableAutoRepeat)(Display *dpy, Bool detectable, Bool *supported); + + +// XI2 interface + +// Reference: https://code.woboq.org/kde/include/X11/extensions/ + +static int (*XISelectEvents)(Display *dpy, Window win, XIEventMask *masks, int num_masks); + + +// Xcursor interface + +static XcursorImage *(*XcursorImageCreate)(int width, int height); +static Cursor (*XcursorImageLoadCursor)(Display *dpy, const XcursorImage *image); +static void (*XcursorImageDestroy)(XcursorImage *image); + + +// GLX interface + +// Reference: https://code.woboq.org/qt5/include/GL/ + +static void *(*glXGetProcAddress)(const GLubyte *procName); +static void (*glXSwapBuffers)(Display *dpy, GLXDrawable drawable); +static XVisualInfo *(*glXChooseVisual)(Display *dpy, int screen, int *attribList); +static GLXContext (*glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct); +static Bool (*glXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx); +static void (*glXDestroyContext)(Display *dpy, GLXContext ctx); +static GLXContext (*glXGetCurrentContext)(void); + +static MTY_Atomic32 LIBX11_LOCK; +static MTY_SO *LIBX11_SO; +static MTY_SO *LIBXFIXES_SO; +static MTY_SO *LIBXI_SO; +static MTY_SO *LIBXCURSOR_SO; +static MTY_SO *LIBGL_SO; +static bool LIBX11_INIT; + +static void __attribute__((destructor)) libX11_global_destroy(void) +{ + MTY_GlobalLock(&LIBX11_LOCK); + + MTY_SOUnload(&LIBGL_SO); + MTY_SOUnload(&LIBXCURSOR_SO); + MTY_SOUnload(&LIBXI_SO); + MTY_SOUnload(&LIBXFIXES_SO); + MTY_SOUnload(&LIBX11_SO); + LIBX11_INIT = false; + + MTY_GlobalUnlock(&LIBX11_LOCK); +} + +static bool libX11_global_init(void) +{ + MTY_GlobalLock(&LIBX11_LOCK); + + if (!LIBX11_INIT) { + bool r = true; + + LIBX11_SO = MTY_SOLoad("libX11.so.6"); + LIBXFIXES_SO = MTY_SOLoad("libXfixes.so.3"); + LIBXI_SO = MTY_SOLoad("libXi.so.6"); + LIBXCURSOR_SO = MTY_SOLoad("libXcursor.so.1"); + LIBGL_SO = MTY_SOLoad("libGL.so.1"); + + if (!LIBX11_SO || !LIBGL_SO || !LIBXI_SO || !LIBXCURSOR_SO) { + r = false; + goto except; + } + + LOAD_SYM(LIBX11_SO, XOpenDisplay); + LOAD_SYM(LIBX11_SO, XScreenOfDisplay); + LOAD_SYM(LIBX11_SO, XDefaultScreenOfDisplay); + LOAD_SYM(LIBX11_SO, XScreenNumberOfScreen); + LOAD_SYM(LIBX11_SO, XCloseDisplay); + LOAD_SYM(LIBX11_SO, XDefaultRootWindow); + LOAD_SYM(LIBX11_SO, XRootWindowOfScreen); + LOAD_SYM(LIBX11_SO, XCreateColormap); + LOAD_SYM(LIBX11_SO, XCreateWindow); + LOAD_SYM(LIBX11_SO, XWithdrawWindow); + LOAD_SYM(LIBX11_SO, XMapRaised); + LOAD_SYM(LIBX11_SO, XSetInputFocus); + LOAD_SYM(LIBX11_SO, XStoreName); + LOAD_SYM(LIBX11_SO, XGetWindowAttributes); + LOAD_SYM(LIBX11_SO, XTranslateCoordinates); + LOAD_SYM(LIBX11_SO, XLookupKeysym); + LOAD_SYM(LIBX11_SO, XSetWMProtocols); + LOAD_SYM(LIBX11_SO, XInternAtom); + LOAD_SYM(LIBX11_SO, XNextEvent); + LOAD_SYM(LIBX11_SO, XEventsQueued); + LOAD_SYM(LIBX11_SO, XMoveWindow); + LOAD_SYM(LIBX11_SO, XMoveResizeWindow); + LOAD_SYM(LIBX11_SO, XChangeProperty); + LOAD_SYM(LIBX11_SO, XGetInputFocus); + LOAD_SYM(LIBX11_SO, XGetDefault); + LOAD_SYM(LIBX11_SO, XWidthOfScreen); + LOAD_SYM(LIBX11_SO, XHeightOfScreen); + LOAD_SYM(LIBX11_SO, XDestroyWindow); + LOAD_SYM(LIBX11_SO, XFree); + LOAD_SYM(LIBX11_SO, XInitThreads); + LOAD_SYM(LIBX11_SO, Xutf8LookupString); + LOAD_SYM(LIBX11_SO, XOpenIM); + LOAD_SYM(LIBX11_SO, XCloseIM); + LOAD_SYM(LIBX11_SO, XCreateIC); + LOAD_SYM(LIBX11_SO, XDestroyIC); + LOAD_SYM(LIBX11_SO, XGetEventData); + LOAD_SYM(LIBX11_SO, XGrabPointer); + LOAD_SYM(LIBX11_SO, XUngrabPointer); + LOAD_SYM(LIBX11_SO, XGrabKeyboard); + LOAD_SYM(LIBX11_SO, XUngrabKeyboard); + LOAD_SYM(LIBX11_SO, XWarpPointer); + LOAD_SYM(LIBX11_SO, XSync); + LOAD_SYM(LIBX11_SO, XCreateBitmapFromData); + LOAD_SYM(LIBX11_SO, XCreatePixmapCursor); + LOAD_SYM(LIBX11_SO, XCreateFontCursor); + LOAD_SYM(LIBX11_SO, XFreePixmap); + LOAD_SYM(LIBX11_SO, XDefineCursor); + LOAD_SYM(LIBX11_SO, XFreeCursor); + LOAD_SYM(LIBX11_SO, XGetSelectionOwner); + LOAD_SYM(LIBX11_SO, XSetSelectionOwner); + LOAD_SYM(LIBX11_SO, XKeysymToString); + LOAD_SYM(LIBX11_SO, XConvertCase); + LOAD_SYM(LIBX11_SO, XQueryPointer); + LOAD_SYM(LIBX11_SO, XGetWindowProperty); + LOAD_SYM(LIBX11_SO, XSendEvent); + LOAD_SYM(LIBX11_SO, XConvertSelection); + LOAD_SYM(LIBX11_SO, XSetWMProperties); + LOAD_SYM(LIBX11_SO, XAllocSizeHints); + LOAD_SYM(LIBX11_SO, XAllocWMHints); + LOAD_SYM(LIBX11_SO, XAllocClassHint); + LOAD_SYM(LIBX11_SO, XResetScreenSaver); + LOAD_SYM(LIBX11_SO, XReparentWindow); + + if (LIBXFIXES_SO) { + LOAD_SYM_OPT(LIBXFIXES_SO, XFixesQueryExtension); + LOAD_SYM_OPT(LIBXFIXES_SO, XFixesSelectSelectionInput); + } + + LOAD_SYM_OPT(LIBX11_SO, XkbSetDetectableAutoRepeat); + + LOAD_SYM(LIBXI_SO, XISelectEvents); + + LOAD_SYM(LIBXCURSOR_SO, XcursorImageCreate); + LOAD_SYM(LIBXCURSOR_SO, XcursorImageLoadCursor); + LOAD_SYM(LIBXCURSOR_SO, XcursorImageDestroy); + + LOAD_SYM(LIBGL_SO, glXGetProcAddress); + LOAD_SYM(LIBGL_SO, glXSwapBuffers); + LOAD_SYM(LIBGL_SO, glXChooseVisual); + LOAD_SYM(LIBGL_SO, glXCreateContext); + LOAD_SYM(LIBGL_SO, glXMakeCurrent); + LOAD_SYM(LIBGL_SO, glXDestroyContext); + LOAD_SYM(LIBGL_SO, glXGetCurrentContext); + + except: + + if (!r) + libX11_global_destroy(); + + LIBX11_INIT = r; + } + + MTY_GlobalUnlock(&LIBX11_LOCK); + + return LIBX11_INIT; +} + +#endif \ No newline at end of file diff --git a/src/unix/linux/x11/gfx/gl-ctx.c b/src/unix/linux/x11/gfx/gl-ctx.c index e9cab395..3dbbbdee 100644 --- a/src/unix/linux/x11/gfx/gl-ctx.c +++ b/src/unix/linux/x11/gfx/gl-ctx.c @@ -6,7 +6,7 @@ GFX_CTX_PROTOTYPES(_gl_) #include "gfx/gl/glproc.c" -#include "dl/libx11.c" +#include "dl/libx11.h" struct gl_ctx { Display *display; diff --git a/src/unix/linux/x11/webview.c b/src/unix/linux/x11/webview.c index 2cc8a45b..b88a222f 100644 --- a/src/unix/linux/x11/webview.c +++ b/src/unix/linux/x11/webview.c @@ -5,14 +5,14 @@ #include "webview.h" #include -#include -#include -#include -#include +// #include +// #include #include "matoya.h" -#define DISPATCH(func, param, should_free) g_idle_add(G_SOURCE_FUNC(func), mty_webview_create_event(ctx, (void *) (param), should_free)) +#include "dl/libgtk.h" + +#define DISPATCH(func, param, should_free) g_idle_add((GSourceFunc) func, mty_webview_create_event(ctx, (void *) (param), should_free)) struct webview { struct webview_base base; @@ -30,12 +30,6 @@ struct mty_webview_event { bool should_free; }; -struct xinfo { - Display *display; - XVisualInfo *vis; - Window window; -}; - static struct mty_webview_event *mty_webview_create_event(struct webview *ctx, void *data, bool should_free) { struct mty_webview_event *event = MTY_Alloc(1, sizeof(struct mty_webview_event)); @@ -71,7 +65,7 @@ static void handle_script_message(WebKitUserContentManager *manager, WebKitJavas MTY_Free(str); } -static int32_t _mty_webview_resize(void *opaque) +static bool _mty_webview_resize(void *opaque) { struct webview *ctx = opaque; @@ -91,20 +85,21 @@ static bool _mty_webview_create(struct mty_webview_event *event) { struct webview *ctx = event->context; - ctx->gtk_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP)); - gtk_widget_realize(GTK_WIDGET(ctx->gtk_window)); + ctx->gtk_window = (GtkWindow *) gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_realize((GtkWidget *) ctx->gtk_window); - GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(ctx->gtk_window)); - XReparentWindow(GDK_WINDOW_XDISPLAY(gdk_window), GDK_WINDOW_XID(gdk_window), ctx->x11_window, 0, 0); + GdkWindow *gdk_window = gtk_widget_get_window((GtkWidget *) ctx->gtk_window); + Display *display = gdk_x11_display_get_xdisplay(gdk_window_get_display(gdk_window)); + XReparentWindow(display, gdk_x11_window_get_xid(gdk_window), ctx->x11_window, 0, 0); - ctx->webview = WEBKIT_WEB_VIEW(webkit_web_view_new()); - gtk_container_add(GTK_CONTAINER(ctx->gtk_window), GTK_WIDGET(ctx->webview)); + ctx->webview = (WebKitWebView *) webkit_web_view_new(); + gtk_container_add((GtkContainer *) ctx->gtk_window, (GtkWidget *) ctx->webview); - gtk_widget_set_app_paintable(GTK_WIDGET(ctx->gtk_window), TRUE); + gtk_widget_set_app_paintable((GtkWidget *) ctx->gtk_window, true); webkit_web_view_set_background_color(ctx->webview, & (GdkRGBA) {0}); WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager(ctx->webview); - g_signal_connect(manager, "script-message-received::native", G_CALLBACK(handle_script_message), ctx); + g_signal_connect_data(manager, "script-message-received::native", (GCallback) handle_script_message, (ctx), NULL, 0); webkit_user_content_manager_register_script_message_handler(manager, "native"); const char *javascript = "window.native = {" @@ -119,9 +114,9 @@ static bool _mty_webview_create(struct mty_webview_event *event) WebKitSettings *settings = webkit_web_view_get_settings(event->context->webview); webkit_settings_set_enable_developer_extras(settings, ctx->base.debug); - g_idle_add(_mty_webview_resize, ctx); + g_idle_add((GSourceFunc) _mty_webview_resize, ctx); - gtk_widget_show_all(GTK_WIDGET(ctx->gtk_window)); + gtk_widget_show_all((GtkWidget *) ctx->gtk_window); mty_webview_destroy_event(&event); @@ -139,6 +134,9 @@ static void *mty_webview_thread_func(void *opaque) struct webview *mty_webview_create(MTY_App *app, MTY_Window window, const char *dir, bool debug, WEBVIEW_READY ready_func, WEBVIEW_TEXT text_func, WEBVIEW_KEY key_func) { + if (!libX11_global_init() || !libgtk_global_init()) + return NULL; + struct webview *ctx = MTY_Alloc(1, sizeof(struct webview)); g_setenv("GDK_BACKEND", "x11", true); @@ -164,8 +162,8 @@ static bool _mty_webview_destroy(struct mty_webview_event *event) return false; gtk_window_close(ctx->gtk_window); - gtk_widget_destroy(GTK_WIDGET(ctx->webview)); - gtk_widget_destroy(GTK_WIDGET(ctx->gtk_window)); + gtk_widget_destroy((GtkWidget *) ctx->webview); + gtk_widget_destroy((GtkWidget *) ctx->gtk_window); gtk_main_quit(); mty_webview_destroy_event(&event); @@ -187,6 +185,9 @@ void mty_webview_destroy(struct webview **webview) mty_webview_base_destroy(&ctx->base); + libX11_global_destroy(); + libgtk_global_destroy(); + MTY_Free(ctx); } @@ -208,7 +209,7 @@ static bool _mty_webview_navigate_html(struct mty_webview_event *event) static bool _mty_webview_show(struct mty_webview_event *event) { - GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(event->context->gtk_window)); + GdkWindow *window = gtk_widget_get_window((GtkWidget *) event->context->gtk_window); if (event->data) { gdk_window_show(window); @@ -253,7 +254,7 @@ void mty_webview_show(struct webview *ctx, bool show) bool mty_webview_is_visible(struct webview *ctx) { - return gdk_window_is_visible(gtk_widget_get_window(GTK_WIDGET(ctx->gtk_window))); + return gdk_window_is_visible(gtk_widget_get_window((GtkWidget *) ctx->gtk_window)); } void mty_webview_send_text(struct webview *ctx, const char *msg) From 5a067fddef6702d7e4308b84da92cc392110cae1 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:58:20 +0100 Subject: [PATCH 18/20] Typo --- src/unix/apple/webview.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unix/apple/webview.m b/src/unix/apple/webview.m index f8d54aa2..03d38f9c 100644 --- a/src/unix/apple/webview.m +++ b/src/unix/apple/webview.m @@ -204,7 +204,7 @@ void mty_webview_send_text(struct webview *ctx, const char *msg) char *script = mty_webview_base_format_text(msg); [ctx->webview evaluateJavaScript:[NSString stringWithUTF8String:script] completionHandler:^(id object, NSError *error) { if (error) - MTY_Log("'WKWebView:evaluateJavaScript' failed to evaluate string '%s'", text); + MTY_Log("'WKWebView:evaluateJavaScript' failed to evaluate string '%s'", script); }]; MTY_Free(script); } From 5b43cc3318522c50d7a1e4fa2f9322daa167ae82 Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:50:06 +0100 Subject: [PATCH 19/20] Update webkitgtk version --- src/unix/linux/x11/dl/libgtk.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unix/linux/x11/dl/libgtk.h b/src/unix/linux/x11/dl/libgtk.h index 00b31958..eaadc653 100644 --- a/src/unix/linux/x11/dl/libgtk.h +++ b/src/unix/linux/x11/dl/libgtk.h @@ -163,8 +163,8 @@ static bool libgtk_global_init(void) { LIBGLIB_SO = MTY_SOLoad("libglib-2.0.so"); LIBDGK_SO = MTY_SOLoad("libgdk-3.so"); LIBGTK_SO = MTY_SOLoad("libgtk-3.so"); - LIBJAVASCRIPT_SO = MTY_SOLoad("libjavascriptcoregtk-4.0.so"); - LIBWEBKIT2_SO = MTY_SOLoad("libwebkit2gtk-4.0.so"); + LIBJAVASCRIPT_SO = MTY_SOLoad("libjavascriptcoregtk-4.1.so"); + LIBWEBKIT2_SO = MTY_SOLoad("libwebkit2gtk-4.1.so"); if (!LIBGOBJECT_SO || !LIBGLIB_SO || !LIBDGK_SO || !LIBGTK_SO || !LIBJAVASCRIPT_SO || !LIBWEBKIT2_SO) { r = false; goto except; From d00f3ba79c6fc5b6b55ee569c8694b399324738f Mon Sep 17 00:00:00 2001 From: Julien Loir <6706489+Namaneo@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:12:21 +0100 Subject: [PATCH 20/20] Relaxed webkit version on linux --- src/unix/linux/x11/dl/libgtk.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/unix/linux/x11/dl/libgtk.h b/src/unix/linux/x11/dl/libgtk.h index eaadc653..7009153d 100644 --- a/src/unix/linux/x11/dl/libgtk.h +++ b/src/unix/linux/x11/dl/libgtk.h @@ -163,8 +163,15 @@ static bool libgtk_global_init(void) { LIBGLIB_SO = MTY_SOLoad("libglib-2.0.so"); LIBDGK_SO = MTY_SOLoad("libgdk-3.so"); LIBGTK_SO = MTY_SOLoad("libgtk-3.so"); - LIBJAVASCRIPT_SO = MTY_SOLoad("libjavascriptcoregtk-4.1.so"); - LIBWEBKIT2_SO = MTY_SOLoad("libwebkit2gtk-4.1.so"); + + LIBJAVASCRIPT_SO = MTY_SOLoad("libjavascriptcoregtk-4.0.so"); + if (LIBJAVASCRIPT_SO == NULL) + LIBJAVASCRIPT_SO = MTY_SOLoad("libjavascriptcoregtk-4.1.so"); + + LIBWEBKIT2_SO = MTY_SOLoad("libwebkit2gtk-4.0.so"); + if (LIBWEBKIT2_SO == NULL) + LIBWEBKIT2_SO = MTY_SOLoad("libwebkit2gtk-4.1.so"); + if (!LIBGOBJECT_SO || !LIBGLIB_SO || !LIBDGK_SO || !LIBGTK_SO || !LIBJAVASCRIPT_SO || !LIBWEBKIT2_SO) { r = false; goto except;