1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * MozContainerWayland is a wrapper over MozContainer which provides
9 * wl_surface for MozContainer widget.
10 *
11 * The widget scheme looks like:
12 *
13 * ---------------------------------------------------------
14 * | mShell Gtk widget (contains wl_surface owned by Gtk+) |
15 * | |
16 * | --------------------------------------------------- |
17 * | | mContainer (contains wl_surface owned by Gtk+) | |
18 * | | | |
19 * | | --------------------------------------------- | |
20 * | | | wl_subsurface (attached to wl_surface | | |
21 * | | | of mContainer) | | |
22 * | | | | | |
23 * | | | | | |
24 * | | --------------------------------------------- | |
25 * | --------------------------------------------------- |
26 * ---------------------------------------------------------
27 *
28 * We draw to wl_subsurface owned by MozContainerWayland.
29 * We need to wait until wl_surface of mContainer is created
30 * and then we create and attach our wl_subsurface to it.
31 *
32 * First wl_subsurface creation has these steps:
33 *
34 * 1) moz_container_wayland_size_allocate() handler is called when
35 * mContainer size/position is known.
36 * It calls moz_container_wayland_surface_create_locked(), registers
37 * a frame callback handler
38 * moz_container_wayland_frame_callback_handler().
39 *
40 * 2) moz_container_wayland_frame_callback_handler() is called
41 * when wl_surface owned by mozContainer is ready.
42 * We call initial_draw_cbs() handler and we can create our wl_subsurface
43 * on top of wl_surface owned by mozContainer.
44 *
45 * When MozContainer hides/show again, moz_container_wayland_size_allocate()
46 * handler may not be called as MozContainer size is set. So after first
47 * show/hide sequence use moz_container_wayland_map_event() to create
48 * wl_subsurface of MozContainer.
49 */
50
51 #include "MozContainer.h"
52
53 #include <dlfcn.h>
54 #include <glib.h>
55 #include <stdio.h>
56 #include <wayland-egl.h>
57
58 #include "mozilla/gfx/gfxVars.h"
59 #include "mozilla/StaticPrefs_widget.h"
60 #include "nsGtkUtils.h"
61 #include "nsWaylandDisplay.h"
62 #include "base/task.h"
63
64 #ifdef MOZ_LOGGING
65
66 # include "mozilla/Logging.h"
67 # include "nsTArray.h"
68 # include "Units.h"
69 # include "nsWindow.h"
70 extern mozilla::LazyLogModule gWidgetWaylandLog;
71 # define LOGWAYLAND(...) \
72 MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
73 #else
74 # define LOGWAYLAND(...)
75 #endif /* MOZ_LOGGING */
76
77 using namespace mozilla;
78 using namespace mozilla::widget;
79
80 /* init methods */
81 static void moz_container_wayland_destroy(GtkWidget* widget);
82
83 /* widget class methods */
84 static void moz_container_wayland_map(GtkWidget* widget);
85 static gboolean moz_container_wayland_map_event(GtkWidget* widget,
86 GdkEventAny* event);
87 static void moz_container_wayland_unmap(GtkWidget* widget);
88 static void moz_container_wayland_size_allocate(GtkWidget* widget,
89 GtkAllocation* allocation);
90 static bool moz_container_wayland_surface_create_locked(
91 const MutexAutoLock& aProofOfLock, MozContainer* container);
92 static void moz_container_wayland_set_opaque_region_locked(
93 const MutexAutoLock& aProofOfLock, MozContainer* container);
94
95 // Imlemented in MozContainer.cpp
96 void moz_container_realize(GtkWidget* widget);
97
98 // Invalidate gtk wl_surface to commit changes to wl_subsurface.
99 // wl_subsurface changes are effective when parent surface is commited.
moz_container_wayland_invalidate(MozContainer * container)100 static void moz_container_wayland_invalidate(MozContainer* container) {
101 LOGWAYLAND("moz_container_wayland_invalidate [%p]\n",
102 (void*)moz_container_get_nsWindow(container));
103
104 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
105 if (!window) {
106 LOGWAYLAND(" Failed - missing GdkWindow!\n");
107 return;
108 }
109 gdk_window_invalidate_rect(window, nullptr, true);
110 }
111
112 // Route input to parent wl_surface owned by Gtk+ so we get input
113 // events from Gtk+.
moz_container_clear_input_region(MozContainer * container)114 static void moz_container_clear_input_region(MozContainer* container) {
115 struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor();
116 MozContainerWayland* wl_container = &container->wl_container;
117 wl_region* region = wl_compositor_create_region(compositor);
118 wl_surface_set_input_region(wl_container->surface, region);
119 wl_region_destroy(region);
120 }
121
moz_container_wayland_move_locked(const MutexAutoLock & aProofOfLock,MozContainer * container,int dx,int dy)122 static void moz_container_wayland_move_locked(const MutexAutoLock& aProofOfLock,
123 MozContainer* container, int dx,
124 int dy) {
125 LOGWAYLAND("moz_container_wayland_move [%p] %d,%d\n",
126 (void*)moz_container_get_nsWindow(container), dx, dy);
127
128 MozContainerWayland* wl_container = &container->wl_container;
129 if (!wl_container->subsurface || (wl_container->subsurface_dx == dx &&
130 wl_container->subsurface_dy == dy)) {
131 return;
132 }
133
134 wl_container->subsurface_dx = dx;
135 wl_container->subsurface_dy = dy;
136 wl_subsurface_set_position(wl_container->subsurface,
137 wl_container->subsurface_dx,
138 wl_container->subsurface_dy);
139 }
140
141 // This is called from layout/compositor code only with
142 // size equal to GL rendering context. Otherwise there are
143 // rendering artifacts as wl_egl_window size does not match
144 // GL rendering pipeline setup.
moz_container_wayland_egl_window_set_size(MozContainer * container,int width,int height)145 void moz_container_wayland_egl_window_set_size(MozContainer* container,
146 int width, int height) {
147 MozContainerWayland* wl_container = &container->wl_container;
148 MutexAutoLock lock(*wl_container->container_lock);
149 if (wl_container->eglwindow) {
150 wl_egl_window_resize(wl_container->eglwindow, width, height, 0, 0);
151 }
152 }
153
moz_container_wayland_class_init(MozContainerClass * klass)154 void moz_container_wayland_class_init(MozContainerClass* klass) {
155 /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
156 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */
157 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
158
159 widget_class->map = moz_container_wayland_map;
160 widget_class->map_event = moz_container_wayland_map_event;
161 widget_class->destroy = moz_container_wayland_destroy;
162 widget_class->unmap = moz_container_wayland_unmap;
163 widget_class->realize = moz_container_realize;
164 widget_class->size_allocate = moz_container_wayland_size_allocate;
165 }
166
moz_container_wayland_init(MozContainerWayland * container)167 void moz_container_wayland_init(MozContainerWayland* container) {
168 container->surface = nullptr;
169 container->subsurface = nullptr;
170 container->eglwindow = nullptr;
171 container->frame_callback_handler = nullptr;
172 container->viewport = nullptr;
173 container->ready_to_draw = false;
174 container->opaque_region_needs_updates = false;
175 container->opaque_region_corner_radius = 0;
176 container->opaque_region_used = false;
177 container->subsurface_dx = 0;
178 container->subsurface_dy = 0;
179 container->before_first_size_alloc = true;
180 container->buffer_scale = 1;
181 container->initial_draw_cbs.clear();
182 container->container_lock = new mozilla::Mutex("MozContainer lock");
183 container->commit_to_parent = false;
184 }
185
moz_container_wayland_destroy(GtkWidget * widget)186 static void moz_container_wayland_destroy(GtkWidget* widget) {
187 MozContainerWayland* container = &MOZ_CONTAINER(widget)->wl_container;
188 if (!container->container_lock) {
189 // moz_container_wayland_init was not called - it's a hidden container.
190 return;
191 }
192 moz_container_wayland_clear_initial_draw_callback(MOZ_CONTAINER(widget));
193 delete container->container_lock;
194 container->container_lock = nullptr;
195 }
196
moz_container_wayland_add_initial_draw_callback(MozContainer * container,const std::function<void (void)> & initial_draw_cb)197 void moz_container_wayland_add_initial_draw_callback(
198 MozContainer* container, const std::function<void(void)>& initial_draw_cb) {
199 MozContainerWayland* wl_container = &MOZ_CONTAINER(container)->wl_container;
200 {
201 MutexAutoLock lock(*container->wl_container.container_lock);
202 if (wl_container->ready_to_draw && !wl_container->surface) {
203 NS_WARNING(
204 "moz_container_wayland_add_initial_draw_callback: ready to draw "
205 "without wayland surface!");
206 }
207 if (!wl_container->ready_to_draw || !wl_container->surface) {
208 wl_container->initial_draw_cbs.push_back(initial_draw_cb);
209 return;
210 }
211 }
212
213 // We're ready to draw as
214 // wl_container->ready_to_draw && wl_container->surface
215 // call the callback directly instead of store them.
216 initial_draw_cb();
217 }
218
moz_container_wayland_clear_initial_draw_callback_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)219 static void moz_container_wayland_clear_initial_draw_callback_locked(
220 const MutexAutoLock& aProofOfLock, MozContainer* container) {
221 MozContainerWayland* wl_container = &MOZ_CONTAINER(container)->wl_container;
222 g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy);
223 wl_container->initial_draw_cbs.clear();
224 }
225
moz_container_wayland_clear_initial_draw_callback(MozContainer * container)226 void moz_container_wayland_clear_initial_draw_callback(
227 MozContainer* container) {
228 MutexAutoLock lock(*container->wl_container.container_lock);
229 moz_container_wayland_clear_initial_draw_callback_locked(lock, container);
230 }
231
moz_container_wayland_frame_callback_handler(void * data,struct wl_callback * callback,uint32_t time)232 static void moz_container_wayland_frame_callback_handler(
233 void* data, struct wl_callback* callback, uint32_t time) {
234 MozContainerWayland* wl_container = &MOZ_CONTAINER(data)->wl_container;
235
236 LOGWAYLAND(
237 "%s [%p] frame_callback_handler %p ready_to_draw %d (set to true)"
238 " initial_draw callback %zd\n",
239 __FUNCTION__, (void*)moz_container_get_nsWindow(MOZ_CONTAINER(data)),
240 (void*)wl_container->frame_callback_handler, wl_container->ready_to_draw,
241 wl_container->initial_draw_cbs.size());
242
243 std::vector<std::function<void(void)>> cbs;
244 {
245 // Protect mozcontainer internals changes by container_lock.
246 MutexAutoLock lock(*wl_container->container_lock);
247 g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy);
248 if (wl_container->ready_to_draw) {
249 return;
250 }
251 wl_container->ready_to_draw = true;
252 MOZ_DIAGNOSTIC_ASSERT(wl_container->surface,
253 "Should have surface if we're ready to draw");
254 cbs = std::move(wl_container->initial_draw_cbs);
255 }
256
257 // Call the callbacks registered by
258 // moz_container_wayland_add_initial_draw_callback().
259 // and we can't do that under mozcontainer lock.
260 for (auto const& cb : cbs) {
261 cb();
262 }
263 }
264
265 static const struct wl_callback_listener moz_container_frame_listener = {
266 moz_container_wayland_frame_callback_handler};
267
after_frame_clock_after_paint(GdkFrameClock * clock,MozContainer * container)268 static void after_frame_clock_after_paint(GdkFrameClock* clock,
269 MozContainer* container) {
270 struct wl_surface* surface = moz_container_wayland_surface_lock(container);
271 if (surface) {
272 wl_surface_commit(surface);
273 moz_container_wayland_surface_unlock(container, &surface);
274 }
275 }
276
moz_gdk_wayland_window_add_frame_callback_surface_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)277 static bool moz_gdk_wayland_window_add_frame_callback_surface_locked(
278 const MutexAutoLock& aProofOfLock, MozContainer* container) {
279 static auto sGdkWaylandWindowAddCallbackSurface =
280 (void (*)(GdkWindow*, struct wl_surface*))dlsym(
281 RTLD_DEFAULT, "gdk_wayland_window_add_frame_callback_surface");
282
283 if (!StaticPrefs::widget_wayland_opaque_region_enabled_AtStartup() ||
284 !sGdkWaylandWindowAddCallbackSurface) {
285 return false;
286 }
287
288 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
289 MozContainerWayland* wl_container = &container->wl_container;
290
291 sGdkWaylandWindowAddCallbackSurface(window, wl_container->surface);
292
293 GdkFrameClock* frame_clock = gdk_window_get_frame_clock(window);
294 g_signal_connect_after(frame_clock, "after-paint",
295 G_CALLBACK(after_frame_clock_after_paint), container);
296 return true;
297 }
298
moz_gdk_wayland_window_remove_frame_callback_surface_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)299 static void moz_gdk_wayland_window_remove_frame_callback_surface_locked(
300 const MutexAutoLock& aProofOfLock, MozContainer* container) {
301 static auto sGdkWaylandWindowRemoveCallbackSurface =
302 (void (*)(GdkWindow*, struct wl_surface*))dlsym(
303 RTLD_DEFAULT, "gdk_wayland_window_remove_frame_callback_surface");
304
305 if (!sGdkWaylandWindowRemoveCallbackSurface) {
306 return;
307 }
308
309 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
310 MozContainerWayland* wl_container = &container->wl_container;
311
312 if (wl_container->surface) {
313 sGdkWaylandWindowRemoveCallbackSurface(window, wl_container->surface);
314 }
315
316 GdkFrameClock* frame_clock = gdk_window_get_frame_clock(window);
317 g_signal_handlers_disconnect_by_func(
318 frame_clock, FuncToGpointer(after_frame_clock_after_paint), container);
319 }
320
moz_container_wayland_unmap_internal(MozContainer * container)321 static void moz_container_wayland_unmap_internal(MozContainer* container) {
322 MozContainerWayland* wl_container = &container->wl_container;
323 MutexAutoLock lock(*wl_container->container_lock);
324
325 LOGWAYLAND("%s [%p]\n", __FUNCTION__,
326 (void*)moz_container_get_nsWindow(container));
327
328 moz_container_wayland_clear_initial_draw_callback_locked(lock, container);
329
330 if (wl_container->opaque_region_used) {
331 moz_gdk_wayland_window_remove_frame_callback_surface_locked(lock,
332 container);
333 }
334 if (wl_container->commit_to_parent) {
335 wl_container->surface = nullptr;
336 }
337
338 g_clear_pointer(&wl_container->eglwindow, wl_egl_window_destroy);
339 g_clear_pointer(&wl_container->subsurface, wl_subsurface_destroy);
340 g_clear_pointer(&wl_container->surface, wl_surface_destroy);
341 g_clear_pointer(&wl_container->viewport, wp_viewport_destroy);
342
343 wl_container->ready_to_draw = false;
344 wl_container->buffer_scale = 1;
345 }
346
moz_container_wayland_map_event(GtkWidget * widget,GdkEventAny * event)347 static gboolean moz_container_wayland_map_event(GtkWidget* widget,
348 GdkEventAny* event) {
349 MozContainerWayland* wl_container = &MOZ_CONTAINER(widget)->wl_container;
350 MutexAutoLock lock(*wl_container->container_lock);
351
352 LOGWAYLAND("%s [%p]\n", __FUNCTION__,
353 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)));
354
355 // Don't create wl_subsurface in map_event when it's already created or
356 // if we create it for the first time.
357 if (wl_container->ready_to_draw || wl_container->before_first_size_alloc) {
358 return FALSE;
359 }
360
361 if (!wl_container->surface) {
362 if (!moz_container_wayland_surface_create_locked(lock,
363 MOZ_CONTAINER(widget))) {
364 return FALSE;
365 }
366 }
367
368 moz_container_wayland_set_scale_factor_locked(MOZ_CONTAINER(widget));
369 moz_container_wayland_set_opaque_region_locked(lock, MOZ_CONTAINER(widget));
370 moz_container_clear_input_region(MOZ_CONTAINER(widget));
371 moz_container_wayland_invalidate(MOZ_CONTAINER(widget));
372 return FALSE;
373 }
374
moz_container_wayland_map(GtkWidget * widget)375 void moz_container_wayland_map(GtkWidget* widget) {
376 LOGWAYLAND("%s [%p]\n", __FUNCTION__,
377 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)));
378
379 g_return_if_fail(IS_MOZ_CONTAINER(widget));
380 gtk_widget_set_mapped(widget, TRUE);
381
382 if (gtk_widget_get_has_window(widget)) {
383 gdk_window_show(gtk_widget_get_window(widget));
384 }
385 }
386
moz_container_wayland_unmap(GtkWidget * widget)387 void moz_container_wayland_unmap(GtkWidget* widget) {
388 LOGWAYLAND("%s [%p]\n", __FUNCTION__,
389 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)));
390
391 g_return_if_fail(IS_MOZ_CONTAINER(widget));
392 gtk_widget_set_mapped(widget, FALSE);
393
394 if (gtk_widget_get_has_window(widget)) {
395 gdk_window_hide(gtk_widget_get_window(widget));
396 moz_container_wayland_unmap_internal(MOZ_CONTAINER(widget));
397 }
398 }
399
moz_container_wayland_size_allocate(GtkWidget * widget,GtkAllocation * allocation)400 void moz_container_wayland_size_allocate(GtkWidget* widget,
401 GtkAllocation* allocation) {
402 MozContainer* container;
403 GtkAllocation tmp_allocation;
404
405 g_return_if_fail(IS_MOZ_CONTAINER(widget));
406
407 LOGWAYLAND("moz_container_wayland_size_allocate [%p] %d,%d -> %d x %d\n",
408 (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)),
409 allocation->x, allocation->y, allocation->width,
410 allocation->height);
411
412 /* short circuit if you can */
413 container = MOZ_CONTAINER(widget);
414 gtk_widget_get_allocation(widget, &tmp_allocation);
415 if (!container->children && tmp_allocation.x == allocation->x &&
416 tmp_allocation.y == allocation->y &&
417 tmp_allocation.width == allocation->width &&
418 tmp_allocation.height == allocation->height) {
419 return;
420 }
421 gtk_widget_set_allocation(widget, allocation);
422
423 if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) {
424 gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
425 allocation->y, allocation->width,
426 allocation->height);
427 // We need to position our subsurface according to GdkWindow
428 // when offset changes (GdkWindow is maximized for instance).
429 // see gtk-clutter-embed.c for reference.
430 MutexAutoLock lock(*container->wl_container.container_lock);
431 if (!container->wl_container.surface) {
432 if (!moz_container_wayland_surface_create_locked(lock, container)) {
433 return;
434 }
435 }
436 moz_container_wayland_set_scale_factor_locked(container);
437 moz_container_wayland_set_opaque_region_locked(lock, container);
438 moz_container_wayland_move_locked(lock, container, allocation->x,
439 allocation->y);
440 moz_container_clear_input_region(container);
441 moz_container_wayland_invalidate(MOZ_CONTAINER(widget));
442 container->wl_container.before_first_size_alloc = false;
443 }
444 }
445
moz_container_wayland_create_opaque_region(int aX,int aY,int aWidth,int aHeight,int aCornerRadius)446 static wl_region* moz_container_wayland_create_opaque_region(
447 int aX, int aY, int aWidth, int aHeight, int aCornerRadius) {
448 struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor();
449 wl_region* region = wl_compositor_create_region(compositor);
450 wl_region_add(region, aX, aY, aWidth, aHeight);
451 if (aCornerRadius) {
452 wl_region_subtract(region, aX, aY, aCornerRadius, aCornerRadius);
453 wl_region_subtract(region, aX + aWidth - aCornerRadius, aY, aCornerRadius,
454 aCornerRadius);
455 }
456 return region;
457 }
458
moz_container_wayland_set_opaque_region_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)459 static void moz_container_wayland_set_opaque_region_locked(
460 const MutexAutoLock& aProofOfLock, MozContainer* container) {
461 MozContainerWayland* wl_container = &container->wl_container;
462
463 if (!wl_container->opaque_region_needs_updates) {
464 return;
465 }
466
467 if (!wl_container->opaque_region_used) {
468 wl_container->opaque_region_needs_updates = false;
469 return;
470 }
471
472 GtkAllocation allocation;
473 gtk_widget_get_allocation(GTK_WIDGET(container), &allocation);
474
475 wl_region* region = moz_container_wayland_create_opaque_region(
476 0, 0, allocation.width, allocation.height,
477 wl_container->opaque_region_corner_radius);
478 wl_surface_set_opaque_region(wl_container->surface, region);
479 wl_region_destroy(region);
480 wl_container->opaque_region_needs_updates = false;
481 }
482
moz_container_wayland_set_opaque_region(MozContainer * container)483 static void moz_container_wayland_set_opaque_region(MozContainer* container) {
484 MozContainerWayland* wl_container = &container->wl_container;
485 MutexAutoLock lock(*wl_container->container_lock);
486 if (wl_container->surface) {
487 moz_container_wayland_set_opaque_region_locked(lock, container);
488 }
489 }
490
moz_container_wayland_set_scale_factor_locked(MozContainer * container)491 void moz_container_wayland_set_scale_factor_locked(MozContainer* container) {
492 if (gfx::gfxVars::UseWebRenderCompositor()) {
493 // the compositor backend handles scaling itself
494 return;
495 }
496
497 MozContainerWayland* wl_container = &container->wl_container;
498 wl_container->container_lock->AssertCurrentThreadOwns();
499
500 nsWindow* window = moz_container_get_nsWindow(container);
501
502 if (window && window->UseFractionalScale()) {
503 if (!wl_container->viewport) {
504 wl_container->viewport = wp_viewporter_get_viewport(
505 WaylandDisplayGet()->GetViewporter(), wl_container->surface);
506 }
507
508 GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(container));
509 wp_viewport_set_destination(wl_container->viewport,
510 gdk_window_get_width(gdkWindow),
511 gdk_window_get_height(gdkWindow));
512 } else {
513 int scale = window ? window->GdkCeiledScaleFactor() : 1;
514
515 if (scale == wl_container->buffer_scale) {
516 return;
517 }
518
519 LOGWAYLAND("%s [%p] scale %d\n", __FUNCTION__,
520 (void*)moz_container_get_nsWindow(container), scale);
521 wl_surface_set_buffer_scale(wl_container->surface, scale);
522 wl_container->buffer_scale = scale;
523 }
524 }
525
moz_container_wayland_set_scale_factor(MozContainer * container)526 void moz_container_wayland_set_scale_factor(MozContainer* container) {
527 MutexAutoLock lock(*container->wl_container.container_lock);
528 if (container->wl_container.surface) {
529 moz_container_wayland_set_scale_factor_locked(container);
530 }
531 }
532
moz_container_wayland_surface_create_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)533 static bool moz_container_wayland_surface_create_locked(
534 const MutexAutoLock& aProofOfLock, MozContainer* container) {
535 MozContainerWayland* wl_container = &container->wl_container;
536
537 LOGWAYLAND("%s [%p]\n", __FUNCTION__,
538 (void*)moz_container_get_nsWindow(container));
539
540 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
541 wl_surface* parent_surface = gdk_wayland_window_get_wl_surface(window);
542 if (!parent_surface) {
543 LOGWAYLAND(" Failed - missing parent surface!");
544 return false;
545 }
546 LOGWAYLAND(" gtk wl_surface %p ID %d\n", (void*)parent_surface,
547 wl_proxy_get_id((struct wl_proxy*)parent_surface));
548
549 if (wl_container->commit_to_parent) {
550 LOGWAYLAND(" commit to parent");
551 wl_container->surface = parent_surface;
552 NS_DispatchToCurrentThread(NewRunnableFunction(
553 "moz_container_wayland_frame_callback_handler",
554 &moz_container_wayland_frame_callback_handler, container, nullptr, 0));
555 return true;
556 }
557
558 // Available as of GTK 3.8+
559 struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor();
560 wl_container->surface = wl_compositor_create_surface(compositor);
561 if (!wl_container->surface) {
562 LOGWAYLAND(" Failed - can't create surface!");
563 return false;
564 }
565
566 wl_container->subsurface =
567 wl_subcompositor_get_subsurface(WaylandDisplayGet()->GetSubcompositor(),
568 wl_container->surface, parent_surface);
569 if (!wl_container->subsurface) {
570 g_clear_pointer(&wl_container->surface, wl_surface_destroy);
571 LOGWAYLAND(" Failed - can't create sub-surface!");
572 return false;
573 }
574 wl_subsurface_set_desync(wl_container->subsurface);
575
576 // Try to guess subsurface offset to avoid potential flickering.
577 int dx, dy;
578 if (moz_container_get_nsWindow(container)->GetCSDDecorationOffset(&dx, &dy)) {
579 wl_container->subsurface_dx = dx;
580 wl_container->subsurface_dy = dy;
581 wl_subsurface_set_position(wl_container->subsurface, dx, dy);
582 LOGWAYLAND(" guessing subsurface position %d %d\n", dx, dy);
583 }
584
585 // If there's pending frame callback it's for wrong parent surface,
586 // so delete it.
587 if (wl_container->frame_callback_handler) {
588 g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy);
589 }
590 wl_container->frame_callback_handler = wl_surface_frame(parent_surface);
591 wl_callback_add_listener(wl_container->frame_callback_handler,
592 &moz_container_frame_listener, container);
593 LOGWAYLAND(
594 " created frame callback ID %d\n",
595 wl_proxy_get_id((struct wl_proxy*)wl_container->frame_callback_handler));
596
597 wl_surface_commit(wl_container->surface);
598 wl_display_flush(WaylandDisplayGet()->GetDisplay());
599
600 wl_container->opaque_region_used =
601 moz_gdk_wayland_window_add_frame_callback_surface_locked(aProofOfLock,
602 container);
603
604 LOGWAYLAND(" created surface %p ID %d\n", (void*)wl_container->surface,
605 wl_proxy_get_id((struct wl_proxy*)wl_container->surface));
606 return true;
607 }
608
moz_container_wayland_surface_lock(MozContainer * container)609 struct wl_surface* moz_container_wayland_surface_lock(MozContainer* container) {
610 // LOGWAYLAND("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__,
611 // (void*)container, (void*)container->wl_container.surface,
612 // container->wl_container.ready_to_draw);
613 if (!container->wl_container.surface ||
614 !container->wl_container.ready_to_draw) {
615 return nullptr;
616 }
617 container->wl_container.container_lock->Lock();
618 return container->wl_container.surface;
619 }
620
moz_container_wayland_surface_unlock(MozContainer * container,struct wl_surface ** surface)621 void moz_container_wayland_surface_unlock(MozContainer* container,
622 struct wl_surface** surface) {
623 // Temporarily disabled to avoid log noise
624 // LOGWAYLAND("%s [%p] surface %p\n", __FUNCTION__, (void*)container,
625 // (void*)container->wl_container.surface);
626 if (*surface) {
627 container->wl_container.container_lock->Unlock();
628 *surface = nullptr;
629 }
630 }
631
moz_container_wayland_get_surface_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)632 struct wl_surface* moz_container_wayland_get_surface_locked(
633 const MutexAutoLock& aProofOfLock, MozContainer* container) {
634 LOGWAYLAND("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__,
635 (void*)moz_container_get_nsWindow(container),
636 (void*)container->wl_container.surface,
637 container->wl_container.ready_to_draw);
638 if (!container->wl_container.surface ||
639 !container->wl_container.ready_to_draw) {
640 return nullptr;
641 }
642 moz_container_wayland_set_scale_factor_locked(container);
643 return container->wl_container.surface;
644 }
645
moz_container_wayland_lock(MozContainer * container)646 void moz_container_wayland_lock(MozContainer* container) {
647 container->wl_container.container_lock->Lock();
648 }
649
moz_container_wayland_unlock(MozContainer * container)650 void moz_container_wayland_unlock(MozContainer* container) {
651 container->wl_container.container_lock->Unlock();
652 }
653
moz_container_wayland_get_egl_window(MozContainer * container,double scale)654 struct wl_egl_window* moz_container_wayland_get_egl_window(
655 MozContainer* container, double scale) {
656 MozContainerWayland* wl_container = &container->wl_container;
657
658 LOGWAYLAND("%s [%p] eglwindow %p\n", __FUNCTION__,
659 (void*)moz_container_get_nsWindow(container),
660 (void*)wl_container->eglwindow);
661
662 MutexAutoLock lock(*wl_container->container_lock);
663 if (!wl_container->surface || !wl_container->ready_to_draw) {
664 return nullptr;
665 }
666 if (!wl_container->eglwindow) {
667 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
668 wl_container->eglwindow = wl_egl_window_create(
669 wl_container->surface, (int)round(gdk_window_get_width(window) * scale),
670 (int)round(gdk_window_get_height(window) * scale));
671
672 LOGWAYLAND("%s [%p] created eglwindow %p\n", __FUNCTION__,
673 (void*)moz_container_get_nsWindow(container),
674 (void*)wl_container->eglwindow);
675 }
676 return wl_container->eglwindow;
677 }
678
moz_container_wayland_has_egl_window(MozContainer * container)679 gboolean moz_container_wayland_has_egl_window(MozContainer* container) {
680 return container->wl_container.eglwindow != nullptr;
681 }
682
moz_container_wayland_update_opaque_region(MozContainer * container,int corner_radius)683 void moz_container_wayland_update_opaque_region(MozContainer* container,
684 int corner_radius) {
685 MozContainerWayland* wl_container = &container->wl_container;
686 wl_container->opaque_region_needs_updates = true;
687 wl_container->opaque_region_corner_radius = corner_radius;
688
689 // When GL compositor / WebRender is used,
690 // moz_container_wayland_get_egl_window() is called only once when window
691 // is created or resized so update opaque region now.
692 if (moz_container_wayland_has_egl_window(container)) {
693 moz_container_wayland_set_opaque_region(container);
694 }
695 }
696
moz_container_wayland_can_draw(MozContainer * container)697 gboolean moz_container_wayland_can_draw(MozContainer* container) {
698 MozContainerWayland* wl_container = &container->wl_container;
699 MutexAutoLock lock(*wl_container->container_lock);
700 return wl_container->ready_to_draw;
701 }
702
moz_container_wayland_get_scale(MozContainer * container)703 double moz_container_wayland_get_scale(MozContainer* container) {
704 nsWindow* window = moz_container_get_nsWindow(container);
705 return window ? window->FractionalScaleFactor() : 1;
706 }
707
moz_container_wayland_set_commit_to_parent(MozContainer * container)708 void moz_container_wayland_set_commit_to_parent(MozContainer* container) {
709 MozContainerWayland* wl_container = &container->wl_container;
710 MOZ_DIAGNOSTIC_ASSERT(!wl_container->surface);
711 wl_container->commit_to_parent = true;
712 }
713
moz_container_wayland_is_commiting_to_parent(MozContainer * container)714 bool moz_container_wayland_is_commiting_to_parent(MozContainer* container) {
715 MozContainerWayland* wl_container = &container->wl_container;
716 return wl_container->commit_to_parent;
717 }
718