diff options
| author | Arslaan Pathan <[email protected]> | 2026-05-02 18:29:13 +1200 |
|---|---|---|
| committer | Arslaan Pathan <[email protected]> | 2026-05-02 18:29:13 +1200 |
| commit | 95c3a91a092c0bf51ec4e70c46b92b70997be952 (patch) | |
| tree | f74ae2303ee4a1304fe4644a577115bfff4e3908 /src/sfwk-wpe.c | |
| parent | 060a5a156f7d7233051bf7dd5ccfe6988bd29335 (diff) | |
| download | saffronwebkit-95c3a91a092c0bf51ec4e70c46b92b70997be952.tar.xz saffronwebkit-95c3a91a092c0bf51ec4e70c46b92b70997be952.zip | |
shoot, i forgot to commit for the entirety of kiwihacks T_T
Diffstat (limited to 'src/sfwk-wpe.c')
| -rw-r--r-- | src/sfwk-wpe.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/src/sfwk-wpe.c b/src/sfwk-wpe.c new file mode 100644 index 0000000..e5ba7ec --- /dev/null +++ b/src/sfwk-wpe.c @@ -0,0 +1,559 @@ +// Copyright (C) 2026 Arslaan Pathan +// This code contains excerpts from wpe-sdl-simple, which is released under MIT. +// The original source code is available here: https://github.com/aperezdc/wpe-sdl-simple +// See external_licenses/COPYING_wpe-sdl-simple.txt + +#include "glib.h" +#include <SDL3/SDL_video.h> +#include <saffron.h> +#include <stdio.h> +#include <stdlib.h> +#include <wpe/webkit.h> +#include <SDL3/SDL.h> +#include <SDL3/SDL_egl.h> +#include <SDL3/SDL_opengles2.h> +#include <saffronwebkit.h> + + +G_DECLARE_FINAL_TYPE(WPEViewSDL3, wpe_view_sdl3, WPE, VIEW_SDL3, WPEView) +G_DECLARE_FINAL_TYPE(WPEToplevelSDL3, wpe_toplevel_sdl3, WPE, TOPLEVEL_SDL3, WPEToplevel) +G_DECLARE_FINAL_TYPE(WPEDisplaySDL3, wpe_display_sdl3, WPE, DISPLAY_SDL3, WPEDisplay) + +G_DEFINE_FINAL_TYPE(WPEViewSDL3, wpe_view_sdl3, WPE_TYPE_VIEW) +G_DEFINE_FINAL_TYPE(WPEToplevelSDL3, wpe_toplevel_sdl3, WPE_TYPE_TOPLEVEL) +G_DEFINE_FINAL_TYPE(WPEDisplaySDL3, wpe_display_sdl3, WPE_TYPE_DISPLAY) + +static void +wpe_toplevel_sdl3_ensure_texture(WPEToplevelSDL3 *self, SDL_Renderer* renderer, SDL_PixelFormat format, int width, int height) +{ + if (self->texture) { + if (self->texture->format == format && self->texture->w == width && self->texture->h == height) + return; + g_debug("%s: toplevel=%p, format=%#x, size=%dx%d, re-creating texture", G_STRFUNC, self, format, width, height); + SDL_DestroyTexture(self->texture); + } + + self->texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STREAMING, width, height); + g_assert(self->texture); +} + +static gboolean +wpe_toplevel_sdl3_render(WPEToplevelSDL3 *self, SDL_Renderer* renderer, WPEView *view [[maybe_unused]], WPEBuffer *buffer, GError **error) +{ + // g_debug("%s: toplevel=%p, view=%p, buffer=%p is a %s", G_STRFUNC, self, view, buffer, G_OBJECT_TYPE_NAME(buffer)); + + int buffer_width = wpe_buffer_get_width(buffer); + int buffer_height = wpe_buffer_get_height(buffer); + + if (buffer) { + WPEDisplaySDL3 *display = (WPEDisplaySDL3*) wpe_toplevel_get_display((WPEToplevel*) self); + EGLImage image = wpe_buffer_import_to_egl_image(buffer, error); + if (image != EGL_NO_IMAGE && display->imageTargetTexture2DOES) { + g_assert(display->egl_display == SDL_EGL_GetCurrentDisplay()); + wpe_toplevel_sdl3_ensure_texture(self, renderer, SDL_PIXELFORMAT_EXTERNAL_OES, buffer_width, buffer_height); + SDL_PropertiesID texture_props = SDL_GetTextureProperties(self->texture); + int texture_id = SDL_GetNumberProperty(texture_props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, 0); + SDL_DestroyProperties(texture_props); + glBindTexture(GL_TEXTURE_2D, texture_id); + display->imageTargetTexture2DOES(GL_TEXTURE_2D, image); + } else if (g_error_matches(*error, WPE_BUFFER_ERROR, WPE_BUFFER_ERROR_NOT_SUPPORTED)) { + g_clear_error(error); + GBytes *bytes = wpe_buffer_import_to_pixels(buffer, error); + if (!bytes) { + g_set_error_literal(error, WPE_VIEW_ERROR, WPE_VIEW_ERROR_RENDER_FAILED, + "Cannot import buffer pixel data"); + return FALSE; + } + wpe_toplevel_sdl3_ensure_texture(self, renderer, SDL_PIXELFORMAT_BGRA32, buffer_width, buffer_height); + // TODO: Update only the damaged rectangles. + void *pixels = NULL; + int stride = 0; + if (!SDL_LockTexture(self->texture, NULL, &pixels, &stride)) { + g_set_error(error, WPE_VIEW_ERROR, WPE_VIEW_ERROR_RENDER_FAILED, + "Cannot lock SDL texture to update from SHM buffer: %s", SDL_GetError()); + return FALSE; + } + size_t size; + const void *source = g_bytes_get_data(bytes, &size); + if (stride == buffer_width * 4) { + memcpy(pixels, source, size); + } else { + for (unsigned i = 0; i < buffer_height; i++) { + memcpy(pixels, source, buffer_width * 4); + pixels = (void*) ((uintptr_t) pixels + stride); + source = (const void*) ((uintptr_t) source + buffer_width * 4); + } + } + SDL_UnlockTexture(self->texture); + } else { + g_set_error_literal(error, WPE_VIEW_ERROR, WPE_VIEW_ERROR_RENDER_FAILED, + "Cannot import buffer as EGLImage, nor import pixel data"); + return FALSE; + } + if (image != EGL_NO_IMAGE) + display->destroyImage(display->egl_display, image); + + // At this point the SDL_Texture contains the data to render. + if (!SDL_RenderTexture(renderer, self->texture, NULL, NULL)) { + g_set_error(error, WPE_VIEW_ERROR, WPE_VIEW_ERROR_RENDER_FAILED, + "Cannot render SDL texture: %s", SDL_GetError()); + return FALSE; + } + } else { + SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + } + + SDL_RenderPresent(renderer); + return TRUE; +} + +static gboolean +wpe_view_sdl3_render_buffer(WPEView *view, WPEBuffer *buffer, const WPERectangle *damage_rects, unsigned n_damage_rects, GError **error) +{ + WPEToplevel *toplevel = wpe_view_get_toplevel(view); + g_assert(WPE_IS_TOPLEVEL_SDL3(toplevel)); + + WPEViewSDL3* view_impl = (WPEViewSDL3*)view; + SFWKWebView* webview = (SFWKWebView*)view_impl->userdata; + SDL_Renderer* renderer = webview->renderer; + + if (!wpe_toplevel_sdl3_render((WPEToplevelSDL3*) toplevel, renderer, view, buffer, error)) + return FALSE; + + wpe_view_buffer_rendered(view, buffer); + wpe_view_buffer_released(view, buffer); + return TRUE; +} + +static void +wpe_view_sdl3_on_notify_toplevel(WPEView *view) +{ + WPEToplevel *toplevel = wpe_view_get_toplevel(view); + if (toplevel) { + ((WPEToplevelSDL3*) toplevel)->view = view; + int width, height; + wpe_toplevel_get_size(toplevel, &width, &height); + if (width && height) + wpe_view_resized(view, width, height); + wpe_view_map(view); + } else { + wpe_view_unmap(view); + } +} + +static void +wpe_view_sdl3_constructed(GObject *object) +{ + G_OBJECT_CLASS(wpe_view_sdl3_parent_class)->constructed(object); + g_signal_connect(object, "notify::toplevel", G_CALLBACK(wpe_view_sdl3_on_notify_toplevel), NULL); + g_debug("%s: view=%p", G_STRFUNC, object); +} + +static void +wpe_view_sdl3_dispose(GObject *object) +{ + g_debug("%s: view=%p", G_STRFUNC, object); + G_OBJECT_CLASS(wpe_view_sdl3_parent_class)->dispose(object); +} + +static void +wpe_view_sdl3_init(WPEViewSDL3 *self) +{ +} + +static void +wpe_view_sdl3_class_init(WPEViewSDL3Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = wpe_view_sdl3_constructed; + object_class->dispose = wpe_view_sdl3_dispose; + + WPEViewClass *view_class = WPE_VIEW_CLASS(klass); + view_class->render_buffer = wpe_view_sdl3_render_buffer; +} + +static void +wpe_toplevel_sdl3_init(WPEToplevelSDL3 *self) +{ +} + +static WPEView* +wpe_view_sdl3_new(WPEDisplay *display) +{ + g_return_val_if_fail(WPE_IS_DISPLAY_SDL3(display), NULL); + return g_object_new(wpe_view_sdl3_get_type(), "display", display, NULL); +} + + +static gboolean +wpe_toplevel_sdl3_each_view_resized(WPEToplevel *toplevel, WPEView *view, void *userdata) +{ + int width, height; + wpe_toplevel_get_size(toplevel, &width, &height); + wpe_view_resized(view, width, height); + return FALSE; // Continue iterating views. +} + +// static gboolean +// wpe_toplevel_sdl3_set_fullscreen(WPEToplevel *toplevel, gboolean fullscreen) +// { +// WPEToplevelSDL3 *self = WPE_TOPLEVEL_SDL3(toplevel); +// if (!SDL_SetWindowFullscreen(self->window, !!fullscreen)) +// g_warning("Could not %s SDL window for toplevel %p", +// fullscreen ? "fullscreen" : "un-fullscreen", +// toplevel); +// return FALSE; +// +// WPEToplevelState state = wpe_toplevel_get_state(toplevel); +// if (fullscreen) +// state |= WPE_TOPLEVEL_STATE_FULLSCREEN; +// else +// state &= ~WPE_TOPLEVEL_STATE_FULLSCREEN; +// wpe_toplevel_state_changed(toplevel, state); +// return TRUE; +// } + +// static void +// wpe_toplevel_sdl3_set_title(WPEToplevel *toplevel, const char *title) +// { +// WPEToplevelSDL3 *self = WPE_TOPLEVEL_SDL3(toplevel); +// if (self->window) +// SDL_SetWindowTitle(self->window, title ? title : SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING)); +// } + +// static gboolean +// wpe_toplevel_sdl3_resize(WPEToplevel *toplevel, int width, int height) +// { +// WPEToplevelSDL3 *self = WPE_TOPLEVEL_SDL3(toplevel); +// if (!SDL_SetWindowSize(self->window, width, height)) { +// g_warning("Could not resize SDL window for toplevel %p: %s", self, SDL_GetError()); +// return FALSE; +// } +// +// wpe_toplevel_resized(toplevel, width, height); +// wpe_toplevel_foreach_view(toplevel, wpe_toplevel_sdl3_each_view_resized, NULL); +// return TRUE; +// } + +static void +wpe_toplevel_sdl3_constructed(GObject *object) +{ + G_OBJECT_CLASS(wpe_toplevel_sdl3_parent_class)->constructed(object); +} + +// what the fuck is this +// im just going to steal this function and put it in my bindings +// GLib is so confusing T_T +// +// coming back: i think this like, removes a view? e.g. closing a tab or smth +static gboolean +wpe_toplevel_sdl3_each_view_detach(WPEToplevel *toplevel [[maybe_unused]], WPEView *view, void *userdata [[maybe_unused]]) +{ + wpe_view_set_toplevel(view, NULL); + return FALSE; // Continue iterating views. +} + +static void +wpe_toplevel_sdl3_dispose(GObject *object) +{ + WPEToplevelSDL3 *self = WPE_TOPLEVEL_SDL3(object); + g_debug("%s: toplevel=%p", G_STRFUNC, self); + + wpe_toplevel_foreach_view(WPE_TOPLEVEL(object), wpe_toplevel_sdl3_each_view_detach, NULL); + + g_clear_pointer(&self->texture, SDL_DestroyTexture); + + G_OBJECT_CLASS(wpe_toplevel_sdl3_parent_class)->dispose(object); +} + +static void +wpe_toplevel_sdl3_class_init(WPEToplevelSDL3Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->constructed = wpe_toplevel_sdl3_constructed; + object_class->finalize = wpe_toplevel_sdl3_dispose; + + WPEToplevelClass *toplevel_class = WPE_TOPLEVEL_CLASS(klass); + // TODO add handlers for this in the future, maybe let the user hook into them with the abstractions + // toplevel_class->set_fullscreen = wpe_toplevel_sdl3_set_fullscreen; + // toplevel_class->set_title = wpe_toplevel_sdl3_set_title; + // toplevel_class->resize = wpe_toplevel_sdl3_resize; +} + +static WPEToplevel* +wpe_toplevel_sdl3_new(WPEDisplay *display) +{ + g_return_val_if_fail(WPE_IS_DISPLAY_SDL3(display), NULL); + return g_object_new(wpe_toplevel_sdl3_get_type(), "display", display, "max-views", 1, NULL); +} + +static WPEView* +wpe_toplevel_sdl3_get_view(WPEToplevelSDL3 *self) +{ + return wpe_toplevel_get_n_views((WPEToplevel*) self) ? self->view : NULL; +} + +static void +wpe_display_sdl3_finalize(GObject *object) +{ + WPEDisplaySDL3 *self = WPE_DISPLAY_SDL3(object); + g_debug("%s: display=%p", G_STRFUNC, self); + + self->egl_display = EGL_NO_DISPLAY; + self->destroyImage = NULL; + self->imageTargetTexture2DOES = NULL; + + g_clear_pointer(&self->gl_context, SDL_GL_DestroyContext); + g_clear_pointer(&self->hidden_window, SDL_DestroyWindow); + + if (self->init_flags) { + SDL_QuitSubSystem(self->init_flags); + self->init_flags = 0; + } + + G_OBJECT_CLASS(wpe_display_sdl3_parent_class)->finalize(object); +} + +static gboolean +wpe_display_sdl3_connect(WPEDisplay *display, GError **error) +{ + WPEDisplaySDL3 *self = WPE_DISPLAY_SDL3(display); + + if (!SDL_WasInit(SDL_INIT_EVENTS)) + self->init_flags |= SDL_INIT_EVENTS; + if (!SDL_WasInit(SDL_INIT_VIDEO)) + self->init_flags |= SDL_INIT_VIDEO; + + if (self->init_flags && !SDL_Init(self->init_flags)) { + g_set_error(error, WPE_DISPLAY_ERROR, WPE_DISPLAY_ERROR_CONNECTION_FAILED, + "Could not initialize SDL: %s", SDL_GetError()); + return FALSE; + } + + if (!(self->hidden_window = SDL_CreateWindow("Dummy", 1, 1, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL))) { + g_set_error(error, WPE_DISPLAY_ERROR, WPE_DISPLAY_ERROR_CONNECTION_FAILED, + "Could not create SDL hidden window: %s", SDL_GetError()); + return FALSE; + } + + if (!(self->gl_context = SDL_GL_CreateContext(self->hidden_window))) { + g_set_error(error, WPE_DISPLAY_ERROR, WPE_DISPLAY_ERROR_CONNECTION_FAILED, + "Could not create SDL-managed EGL context: %s", SDL_GetError()); + return FALSE; + } + SDL_GL_MakeCurrent(self->hidden_window, self->gl_context); + + if (!SDL_SyncWindow(self->hidden_window)) { + g_set_error(error, WPE_DISPLAY_ERROR, WPE_DISPLAY_ERROR_CONNECTION_FAILED, + "Could not sync hidden SDL window: %s", SDL_GetError()); + return FALSE; + } + + if ((self->egl_display = SDL_EGL_GetCurrentDisplay()) == EGL_NO_DISPLAY) { + g_set_error(error, WPE_DISPLAY_ERROR, WPE_DISPLAY_ERROR_CONNECTION_FAILED, + "Could not get SDL-managed EGL display: %s", SDL_GetError()); + return FALSE; + } + + self->destroyImage = (PFNEGLDESTROYIMAGEKHRPROC) + SDL_EGL_GetProcAddress("eglDestroyImageKHR"); + self->imageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) + SDL_EGL_GetProcAddress("glEGLImageTargetTexture2DOES"); + g_debug("%s: done, eglDestroyImageKHR=%p, glEGLImageTargetTexture2DOES=%p", G_STRFUNC, + self->destroyImage, self->imageTargetTexture2DOES); + + if (!self->destroyImage) { + g_set_error_literal(error, WPE_DISPLAY_ERROR, WPE_DISPLAY_ERROR_CONNECTION_FAILED, + "EGL does not support eglDestroyImageKHR"); + return FALSE; + } + + return TRUE; +} + +static EGLDisplay +wpe_display_sdl3_get_egl_display(WPEDisplay *display, GError **error [[maybe_unused]]) +{ + WPEDisplaySDL3 *self = WPE_DISPLAY_SDL3(display); + return self->egl_display; +} +// +// creates a new wpe view and toplevel +// oh and don't forget, GLib black magic +// yaaayyyyyyyyyyyy... we love glib (sarc) +static WPEView* +wpe_display_sdl3_create_view(WPEDisplay *display) +{ + g_autoptr(WPEView) view = wpe_view_sdl3_new(display); + + const gboolean create_toplevel = wpe_settings_get_boolean(wpe_display_get_settings(display), WPE_SETTING_CREATE_VIEWS_WITH_A_TOPLEVEL, NULL); + if (create_toplevel) { + g_autoptr(WPEToplevel) toplevel = wpe_toplevel_sdl3_new(display); + wpe_view_set_toplevel(view, toplevel); + } + + return g_steal_pointer(&view); +} + +// initializes the WPEDisplaySDL3Class class? what is this sh*t? why did they have to use GLib for the WPE api? +// WHY IGALIA WHYYYYYYYY +static void +wpe_display_sdl3_class_init(WPEDisplaySDL3Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = wpe_display_sdl3_finalize; + + WPEDisplayClass *display_class = WPE_DISPLAY_CLASS(klass); + display_class->connect = wpe_display_sdl3_connect; + display_class->get_egl_display = wpe_display_sdl3_get_egl_display; + display_class->create_view = wpe_display_sdl3_create_view; +} + +static void +wpe_display_sdl3_init(WPEDisplaySDL3 *self) +{ + g_debug("%s: display=%p", G_STRFUNC, self); +} + +// if the webview has a new title, go call that function that i annotated like 10 years ago and do the thing +static void +handle_web_view_notify_title(WebKitWebView *web_view) +{ + const char *title = webkit_web_view_get_title(web_view); + if (!(title && *title)) + title = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING); + + g_message("View<%p>.title = %s", web_view, title); + + WPEView *view = webkit_web_view_get_wpe_view(web_view); + wpe_toplevel_set_title(wpe_view_get_toplevel(view), title); +} + +static WebKitWebView* handle_web_view_create(WebKitWebView*, WebKitNavigationAction*, SFWKContext*); + + +static void +free_weak_ref(void *ptr) +{ + GWeakRef *ref = ptr; + g_weak_ref_clear(ref); + g_free(ref); +} + +static WebKitWebView* +context_add_web_view(SFWKContext* self, WebKitWebView *related_view, const char *uri) +{ + g_autoptr(WebKitWebView) view = g_object_new(WEBKIT_TYPE_WEB_VIEW, + "display", self->display, + "related-view", related_view, + NULL); + + GWeakRef *view_ref = g_new0(GWeakRef, 1); + g_weak_ref_init(view_ref, view); + g_object_set_data_full(G_OBJECT(webkit_web_view_get_wpe_view(view)), "weak-web-view", view_ref, free_weak_ref); + + g_signal_connect(view, "notify::title", G_CALLBACK(handle_web_view_notify_title), NULL); + g_signal_connect(view, "create", G_CALLBACK(handle_web_view_create), self); + + if (uri) + webkit_web_view_load_uri(view, uri); + + return g_steal_pointer(&view); +} + +static WebKitWebView* +handle_web_view_create(WebKitWebView *view, WebKitNavigationAction *action [[maybe_unused]], SFWKContext *context) +{ + return context_add_web_view(context, view, NULL); +} + +// map SDL buttons to WPE buttons +static unsigned +wpe_button_for_sdl_button(unsigned index) +{ + switch (index) { + case 1: return WPE_BUTTON_PRIMARY; + case 2: return WPE_BUTTON_MIDDLE; + case 3: return WPE_BUTTON_SECONDARY; + default: return 0; + } +} + +// map SDL keymodifiers to WPE mods +static WPEModifiers +wpe_modifiers_for_sdl_keymod(SDL_Keymod keymod) +{ + WPEModifiers result = 0; + if (keymod & SDL_KMOD_CTRL) + result |= WPE_MODIFIER_KEYBOARD_CONTROL; + if (keymod & SDL_KMOD_SHIFT) + result |= WPE_MODIFIER_KEYBOARD_SHIFT; + if (keymod & SDL_KMOD_ALT) + result |= WPE_MODIFIER_KEYBOARD_ALT; + if (keymod & SDL_KMOD_GUI) + result |= WPE_MODIFIER_KEYBOARD_META; + if (keymod & SDL_KMOD_CAPS) + result |= WPE_MODIFIER_KEYBOARD_CAPS_LOCK; + return result; +} + +SFWKContext* sfwk_init() { + g_io_extension_point_register(WPE_DISPLAY_EXTENSION_POINT_NAME); + g_io_extension_point_implement(WPE_DISPLAY_EXTENSION_POINT_NAME, wpe_display_sdl3_get_type(), "sdl3", 200); + + SFWKContext* context = g_new0(SFWKContext, 1); + context->main_context = g_main_context_new_with_flags(G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING); + + g_autoptr(GError) error = NULL; + + context->display = g_object_new(wpe_display_sdl3_get_type(), NULL); + if (!wpe_display_connect(context->display, &error)) { + g_printerr("cant connect to display T_T %s\n", error->message); + g_clear_object(&context->display); + g_clear_pointer(&context->main_context, g_main_context_unref); + g_free(context); + return NULL; + } + + return context; +} + +static void sfwk_webview_draw(SaffronWidget* widget, SDL_Renderer* renderer) { + SFWKWebView* webview = (SFWKWebView*)widget; + webview->renderer = renderer; + + WPEToplevelSDL3 *toplevel = (WPEToplevelSDL3*)webview->toplevel; + if (toplevel->texture) { + SDL_RenderTexture(renderer, toplevel->texture, NULL, NULL); + } +} + +void sfwk_process_event(SFWKContext *context, SFWKWebView* webview, SDL_Event *event) { + WPEToplevel* toplevel = webview->toplevel; + // for some reason we're doing this weird sh*t? we could just use webview->view but oh well + WPEView *view = toplevel ? wpe_toplevel_sdl3_get_view((WPEToplevelSDL3*) toplevel) : NULL; +} + +SFWKWebView* sfwk_webview_new(SFWKContext* context, const char* url, int w, int h) { + SFWKWebView* webview = malloc(sizeof(SFWKWebView)); + if (!webview) return NULL; // good fucking luck + // + saffron_widget_init((SaffronWidget*)webview); + ((SaffronWidget*)webview)->type = SAFFRON_WIDGET_UNKNOWN; // saffron does not have a builtin for webviews, and i cant be bothered to add one because "if you have to edit the library, it's not extensible" - arslaan 2026 + ((SaffronWidget*)webview)->w = w; + ((SaffronWidget*)webview)->h = h; + ((SaffronWidget*)webview)->draw = sfwk_webview_draw; + // add an on_resize here + + webview->toplevel = wpe_toplevel_sdl3_new(context->display); + + webview->wpeview = wpe_view_sdl3_new(context->display); + ((WPEViewSDL3*)webview->wpeview)->userdata = webview; + wpe_view_set_toplevel(webview->wpeview, webview->toplevel); + + webview->wkwebview = g_object_new(WEBKIT_TYPE_WEB_VIEW, "display", context->display, NULL); + webkit_web_view_load_uri(webview->wkwebview, url); + + return webview; +} |
