1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
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/. */
8 #include "MozContainer.h"
13 #include "nsWaylandDisplay.h"
14 #include "gfxPlatformGtk.h"
15 #include <wayland-egl.h>
22 # include "mozilla/Logging.h"
23 # include "nsTArray.h"
25 extern mozilla::LazyLogModule gWidgetWaylandLog
;
26 # define LOGWAYLAND(args) \
27 MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, args)
29 # define LOGWAYLAND(args)
30 #endif /* MOZ_LOGGING */
32 using namespace mozilla
;
33 using namespace mozilla::widget
;
36 static void moz_container_wayland_destroy(GtkWidget
* widget
);
38 /* widget class methods */
39 static void moz_container_wayland_map(GtkWidget
* widget
);
40 static gboolean
moz_container_wayland_map_event(GtkWidget
* widget
,
42 static void moz_container_wayland_unmap(GtkWidget
* widget
);
43 static void moz_container_wayland_size_allocate(GtkWidget
* widget
,
44 GtkAllocation
* allocation
);
46 // Imlemented in MozContainer.cpp
47 void moz_container_realize(GtkWidget
* widget
);
49 static void moz_container_wayland_move_locked(MozContainer
* container
, int dx
,
51 LOGWAYLAND(("moz_container_wayland_move_locked [%p] %d,%d\n",
52 (void*)container
, dx
, dy
));
54 MozContainerWayland
* wl_container
= &container
->wl_container
;
56 wl_container
->subsurface_dx
= dx
;
57 wl_container
->subsurface_dy
= dy
;
58 wl_container
->surface_position_needs_update
= true;
60 // Wayland subsurface is not created yet.
61 if (!wl_container
->subsurface
) {
65 // wl_subsurface_set_position is actually property of parent surface
66 // which is effective when parent surface is commited.
67 wl_subsurface_set_position(wl_container
->subsurface
,
68 wl_container
->subsurface_dx
,
69 wl_container
->subsurface_dy
);
70 wl_container
->surface_position_needs_update
= false;
72 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
74 GdkRectangle rect
= (GdkRectangle
){0, 0, gdk_window_get_width(window
),
75 gdk_window_get_height(window
)};
76 gdk_window_invalidate_rect(window
, &rect
, false);
80 static void moz_container_wayland_move(MozContainer
* container
, int dx
,
82 MutexAutoLock
lock(*container
->wl_container
.container_lock
);
84 ("moz_container_wayland_move [%p] %d,%d\n", (void*)container
, dx
, dy
));
85 moz_container_wayland_move_locked(container
, dx
, dy
);
88 // This is called from layout/compositor code only with
89 // size equal to GL rendering context. Otherwise there are
90 // rendering artifacts as wl_egl_window size does not match
91 // GL rendering pipeline setup.
92 void moz_container_wayland_egl_window_set_size(MozContainer
* container
,
93 int width
, int height
) {
94 MozContainerWayland
* wl_container
= &container
->wl_container
;
95 MutexAutoLock
lock(*wl_container
->container_lock
);
96 if (wl_container
->eglwindow
) {
97 wl_egl_window_resize(wl_container
->eglwindow
, width
, height
, 0, 0);
101 void moz_container_wayland_class_init(MozContainerClass
* klass
) {
102 /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
103 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */
104 GtkWidgetClass
* widget_class
= GTK_WIDGET_CLASS(klass
);
106 widget_class
->map
= moz_container_wayland_map
;
107 widget_class
->map_event
= moz_container_wayland_map_event
;
108 widget_class
->destroy
= moz_container_wayland_destroy
;
109 widget_class
->unmap
= moz_container_wayland_unmap
;
110 widget_class
->realize
= moz_container_realize
;
111 widget_class
->size_allocate
= moz_container_wayland_size_allocate
;
114 void moz_container_wayland_init(MozContainerWayland
* container
) {
115 container
->surface
= nullptr;
116 container
->subsurface
= nullptr;
117 container
->eglwindow
= nullptr;
118 container
->frame_callback_handler
= nullptr;
119 container
->frame_callback_handler_surface_id
= -1;
120 container
->ready_to_draw
= false;
121 container
->opaque_region_needs_update
= false;
122 container
->opaque_region_subtract_corners
= false;
123 container
->surface_needs_clear
= true;
124 container
->subsurface_dx
= 0;
125 container
->subsurface_dy
= 0;
126 container
->surface_position_needs_update
= 0;
127 container
->initial_draw_cbs
.clear();
128 container
->container_lock
= new mozilla::Mutex("MozContainer lock");
131 static void moz_container_wayland_destroy(GtkWidget
* widget
) {
132 MozContainerWayland
* container
= &MOZ_CONTAINER(widget
)->wl_container
;
133 delete container
->container_lock
;
134 container
->container_lock
= nullptr;
137 void moz_container_wayland_add_initial_draw_callback(
138 MozContainer
* container
, const std::function
<void(void)>& initial_draw_cb
) {
139 container
->wl_container
.initial_draw_cbs
.push_back(initial_draw_cb
);
142 wl_surface
* moz_gtk_widget_get_wl_surface(GtkWidget
* aWidget
) {
143 GdkWindow
* window
= gtk_widget_get_window(aWidget
);
144 wl_surface
* surface
= gdk_wayland_window_get_wl_surface(window
);
146 LOGWAYLAND(("moz_gtk_widget_get_wl_surface [%p] wl_surface %p ID %d\n",
147 (void*)aWidget
, (void*)surface
,
148 surface
? wl_proxy_get_id((struct wl_proxy
*)surface
) : -1));
153 static void moz_container_wayland_frame_callback_handler(
154 void* data
, struct wl_callback
* callback
, uint32_t time
) {
155 MozContainerWayland
* wl_container
= &MOZ_CONTAINER(data
)->wl_container
;
158 ("%s [%p] frame_callback_handler %p ready_to_draw %d (set to true)"
159 " initial_draw callback %zd\n",
160 __FUNCTION__
, (void*)MOZ_CONTAINER(data
),
161 (void*)wl_container
->frame_callback_handler
, wl_container
->ready_to_draw
,
162 wl_container
->initial_draw_cbs
.size()));
164 g_clear_pointer(&wl_container
->frame_callback_handler
, wl_callback_destroy
);
165 wl_container
->frame_callback_handler_surface_id
= -1;
167 if (!wl_container
->ready_to_draw
) {
168 wl_container
->ready_to_draw
= true;
169 for (auto const& cb
: wl_container
->initial_draw_cbs
) {
172 wl_container
->initial_draw_cbs
.clear();
176 static const struct wl_callback_listener moz_container_frame_listener
= {
177 moz_container_wayland_frame_callback_handler
};
179 static void moz_container_wayland_request_parent_frame_callback(
180 MozContainer
* container
) {
181 MozContainerWayland
* wl_container
= &container
->wl_container
;
183 wl_surface
* gtk_container_surface
=
184 moz_gtk_widget_get_wl_surface(GTK_WIDGET(container
));
185 int gtk_container_surface_id
=
186 gtk_container_surface
187 ? wl_proxy_get_id((struct wl_proxy
*)gtk_container_surface
)
191 ("%s [%p] frame_callback_handler %p "
192 "frame_callback_handler_surface_id %d\n",
193 __FUNCTION__
, (void*)container
, wl_container
->frame_callback_handler
,
194 wl_container
->frame_callback_handler_surface_id
));
196 if (wl_container
->frame_callback_handler
&&
197 wl_container
->frame_callback_handler_surface_id
==
198 gtk_container_surface_id
) {
202 // If there's pending frame callback, delete it.
203 if (wl_container
->frame_callback_handler
) {
204 g_clear_pointer(&wl_container
->frame_callback_handler
, wl_callback_destroy
);
207 if (gtk_container_surface
) {
208 wl_container
->frame_callback_handler_surface_id
= gtk_container_surface_id
;
209 wl_container
->frame_callback_handler
=
210 wl_surface_frame(gtk_container_surface
);
211 wl_callback_add_listener(wl_container
->frame_callback_handler
,
212 &moz_container_frame_listener
, container
);
214 wl_container
->frame_callback_handler_surface_id
= -1;
218 static gboolean
moz_container_wayland_map_event(GtkWidget
* widget
,
219 GdkEventAny
* event
) {
220 MozContainerWayland
* wl_container
= &MOZ_CONTAINER(widget
)->wl_container
;
222 LOGWAYLAND(("%s begin [%p] ready_to_draw %d\n", __FUNCTION__
,
223 (void*)MOZ_CONTAINER(widget
), wl_container
->ready_to_draw
));
225 if (wl_container
->ready_to_draw
) {
229 moz_container_wayland_request_parent_frame_callback(MOZ_CONTAINER(widget
));
233 static void moz_container_wayland_unmap_internal(MozContainer
* container
) {
234 MozContainerWayland
* wl_container
= &container
->wl_container
;
235 MutexAutoLock
lock(*wl_container
->container_lock
);
237 g_clear_pointer(&wl_container
->eglwindow
, wl_egl_window_destroy
);
238 g_clear_pointer(&wl_container
->subsurface
, wl_subsurface_destroy
);
239 g_clear_pointer(&wl_container
->surface
, wl_surface_destroy
);
240 g_clear_pointer(&wl_container
->frame_callback_handler
, wl_callback_destroy
);
241 wl_container
->frame_callback_handler_surface_id
= -1;
243 wl_container
->surface_needs_clear
= true;
244 wl_container
->ready_to_draw
= false;
246 LOGWAYLAND(("%s [%p]\n", __FUNCTION__
, (void*)container
));
249 void moz_container_wayland_map(GtkWidget
* widget
) {
250 g_return_if_fail(IS_MOZ_CONTAINER(widget
));
251 gtk_widget_set_mapped(widget
, TRUE
);
253 if (gtk_widget_get_has_window(widget
)) {
254 gdk_window_show(gtk_widget_get_window(widget
));
255 moz_container_wayland_map_event(widget
, nullptr);
259 void moz_container_wayland_unmap(GtkWidget
* widget
) {
260 g_return_if_fail(IS_MOZ_CONTAINER(widget
));
262 gtk_widget_set_mapped(widget
, FALSE
);
264 if (gtk_widget_get_has_window(widget
)) {
265 gdk_window_hide(gtk_widget_get_window(widget
));
266 moz_container_wayland_unmap_internal(MOZ_CONTAINER(widget
));
270 void moz_container_wayland_size_allocate(GtkWidget
* widget
,
271 GtkAllocation
* allocation
) {
272 MozContainer
* container
;
273 GtkAllocation tmp_allocation
;
275 g_return_if_fail(IS_MOZ_CONTAINER(widget
));
277 LOGWAYLAND(("moz_container_wayland_size_allocate [%p] %d,%d -> %d x %d\n",
278 (void*)widget
, allocation
->x
, allocation
->y
, allocation
->width
,
279 allocation
->height
));
281 /* short circuit if you can */
282 container
= MOZ_CONTAINER(widget
);
283 gtk_widget_get_allocation(widget
, &tmp_allocation
);
284 if (!container
->children
&& tmp_allocation
.x
== allocation
->x
&&
285 tmp_allocation
.y
== allocation
->y
&&
286 tmp_allocation
.width
== allocation
->width
&&
287 tmp_allocation
.height
== allocation
->height
) {
291 gtk_widget_set_allocation(widget
, allocation
);
293 if (gtk_widget_get_has_window(widget
) && gtk_widget_get_realized(widget
)) {
294 gdk_window_move_resize(gtk_widget_get_window(widget
), allocation
->x
,
295 allocation
->y
, allocation
->width
,
297 // We need to position our subsurface according to GdkWindow
298 // when offset changes (GdkWindow is maximized for instance).
299 // see gtk-clutter-embed.c for reference.
300 if (gfxPlatformGtk::GetPlatform()->IsWaylandDisplay()) {
301 moz_container_wayland_move(MOZ_CONTAINER(widget
), allocation
->x
,
307 static wl_region
* moz_container_wayland_create_opaque_region(
308 int aX
, int aY
, int aWidth
, int aHeight
, bool aSubtractCorners
) {
309 struct wl_compositor
* compositor
= WaylandDisplayGet()->GetCompositor();
310 wl_region
* region
= wl_compositor_create_region(compositor
);
311 wl_region_add(region
, aX
, aY
, aWidth
, aHeight
);
312 if (aSubtractCorners
) {
313 wl_region_subtract(region
, aX
, aY
, TITLEBAR_SHAPE_MASK_HEIGHT
,
314 TITLEBAR_SHAPE_MASK_HEIGHT
);
315 wl_region_subtract(region
, aX
+ aWidth
- TITLEBAR_SHAPE_MASK_HEIGHT
, aY
,
316 TITLEBAR_SHAPE_MASK_HEIGHT
, TITLEBAR_SHAPE_MASK_HEIGHT
);
321 static void moz_container_wayland_set_opaque_region_locked(
322 MozContainer
* container
) {
323 MozContainerWayland
* wl_container
= &container
->wl_container
;
325 if (!wl_container
->opaque_region_needs_update
|| !wl_container
->surface
) {
329 GtkAllocation allocation
;
330 gtk_widget_get_allocation(GTK_WIDGET(container
), &allocation
);
332 wl_region
* region
= moz_container_wayland_create_opaque_region(
333 0, 0, allocation
.width
, allocation
.height
,
334 wl_container
->opaque_region_subtract_corners
);
335 wl_surface_set_opaque_region(wl_container
->surface
, region
);
336 wl_region_destroy(region
);
338 wl_container
->opaque_region_needs_update
= false;
341 static void moz_container_wayland_set_opaque_region(MozContainer
* container
) {
342 MutexAutoLock
lock(*container
->wl_container
.container_lock
);
343 moz_container_wayland_set_opaque_region_locked(container
);
346 static int moz_gtk_widget_get_scale_factor(MozContainer
* container
) {
347 static auto sGtkWidgetGetScaleFactor
=
348 (gint(*)(GtkWidget
*))dlsym(RTLD_DEFAULT
, "gtk_widget_get_scale_factor");
349 return sGtkWidgetGetScaleFactor
350 ? sGtkWidgetGetScaleFactor(GTK_WIDGET(container
))
354 static void moz_container_wayland_set_scale_factor_locked(
355 MozContainer
* container
) {
356 if (!container
->wl_container
.surface
) {
359 wl_surface_set_buffer_scale(container
->wl_container
.surface
,
360 moz_gtk_widget_get_scale_factor(container
));
363 void moz_container_wayland_set_scale_factor(MozContainer
* container
) {
364 MutexAutoLock
lock(*container
->wl_container
.container_lock
);
365 moz_container_wayland_set_scale_factor_locked(container
);
368 static struct wl_surface
* moz_container_wayland_get_surface_locked(
369 MozContainer
* container
, nsWaylandDisplay
* aWaylandDisplay
) {
370 MozContainerWayland
* wl_container
= &container
->wl_container
;
372 LOGWAYLAND(("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__
,
373 (void*)container
, (void*)wl_container
->surface
,
374 wl_container
->ready_to_draw
));
376 if (!wl_container
->surface
) {
377 if (!wl_container
->ready_to_draw
) {
378 moz_container_wayland_request_parent_frame_callback(container
);
381 wl_surface
* parent_surface
=
382 moz_gtk_widget_get_wl_surface(GTK_WIDGET(container
));
383 if (!parent_surface
) {
387 // Available as of GTK 3.8+
388 struct wl_compositor
* compositor
= aWaylandDisplay
->GetCompositor();
389 wl_container
->surface
= wl_compositor_create_surface(compositor
);
390 if (!wl_container
->surface
) {
394 wl_container
->subsurface
=
395 wl_subcompositor_get_subsurface(aWaylandDisplay
->GetSubcompositor(),
396 wl_container
->surface
, parent_surface
);
397 if (!wl_container
->subsurface
) {
398 g_clear_pointer(&wl_container
->surface
, wl_surface_destroy
);
402 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
404 gdk_window_get_position(window
, &x
, &y
);
405 moz_container_wayland_move_locked(container
, x
, y
);
406 wl_subsurface_set_desync(wl_container
->subsurface
);
408 // Route input to parent wl_surface owned by Gtk+ so we get input
410 wl_region
* region
= wl_compositor_create_region(compositor
);
411 wl_surface_set_input_region(wl_container
->surface
, region
);
412 wl_region_destroy(region
);
414 wl_surface_commit(wl_container
->surface
);
415 wl_display_flush(aWaylandDisplay
->GetDisplay());
417 LOGWAYLAND(("%s [%p] created surface %p\n", __FUNCTION__
, (void*)container
,
418 (void*)wl_container
->surface
));
421 if (wl_container
->surface_position_needs_update
) {
422 moz_container_wayland_move_locked(container
, wl_container
->subsurface_dx
,
423 wl_container
->subsurface_dy
);
426 moz_container_wayland_set_opaque_region_locked(container
);
427 moz_container_wayland_set_scale_factor_locked(container
);
429 return wl_container
->surface
;
432 struct wl_surface
* moz_container_wayland_surface_lock(MozContainer
* container
) {
433 GdkDisplay
* display
= gtk_widget_get_display(GTK_WIDGET(container
));
434 RefPtr
<nsWaylandDisplay
> waylandDisplay
= WaylandDisplayGet(display
);
436 LOGWAYLAND(("%s [%p] surface %p\n", __FUNCTION__
, (void*)container
,
437 (void*)container
->wl_container
.surface
));
439 container
->wl_container
.container_lock
->Lock();
440 struct wl_surface
* surface
=
441 moz_container_wayland_get_surface_locked(container
, waylandDisplay
);
442 if (surface
== nullptr) {
443 container
->wl_container
.container_lock
->Unlock();
448 void moz_container_wayland_surface_unlock(MozContainer
* container
,
449 struct wl_surface
** surface
) {
450 LOGWAYLAND(("%s [%p] surface %p\n", __FUNCTION__
, (void*)container
,
451 (void*)container
->wl_container
.surface
));
453 container
->wl_container
.container_lock
->Unlock();
458 struct wl_egl_window
* moz_container_wayland_get_egl_window(
459 MozContainer
* container
, int scale
) {
460 GdkDisplay
* display
= gtk_widget_get_display(GTK_WIDGET(container
));
461 RefPtr
<nsWaylandDisplay
> waylandDisplay
= WaylandDisplayGet(display
);
462 MozContainerWayland
* wl_container
= &container
->wl_container
;
464 LOGWAYLAND(("%s [%p] eglwindow %p\n", __FUNCTION__
, (void*)container
,
465 (void*)wl_container
->eglwindow
));
467 MutexAutoLock
lock(*wl_container
->container_lock
);
469 // Always call moz_container_get_wl_surface() to ensure underlying
470 // container->surface has correct scale and position.
471 wl_surface
* surface
=
472 moz_container_wayland_get_surface_locked(container
, waylandDisplay
);
476 if (!wl_container
->eglwindow
) {
477 GdkWindow
* window
= gtk_widget_get_window(GTK_WIDGET(container
));
478 wl_container
->eglwindow
=
479 wl_egl_window_create(surface
, gdk_window_get_width(window
) * scale
,
480 gdk_window_get_height(window
) * scale
);
482 LOGWAYLAND(("%s [%p] created eglwindow %p\n", __FUNCTION__
,
483 (void*)container
, (void*)wl_container
->eglwindow
));
486 return wl_container
->eglwindow
;
489 gboolean
moz_container_wayland_has_egl_window(MozContainer
* container
) {
490 return container
->wl_container
.eglwindow
? true : false;
493 gboolean
moz_container_wayland_surface_needs_clear(MozContainer
* container
) {
494 int ret
= container
->wl_container
.surface_needs_clear
;
495 container
->wl_container
.surface_needs_clear
= false;
499 void moz_container_wayland_update_opaque_region(MozContainer
* container
,
500 bool aSubtractCorners
) {
501 MozContainerWayland
* wl_container
= &container
->wl_container
;
502 wl_container
->opaque_region_needs_update
= true;
503 wl_container
->opaque_region_subtract_corners
= aSubtractCorners
;
505 // When GL compositor / WebRender is used,
506 // moz_container_wayland_get_egl_window() is called only once when window
507 // is created or resized so update opaque region now.
508 if (moz_container_wayland_has_egl_window(container
)) {
509 moz_container_wayland_set_opaque_region(container
);
513 gboolean
moz_container_wayland_can_draw(MozContainer
* container
) {
514 return container
? container
->wl_container
.ready_to_draw
: false;