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.
24 #include "WPEThreadedView.h"
26 #include <gst/gl/gl.h>
27 #include <gst/gl/egl/gsteglimage.h>
28 #include <gst/gl/egl/gstgldisplay_egl.h>
33 GST_DEBUG_CATEGORY_EXTERN (wpe_src_debug);
34 #define GST_CAT_DEFAULT wpe_src_debug
36 #if defined(WPE_FDO_CHECK_VERSION) && WPE_FDO_CHECK_VERSION(1, 3, 0)
37 #define USE_DEPRECATED_FDO_EGL_IMAGE 0
38 #define WPE_GLIB_SOURCE_PRIORITY G_PRIORITY_DEFAULT
40 #define USE_DEPRECATED_FDO_EGL_IMAGE 1
41 #define WPE_GLIB_SOURCE_PRIORITY -70
46 GMutexHolder(GMutex& mutex)
60 WPEThreadedView::WPEThreadedView()
62 g_mutex_init(&threading.mutex);
63 g_cond_init(&threading.cond);
64 g_mutex_init(&threading.ready_mutex);
65 g_cond_init(&threading.ready_cond);
67 g_mutex_init(&images.mutex);
70 GMutexHolder lock(threading.mutex);
71 threading.thread = g_thread_new("WPEThreadedView",
73 g_cond_wait(&threading.cond, &threading.mutex);
74 GST_DEBUG("thread spawned");
78 WPEThreadedView::~WPEThreadedView()
81 GMutexHolder lock(images.mutex);
84 gst_egl_image_unref(images.pending);
85 images.pending = nullptr;
87 if (images.committed) {
88 gst_egl_image_unref(images.committed);
89 images.committed = nullptr;
94 GMutexHolder lock(threading.mutex);
95 wpe_view_backend_exportable_fdo_destroy(wpe.exportable);
99 gst_object_unref(gst.display);
100 gst.display = nullptr;
104 gst_object_unref(gst.context);
105 gst.context = nullptr;
108 if (threading.thread) {
109 g_thread_unref(threading.thread);
110 threading.thread = nullptr;
113 g_mutex_clear(&threading.mutex);
114 g_cond_clear(&threading.cond);
115 g_mutex_clear(&threading.ready_mutex);
116 g_cond_clear(&threading.ready_cond);
117 g_mutex_clear(&images.mutex);
120 gpointer WPEThreadedView::s_viewThread(gpointer data)
122 auto& view = *static_cast<WPEThreadedView*>(data);
124 view.glib.context = g_main_context_new();
125 view.glib.loop = g_main_loop_new(view.glib.context, FALSE);
127 g_main_context_push_thread_default(view.glib.context);
130 GSource* source = g_idle_source_new();
131 g_source_set_callback(source,
132 [](gpointer data) -> gboolean {
133 auto& view = *static_cast<WPEThreadedView*>(data);
134 GMutexHolder lock(view.threading.mutex);
135 g_cond_signal(&view.threading.cond);
136 return G_SOURCE_REMOVE;
139 g_source_attach(source, view.glib.context);
140 g_source_unref(source);
143 g_main_loop_run(view.glib.loop);
145 g_main_loop_unref(view.glib.loop);
146 view.glib.loop = nullptr;
148 if (view.webkit.view) {
149 g_object_unref(view.webkit.view);
150 view.webkit.view = nullptr;
152 if (view.webkit.uri) {
153 g_free(view.webkit.uri);
154 view.webkit.uri = nullptr;
157 g_main_context_pop_thread_default(view.glib.context);
158 g_main_context_unref(view.glib.context);
159 view.glib.context = nullptr;
163 struct wpe_view_backend* WPEThreadedView::backend() const
165 return wpe.exportable ? wpe_view_backend_exportable_fdo_get_view_backend(wpe.exportable) : nullptr;
168 void WPEThreadedView::s_loadEvent(WebKitWebView*, WebKitLoadEvent event, gpointer data)
170 if (event == WEBKIT_LOAD_COMMITTED) {
171 auto& view = *static_cast<WPEThreadedView*>(data);
172 GMutexHolder lock(view.threading.ready_mutex);
173 g_cond_signal(&view.threading.ready_cond);
177 void WPEThreadedView::initialize(GstWpeSrc* src, GstGLContext* context, GstGLDisplay* display, int width, int height)
179 GST_DEBUG("context %p display %p, size (%d,%d)", context, display, width, height);
181 static std::once_flag s_loaderFlag;
182 std::call_once(s_loaderFlag,
184 #if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(1, 2, 0)
185 wpe_loader_init("libWPEBackend-fdo-1.0.so");
189 struct InitializeContext {
191 WPEThreadedView& view;
192 GstGLContext* context;
193 GstGLDisplay* display;
196 } initializeContext{ src, *this, context, display, width, height };
198 GSource* source = g_idle_source_new();
199 g_source_set_callback(source,
200 [](gpointer data) -> gboolean {
201 GST_DEBUG("on view thread");
202 auto& initializeContext = *static_cast<InitializeContext*>(data);
203 auto& view = initializeContext.view;
205 GMutexHolder lock(view.threading.mutex);
207 view.gst.context = GST_GL_CONTEXT(gst_object_ref(initializeContext.context));
208 view.gst.display = GST_GL_DISPLAY(gst_object_ref(initializeContext.display));
210 view.wpe.width = initializeContext.width;
211 view.wpe.height = initializeContext.height;
213 EGLDisplay eglDisplay = gst_gl_display_egl_get_from_native(
214 GST_GL_DISPLAY_TYPE_WAYLAND,
215 gst_gl_display_get_handle(initializeContext.display));
216 GST_DEBUG("eglDisplay %p", eglDisplay);
217 wpe_fdo_initialize_for_egl_display(eglDisplay);
219 view.wpe.exportable = wpe_view_backend_exportable_fdo_egl_create(&s_exportableClient,
220 &view, view.wpe.width, view.wpe.height);
221 auto* wpeViewBackend = wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable);
222 auto* viewBackend = webkit_web_view_backend_new(wpeViewBackend, nullptr, nullptr);
223 #if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(1, 1, 0)
224 wpe_view_backend_add_activity_state(wpeViewBackend, wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window);
227 view.webkit.view = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
228 "backend", viewBackend, nullptr));
230 gst_wpe_src_configure_web_view(initializeContext.src, view.webkit.view);
232 g_signal_connect(view.webkit.view, "load-changed", G_CALLBACK(s_loadEvent), &view);
234 const gchar* location;
235 gboolean drawBackground = TRUE;
236 g_object_get(initializeContext.src, "location", &location, "draw-background", &drawBackground, nullptr);
238 g_warning("Invalid location");
240 view.setDrawBackground(drawBackground);
241 view.loadUriUnlocked(location);
243 g_cond_signal(&view.threading.cond);
244 return G_SOURCE_REMOVE;
246 &initializeContext, nullptr);
247 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
250 GMutexHolder lock(threading.mutex);
251 g_source_attach(source, glib.context);
252 g_cond_wait(&threading.cond, &threading.mutex);
255 g_source_unref(source);
258 GST_DEBUG("waiting load to finish");
259 GMutexHolder lock(threading.ready_mutex);
260 g_cond_wait(&threading.ready_cond, &threading.ready_mutex);
265 GstEGLImage* WPEThreadedView::image()
267 GstEGLImage* ret = nullptr;
268 GMutexHolder lock(images.mutex);
270 GST_TRACE("pending %" GST_PTR_FORMAT " committed %" GST_PTR_FORMAT, images.pending, images.committed);
272 if (images.pending) {
273 auto* previousImage = images.committed;
274 images.committed = images.pending;
275 images.pending = nullptr;
280 gst_egl_image_unref(previousImage);
283 if (images.committed)
284 ret = images.committed;
289 void WPEThreadedView::resize(int width, int height)
293 GSource* source = g_idle_source_new();
294 g_source_set_callback(source,
295 [](gpointer data) -> gboolean {
296 auto& view = *static_cast<WPEThreadedView*>(data);
297 GMutexHolder lock(view.threading.mutex);
299 GST_DEBUG("dispatching");
300 if (view.wpe.exportable && wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable))
301 wpe_view_backend_dispatch_set_size(wpe_view_backend_exportable_fdo_get_view_backend(view.wpe.exportable), view.wpe.width, view.wpe.height);
303 g_cond_signal(&view.threading.cond);
304 return G_SOURCE_REMOVE;
307 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
310 GMutexHolder lock(threading.mutex);
311 g_source_attach(source, glib.context);
312 g_cond_wait(&threading.cond, &threading.mutex);
315 g_source_unref(source);
318 void WPEThreadedView::frameComplete()
320 GST_DEBUG("frame complete");
322 GSource* source = g_idle_source_new();
323 g_source_set_callback(source,
324 [](gpointer data) -> gboolean {
325 auto& view = *static_cast<WPEThreadedView*>(data);
326 GMutexHolder lock(view.threading.mutex);
328 GST_DEBUG("dispatching");
329 wpe_view_backend_exportable_fdo_dispatch_frame_complete(view.wpe.exportable);
331 g_cond_signal(&view.threading.cond);
332 return G_SOURCE_REMOVE;
335 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
338 GMutexHolder lock(threading.mutex);
339 g_source_attach(source, glib.context);
340 g_cond_wait(&threading.cond, &threading.mutex);
343 g_source_unref(source);
346 void WPEThreadedView::loadUriUnlocked(const gchar* uri)
351 GST_DEBUG("loading %s", uri);
352 webkit.uri = g_strdup(uri);
353 webkit_web_view_load_uri(webkit.view, webkit.uri);
356 void WPEThreadedView::loadUri(const gchar* uri)
359 WPEThreadedView& view;
361 } uriContext{ *this, uri };
363 GSource* source = g_idle_source_new();
364 g_source_set_callback(source,
365 [](gpointer data) -> gboolean {
366 GST_DEBUG("on view thread");
367 auto& uriContext = *static_cast<UriContext*>(data);
368 auto& view = uriContext.view;
369 GMutexHolder lock(view.threading.mutex);
371 view.loadUriUnlocked(uriContext.uri);
373 g_cond_signal(&view.threading.cond);
374 return G_SOURCE_REMOVE;
376 &uriContext, nullptr);
377 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
380 GMutexHolder lock(threading.mutex);
381 g_source_attach(source, glib.context);
382 g_cond_wait(&threading.cond, &threading.mutex);
386 g_source_unref(source);
389 void WPEThreadedView::setDrawBackground(gboolean drawsBackground)
391 #if WEBKIT_CHECK_VERSION(2, 24, 0)
392 GST_DEBUG("%s background rendering", drawsBackground ? "Enabling" : "Disabling");
394 webkit_color_parse(&color, drawsBackground ? "white" : "transparent");
395 webkit_web_view_set_background_color(webkit.view, &color);
397 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());
401 void WPEThreadedView::releaseImage(gpointer imagePointer)
403 struct ReleaseImageContext {
404 WPEThreadedView& view;
405 gpointer imagePointer;
406 } releaseImageContext{ *this, imagePointer };
408 GSource* source = g_idle_source_new();
409 g_source_set_callback(source,
410 [](gpointer data) -> gboolean {
411 auto& releaseImageContext = *static_cast<ReleaseImageContext*>(data);
412 auto& view = releaseImageContext.view;
413 GMutexHolder lock(view.threading.mutex);
415 GST_DEBUG("Dispatch release exported image");
416 #if USE_DEPRECATED_FDO_EGL_IMAGE
417 wpe_view_backend_exportable_fdo_egl_dispatch_release_image(releaseImageContext.view.wpe.exportable,
418 static_cast<EGLImageKHR>(releaseImageContext.imagePointer));
420 wpe_view_backend_exportable_fdo_egl_dispatch_release_exported_image(releaseImageContext.view.wpe.exportable,
421 static_cast<struct wpe_fdo_egl_exported_image*>(releaseImageContext.imagePointer));
423 g_cond_signal(&view.threading.cond);
424 return G_SOURCE_REMOVE;
426 &releaseImageContext, nullptr);
427 g_source_set_priority(source, WPE_GLIB_SOURCE_PRIORITY);
430 GMutexHolder lock(threading.mutex);
431 g_source_attach(source, glib.context);
432 g_cond_wait(&threading.cond, &threading.mutex);
435 g_source_unref(source);
438 struct ImageContext {
439 WPEThreadedView* view;
443 void WPEThreadedView::handleExportedImage(gpointer image)
445 ImageContext* imageContext = g_slice_new(ImageContext);
446 imageContext->view = this;
447 imageContext->image = static_cast<gpointer>(image);
448 EGLImageKHR eglImage;
449 #if USE_DEPRECATED_FDO_EGL_IMAGE
450 eglImage = static_cast<EGLImageKHR>(image);
452 eglImage = wpe_fdo_egl_exported_image_get_egl_image(static_cast<struct wpe_fdo_egl_exported_image*>(image));
455 auto* gstImage = gst_egl_image_new_wrapped(gst.context, eglImage, GST_GL_RGBA, imageContext, s_releaseImage);
456 GMutexHolder lock(images.mutex);
457 images.pending = gstImage;
460 struct wpe_view_backend_exportable_fdo_egl_client WPEThreadedView::s_exportableClient = {
461 #if USE_DEPRECATED_FDO_EGL_IMAGE
463 [](void* data, EGLImageKHR image) {
464 auto& view = *static_cast<WPEThreadedView*>(data);
465 view.handleExportedImage(static_cast<gpointer>(image));
471 [](void* data, struct wpe_fdo_egl_exported_image* image) {
472 auto& view = *static_cast<WPEThreadedView*>(data);
473 view.handleExportedImage(static_cast<gpointer>(image));
477 nullptr, nullptr, nullptr
480 void WPEThreadedView::s_releaseImage(GstEGLImage* image, gpointer data)
482 ImageContext* context = static_cast<ImageContext*>(data);
483 context->view->releaseImage(context->image);
484 g_slice_free(ImageContext, context);