1 /* Copyright (C) <2018> Philippe Normand <philn@igalia.com>
2 * Copyright (C) <2018> Žan Doberšek <zdobersek@igalia.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 #include "WPEThreadedView.h"
25 #define GST_CAT_DEFAULT wpe_src_debug
27 // -70 is the GLib priority we use internally in WebKit, for WPE.
28 #define WPE_GLIB_SOURCE_PRIORITY -70
32 GMutexHolder(GMutex& mutex)
46 WPEThreadedView::WPEThreadedView()
48 g_mutex_init(&threading.mutex);
49 g_cond_init(&threading.cond);
50 g_mutex_init(&threading.ready_mutex);
51 g_cond_init(&threading.ready_cond);
53 g_mutex_init(&images.mutex);
56 GMutexHolder lock(threading.mutex);
57 threading.thread = g_thread_new("WPEThreadedView",
59 g_cond_wait(&threading.cond, &threading.mutex);
60 GST_DEBUG("thread spawned");
64 WPEThreadedView::~WPEThreadedView()
67 GMutexHolder lock(images.mutex);
70 gst_egl_image_unref(images.pending);
71 images.pending = nullptr;
73 if (images.committed) {
74 gst_egl_image_unref(images.committed);
75 images.committed = nullptr;
80 GMutexHolder lock(threading.mutex);
81 wpe_view_backend_exportable_fdo_destroy(wpe.exportable);
85 gst_object_unref(gst.display);
86 gst.display = nullptr;
90 gst_object_unref(gst.context);
91 gst.context = nullptr;
94 if (threading.thread) {
95 g_thread_unref(threading.thread);
96 threading.thread = nullptr;
99 g_mutex_clear(&threading.mutex);
100 g_cond_clear(&threading.cond);
101 g_mutex_clear(&threading.ready_mutex);
102 g_cond_clear(&threading.ready_cond);
103 g_mutex_clear(&images.mutex);
106 gpointer WPEThreadedView::s_viewThread(gpointer data)
108 auto& view = *static_cast<WPEThreadedView*>(data);
110 view.glib.context = g_main_context_new();
111 view.glib.loop = g_main_loop_new(view.glib.context, FALSE);
113 g_main_context_push_thread_default(view.glib.context);
116 GSource* source = g_idle_source_new();
117 g_source_set_callback(source,
118 [](gpointer data) -> gboolean {
119 auto& view = *static_cast<WPEThreadedView*>(data);
120 GMutexHolder lock(view.threading.mutex);
121 g_cond_signal(&view.threading.cond);
122 return G_SOURCE_REMOVE;
125 g_source_attach(source, view.glib.context);
126 g_source_unref(source);
129 g_main_loop_run(view.glib.loop);
131 g_main_loop_unref(view.glib.loop);
132 view.glib.loop = nullptr;
134 if (view.webkit.view) {
135 g_object_unref(view.webkit.view);
136 view.webkit.view = nullptr;
138 if (view.webkit.uri) {
139 g_free(view.webkit.uri);
140 view.webkit.uri = nullptr;
143 g_main_context_pop_thread_default(view.glib.context);
144 g_main_context_unref(view.glib.context);
145 view.glib.context = nullptr;
149 struct wpe_view_backend* WPEThreadedView::backend() const
151 return wpe.exportable ? wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable) : nullptr;
154 void WPEThreadedView::s_loadEvent(WebKitWebView*, WebKitLoadEvent event, gpointer data)
156 if (event == WEBKIT_LOAD_COMMITTED) {
157 auto& view = *static_cast<WPEThreadedView*>(data);
158 GMutexHolder lock(view.threading.ready_mutex);
159 g_cond_signal(&view.threading.ready_cond);
163 void WPEThreadedView::initialize(GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height)
165 GST_DEBUG("context %p display %p, size (%d,%d)", context, display, width, height);
167 static std::once_flag s_loaderFlag;
168 std::call_once(s_loaderFlag,
170 #if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(0, 2, 0)
171 wpe_loader_init("libWPEBackend-fdo-0.1.so");
175 struct InitializeContext {
177 WPEThreadedView& view;
178 GstGLContext* context;
179 GstGLDisplay* display;
182 } initializeContext{ src, *this, context, display, width, height };
184 GSource* source = g_idle_source_new();
185 g_source_set_callback(source,
186 [](gpointer data) -> gboolean {
187 GST_DEBUG("on view thread");
188 auto& initializeContext = *static_cast<InitializeContext*>(data);
189 auto& view = initializeContext.view;
191 GMutexHolder lock(view.threading.mutex);
193 view.gst.context = GST_GL_CONTEXT(gst_object_ref(initializeContext.context));
194 view.gst.display = GST_GL_DISPLAY(gst_object_ref(initializeContext.display));
196 view.wpe.width = initializeContext.width;
197 view.wpe.height = initializeContext.height;
199 EGLDisplay eglDisplay = gst_gl_display_egl_get_from_native(
200 GST_GL_DISPLAY_TYPE_WAYLAND,
201 gst_gl_display_get_handle(initializeContext.display));
202 GST_DEBUG("eglDisplay %p", eglDisplay);
203 wpe_fdo_initialize_for_egl_display(eglDisplay);
205 view.wpe.exportable = wpe_view_backend_exportable_fdo_egl_create(&s_exportableClient,
206 &view, view.wpe.width, view.wpe.height);
207 auto* viewBackend = webkit_web_view_backend_new(
208 wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable), nullptr, nullptr);
210 view.webkit.view = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
211 "backend", viewBackend, nullptr));
213 gst_wpe_src_configure_web_view(initializeContext.src, view.webkit.view);
215 g_signal_connect(view.webkit.view, "load-changed", G_CALLBACK(s_loadEvent), &view);
217 const gchar* location;
218 gboolean drawBackground = TRUE;
219 g_object_get(initializeContext.src, "location", &location, "draw-background", &drawBackground, nullptr);
221 g_warning("Invalid location");
223 view.setDrawBackground(drawBackground);
224 view.loadUriUnlocked(location);
226 g_cond_signal(&view.threading.cond);
227 return G_SOURCE_REMOVE;
229 &initializeContext, nullptr);
230 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
233 GMutexHolder lock(threading.mutex);
234 g_source_attach(source, glib.context);
235 g_cond_wait(&threading.cond, &threading.mutex);
238 g_source_unref(source);
241 GST_DEBUG("waiting load to finish");
242 GMutexHolder lock(threading.ready_mutex);
243 g_cond_wait(&threading.ready_cond, &threading.ready_mutex);
248 GstEGLImage* WPEThreadedView::image()
250 GstEGLImage* ret = nullptr;
251 GMutexHolder lock(images.mutex);
253 GST_TRACE("pending %" GST_PTR_FORMAT " committed %" GST_PTR_FORMAT, images.pending, images.committed);
255 if (images.pending) {
256 auto* previousImage = images.committed;
257 images.committed = images.pending;
258 images.pending = nullptr;
263 gst_egl_image_unref(previousImage);
266 if (images.committed)
267 ret = images.committed;
272 void WPEThreadedView::resize(int width, int height)
276 GSource* source = g_idle_source_new();
277 g_source_set_callback(source,
278 [](gpointer data) -> gboolean {
279 auto& view = *static_cast<WPEThreadedView*>(data);
280 GMutexHolder lock(view.threading.mutex);
282 GST_DEBUG("dispatching");
283 if (view.wpe.exportable && wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable))
284 wpe_view_backend_dispatch_set_size(wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable), view.wpe.width, view.wpe.height);
286 g_cond_signal(&view.threading.cond);
287 return G_SOURCE_REMOVE;
290 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
293 GMutexHolder lock(threading.mutex);
294 g_source_attach(source, glib.context);
295 g_cond_wait(&threading.cond, &threading.mutex);
298 g_source_unref(source);
301 void WPEThreadedView::frameComplete()
303 GST_DEBUG("frame complete");
305 GSource* source = g_idle_source_new();
306 g_source_set_callback(source,
307 [](gpointer data) -> gboolean {
308 auto& view = *static_cast<WPEThreadedView*>(data);
309 GMutexHolder lock(view.threading.mutex);
311 GST_DEBUG("dispatching");
312 wpe_view_backend_exportable_fdo_dispatch_frame_complete(view.wpe.exportable);
314 g_cond_signal(&view.threading.cond);
315 return G_SOURCE_REMOVE;
318 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
321 GMutexHolder lock(threading.mutex);
322 g_source_attach(source, glib.context);
323 g_cond_wait(&threading.cond, &threading.mutex);
326 g_source_unref(source);
329 void WPEThreadedView::loadUriUnlocked(const gchar* uri)
333 webkit.uri = g_strdup(uri);
334 webkit_web_view_load_uri(webkit.view, webkit.uri);
337 void WPEThreadedView::loadUri(const gchar* uri)
339 GST_DEBUG("loading %s", uri);
342 WPEThreadedView& view;
344 } uriContext{ *this, uri };
346 GSource* source = g_idle_source_new();
347 g_source_set_callback(source,
348 [](gpointer data) -> gboolean {
349 GST_DEBUG("on view thread");
350 auto& uriContext = *static_cast<UriContext*>(data);
351 auto& view = uriContext.view;
352 GMutexHolder lock(view.threading.mutex);
354 view.loadUriUnlocked(uriContext.uri);
356 g_cond_signal(&view.threading.cond);
357 return G_SOURCE_REMOVE;
359 &uriContext, nullptr);
360 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
363 GMutexHolder lock(threading.mutex);
364 g_source_attach(source, glib.context);
365 g_cond_wait(&threading.cond, &threading.mutex);
369 g_source_unref(source);
372 void WPEThreadedView::setDrawBackground(gboolean drawsBackground)
374 #if WEBKIT_CHECK_VERSION(2, 23, 0)
375 GST_DEBUG("%s background rendering", drawsBackground ? "Enabling" : "Disabling");
377 webkit_color_parse(&color, drawsBackground ? "white" : "transparent");
378 webkit_web_view_set_background_color(webkit.view, &color);
380 GST_FIXME("webkit_web_view_set_background_color is not implemented in WPE %u.%u. Please upgrade to 2.24", webkit_get_major_version(), webkit_get_minor_version());
384 void WPEThreadedView::releaseImage(EGLImageKHR image)
386 struct ReleaseImageContext {
387 WPEThreadedView& view;
389 } releaseImageContext{ *this, image };
391 GSource* source = g_idle_source_new();
392 g_source_set_callback(source,
393 [](gpointer data) -> gboolean {
394 auto& releaseImageContext = *static_cast<ReleaseImageContext*>(data);
395 auto& view = releaseImageContext.view;
396 GMutexHolder lock(view.threading.mutex);
398 wpe_view_backend_exportable_fdo_egl_dispatch_release_image(
399 releaseImageContext.view.wpe.exportable, releaseImageContext.image);
401 g_cond_signal(&view.threading.cond);
402 return G_SOURCE_REMOVE;
404 &releaseImageContext, nullptr);
405 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
408 GMutexHolder lock(threading.mutex);
409 g_source_attach(source, glib.context);
410 g_cond_wait(&threading.cond, &threading.mutex);
413 g_source_unref(source);
416 struct wpe_view_backend_exportable_fdo_egl_client WPEThreadedView::s_exportableClient = {
417 // export_buffer_resource
418 [](void* data, EGLImageKHR image) {
419 auto& view = *static_cast<WPEThreadedView*>(data);
420 auto* gstImage = gst_egl_image_new_wrapped(view.gst.context, image,
421 GST_GL_RGBA, &view, s_releaseImage);
422 GMutexHolder lock(view.images.mutex);
424 view.images.pending = gstImage;
427 nullptr, nullptr, nullptr, nullptr
430 void WPEThreadedView::s_releaseImage(GstEGLImage* image, gpointer data)
432 auto& view = *static_cast<WPEThreadedView*>(data);
433 GST_DEBUG("view %p image %" GST_PTR_FORMAT, &view, image);
434 view.releaseImage(gst_egl_image_get_image(image));