1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
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 #include "mozcontainer.h"
9 #include <gtk/gtk.h>
10 #ifdef MOZ_WAYLAND
11 #include <gdk/gdkx.h>
12 #include <gdk/gdkwayland.h>
13 #endif
14 #include <stdio.h>
15 #include <dlfcn.h>
16
17 #ifdef ACCESSIBILITY
18 #include <atk/atk.h>
19 #include "maiRedundantObjectFactory.h"
20 #endif
21
22 /* init methods */
23 static void moz_container_class_init(MozContainerClass *klass);
24 static void moz_container_init(MozContainer *container);
25
26 /* widget class methods */
27 static void moz_container_map(GtkWidget *widget);
28 static void moz_container_unmap(GtkWidget *widget);
29 static void moz_container_realize(GtkWidget *widget);
30 static void moz_container_size_allocate(GtkWidget *widget,
31 GtkAllocation *allocation);
32
33 /* container class methods */
34 static void moz_container_remove(GtkContainer *container,
35 GtkWidget *child_widget);
36 static void moz_container_forall(GtkContainer *container,
37 gboolean include_internals,
38 GtkCallback callback, gpointer callback_data);
39 static void moz_container_add(GtkContainer *container, GtkWidget *widget);
40
41 typedef struct _MozContainerChild MozContainerChild;
42
43 struct _MozContainerChild {
44 GtkWidget *widget;
45 gint x;
46 gint y;
47 };
48
49 static void moz_container_allocate_child(MozContainer *container,
50 MozContainerChild *child);
51 static MozContainerChild *moz_container_get_child(MozContainer *container,
52 GtkWidget *child);
53
54 /* public methods */
55
moz_container_get_type(void)56 GType moz_container_get_type(void) {
57 static GType moz_container_type = 0;
58
59 if (!moz_container_type) {
60 static GTypeInfo moz_container_info = {
61 sizeof(MozContainerClass), /* class_size */
62 NULL, /* base_init */
63 NULL, /* base_finalize */
64 (GClassInitFunc)moz_container_class_init, /* class_init */
65 NULL, /* class_destroy */
66 NULL, /* class_data */
67 sizeof(MozContainer), /* instance_size */
68 0, /* n_preallocs */
69 (GInstanceInitFunc)moz_container_init, /* instance_init */
70 NULL, /* value_table */
71 };
72
73 moz_container_type =
74 g_type_register_static(GTK_TYPE_CONTAINER, "MozContainer",
75 &moz_container_info, static_cast<GTypeFlags>(0));
76 #ifdef ACCESSIBILITY
77 /* Set a factory to return accessible object with ROLE_REDUNDANT for
78 * MozContainer, so that gail won't send focus notification for it */
79 atk_registry_set_factory_type(atk_get_default_registry(),
80 moz_container_type,
81 mai_redundant_object_factory_get_type());
82 #endif
83 }
84
85 return moz_container_type;
86 }
87
moz_container_new(void)88 GtkWidget *moz_container_new(void) {
89 MozContainer *container;
90
91 container =
92 static_cast<MozContainer *>(g_object_new(MOZ_CONTAINER_TYPE, nullptr));
93
94 return GTK_WIDGET(container);
95 }
96
moz_container_put(MozContainer * container,GtkWidget * child_widget,gint x,gint y)97 void moz_container_put(MozContainer *container, GtkWidget *child_widget, gint x,
98 gint y) {
99 MozContainerChild *child;
100
101 child = g_new(MozContainerChild, 1);
102
103 child->widget = child_widget;
104 child->x = x;
105 child->y = y;
106
107 /* printf("moz_container_put %p %p %d %d\n", (void *)container,
108 (void *)child_widget, x, y); */
109
110 container->children = g_list_append(container->children, child);
111
112 /* we assume that the caller of this function will have already set
113 the parent GdkWindow because we can have many anonymous children. */
114 gtk_widget_set_parent(child_widget, GTK_WIDGET(container));
115 }
116
moz_container_move(MozContainer * container,GtkWidget * child_widget,gint x,gint y,gint width,gint height)117 void moz_container_move(MozContainer *container, GtkWidget *child_widget,
118 gint x, gint y, gint width, gint height) {
119 MozContainerChild *child;
120 GtkAllocation new_allocation;
121
122 child = moz_container_get_child(container, child_widget);
123
124 child->x = x;
125 child->y = y;
126
127 new_allocation.x = x;
128 new_allocation.y = y;
129 new_allocation.width = width;
130 new_allocation.height = height;
131
132 /* printf("moz_container_move %p %p will allocate to %d %d %d %d\n",
133 (void *)container, (void *)child_widget,
134 new_allocation.x, new_allocation.y,
135 new_allocation.width, new_allocation.height); */
136
137 gtk_widget_size_allocate(child_widget, &new_allocation);
138 }
139
140 /* static methods */
141
moz_container_class_init(MozContainerClass * klass)142 void moz_container_class_init(MozContainerClass *klass) {
143 /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
144 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */
145 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
146 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
147
148 widget_class->map = moz_container_map;
149 widget_class->unmap = moz_container_unmap;
150 widget_class->realize = moz_container_realize;
151 widget_class->size_allocate = moz_container_size_allocate;
152
153 container_class->remove = moz_container_remove;
154 container_class->forall = moz_container_forall;
155 container_class->add = moz_container_add;
156 }
157
158 #if defined(MOZ_WAYLAND)
registry_handle_global(void * data,struct wl_registry * registry,uint32_t name,const char * interface,uint32_t version)159 static void registry_handle_global(void *data, struct wl_registry *registry,
160 uint32_t name, const char *interface,
161 uint32_t version) {
162 MozContainer *container = MOZ_CONTAINER(data);
163 if (strcmp(interface, "wl_subcompositor") == 0) {
164 container->subcompositor = static_cast<wl_subcompositor *>(
165 wl_registry_bind(registry, name, &wl_subcompositor_interface, 1));
166 }
167 }
168
registry_handle_global_remove(void * data,struct wl_registry * registry,uint32_t name)169 static void registry_handle_global_remove(void *data,
170 struct wl_registry *registry,
171 uint32_t name) {}
172
173 static const struct wl_registry_listener registry_listener = {
174 registry_handle_global, registry_handle_global_remove};
175 #endif
176
moz_container_init(MozContainer * container)177 void moz_container_init(MozContainer *container) {
178 gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
179 gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE);
180 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
181
182 #if defined(MOZ_WAYLAND)
183 {
184 GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container));
185 if (GDK_IS_WAYLAND_DISPLAY(gdk_display)) {
186 // Available as of GTK 3.8+
187 static auto sGdkWaylandDisplayGetWlDisplay =
188 (wl_display * (*)(GdkDisplay *))
189 dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
190
191 wl_display *display = sGdkWaylandDisplayGetWlDisplay(gdk_display);
192 wl_registry *registry = wl_display_get_registry(display);
193 wl_registry_add_listener(registry, ®istry_listener, container);
194 wl_display_dispatch(display);
195 wl_display_roundtrip(display);
196 }
197 }
198 #endif
199 }
200
201 #if defined(MOZ_WAYLAND)
202 /* We want to draw to GdkWindow owned by mContainer from Compositor thread but
203 * Gtk+ can be used in main thread only. So we create wayland wl_surface
204 * and attach it as an overlay to GdkWindow.
205 *
206 * see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c
207 * for reference.
208 */
moz_container_map_surface(MozContainer * container)209 static gboolean moz_container_map_surface(MozContainer *container) {
210 // Available as of GTK 3.8+
211 static auto sGdkWaylandDisplayGetWlCompositor =
212 (wl_compositor * (*)(GdkDisplay *))
213 dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
214 static auto sGdkWaylandWindowGetWlSurface = (wl_surface * (*)(GdkWindow *))
215 dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
216
217 GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
218 if (GDK_IS_X11_DISPLAY(display)) return false;
219
220 if (container->subsurface && container->surface) return true;
221
222 if (!container->surface) {
223 struct wl_compositor *compositor;
224 compositor = sGdkWaylandDisplayGetWlCompositor(display);
225 container->surface = wl_compositor_create_surface(compositor);
226 }
227
228 if (!container->subsurface) {
229 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
230 wl_surface *gtk_surface = sGdkWaylandWindowGetWlSurface(window);
231 if (!gtk_surface) {
232 // We requested the underlying wl_surface too early when container
233 // is not realized yet. We'll try again before first rendering
234 // to mContainer.
235 return false;
236 }
237
238 container->subsurface = wl_subcompositor_get_subsurface(
239 container->subcompositor, container->surface, gtk_surface);
240 gint x, y;
241 gdk_window_get_position(window, &x, &y);
242 wl_subsurface_set_position(container->subsurface, x, y);
243 wl_subsurface_set_desync(container->subsurface);
244
245 // Route input to parent wl_surface owned by Gtk+ so we get input
246 // events from Gtk+.
247 GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
248 wl_compositor *compositor = sGdkWaylandDisplayGetWlCompositor(display);
249 wl_region *region = wl_compositor_create_region(compositor);
250 wl_surface_set_input_region(container->surface, region);
251 wl_region_destroy(region);
252 }
253 return true;
254 }
255
moz_container_unmap_surface(MozContainer * container)256 static void moz_container_unmap_surface(MozContainer *container) {
257 g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
258 g_clear_pointer(&container->surface, wl_surface_destroy);
259 }
260
261 #endif
262
moz_container_map(GtkWidget * widget)263 void moz_container_map(GtkWidget *widget) {
264 MozContainer *container;
265 GList *tmp_list;
266 GtkWidget *tmp_child;
267
268 g_return_if_fail(IS_MOZ_CONTAINER(widget));
269 container = MOZ_CONTAINER(widget);
270
271 gtk_widget_set_mapped(widget, TRUE);
272
273 tmp_list = container->children;
274 while (tmp_list) {
275 tmp_child = ((MozContainerChild *)tmp_list->data)->widget;
276
277 if (gtk_widget_get_visible(tmp_child)) {
278 if (!gtk_widget_get_mapped(tmp_child)) gtk_widget_map(tmp_child);
279 }
280 tmp_list = tmp_list->next;
281 }
282
283 if (gtk_widget_get_has_window(widget)) {
284 gdk_window_show(gtk_widget_get_window(widget));
285 #if defined(MOZ_WAYLAND)
286 moz_container_map_surface(MOZ_CONTAINER(widget));
287 #endif
288 }
289 }
290
moz_container_unmap(GtkWidget * widget)291 void moz_container_unmap(GtkWidget *widget) {
292 g_return_if_fail(IS_MOZ_CONTAINER(widget));
293
294 gtk_widget_set_mapped(widget, FALSE);
295
296 if (gtk_widget_get_has_window(widget)) {
297 gdk_window_hide(gtk_widget_get_window(widget));
298 #if defined(MOZ_WAYLAND)
299 moz_container_unmap_surface(MOZ_CONTAINER(widget));
300 #endif
301 }
302 }
303
moz_container_realize(GtkWidget * widget)304 void moz_container_realize(GtkWidget *widget) {
305 GdkWindow *parent = gtk_widget_get_parent_window(widget);
306 GdkWindow *window;
307
308 gtk_widget_set_realized(widget, TRUE);
309
310 if (gtk_widget_get_has_window(widget)) {
311 GdkWindowAttr attributes;
312 gint attributes_mask = GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y;
313 GtkAllocation allocation;
314
315 gtk_widget_get_allocation(widget, &allocation);
316 attributes.event_mask = gtk_widget_get_events(widget);
317 attributes.x = allocation.x;
318 attributes.y = allocation.y;
319 attributes.width = allocation.width;
320 attributes.height = allocation.height;
321 attributes.wclass = GDK_INPUT_OUTPUT;
322 attributes.visual = gtk_widget_get_visual(widget);
323 attributes.window_type = GDK_WINDOW_CHILD;
324
325 window = gdk_window_new(parent, &attributes, attributes_mask);
326 gdk_window_set_user_data(window, widget);
327 } else {
328 window = parent;
329 g_object_ref(window);
330 }
331
332 gtk_widget_set_window(widget, window);
333 }
334
moz_container_size_allocate(GtkWidget * widget,GtkAllocation * allocation)335 void moz_container_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
336 MozContainer *container;
337 GList *tmp_list;
338 GtkAllocation tmp_allocation;
339
340 g_return_if_fail(IS_MOZ_CONTAINER(widget));
341
342 /* printf("moz_container_size_allocate %p %d %d %d %d\n",
343 (void *)widget,
344 allocation->x,
345 allocation->y,
346 allocation->width,
347 allocation->height); */
348
349 /* short circuit if you can */
350 container = MOZ_CONTAINER(widget);
351 gtk_widget_get_allocation(widget, &tmp_allocation);
352 if (!container->children && tmp_allocation.x == allocation->x &&
353 tmp_allocation.y == allocation->y &&
354 tmp_allocation.width == allocation->width &&
355 tmp_allocation.height == allocation->height) {
356 return;
357 }
358
359 gtk_widget_set_allocation(widget, allocation);
360
361 tmp_list = container->children;
362
363 while (tmp_list) {
364 MozContainerChild *child = static_cast<MozContainerChild *>(tmp_list->data);
365
366 moz_container_allocate_child(container, child);
367
368 tmp_list = tmp_list->next;
369 }
370
371 if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) {
372 gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
373 allocation->y, allocation->width,
374 allocation->height);
375 }
376
377 #if defined(MOZ_WAYLAND)
378 // We need to position our subsurface according to GdkWindow
379 // when offset changes (GdkWindow is maximized for instance).
380 // see gtk-clutter-embed.c for reference.
381 if (container->subsurface) {
382 gint x, y;
383 gdk_window_get_position(gtk_widget_get_window(widget), &x, &y);
384 wl_subsurface_set_position(container->subsurface, x, y);
385 }
386 #endif
387 }
388
moz_container_remove(GtkContainer * container,GtkWidget * child_widget)389 void moz_container_remove(GtkContainer *container, GtkWidget *child_widget) {
390 MozContainerChild *child;
391 MozContainer *moz_container;
392 GdkWindow *parent_window;
393
394 g_return_if_fail(IS_MOZ_CONTAINER(container));
395 g_return_if_fail(GTK_IS_WIDGET(child_widget));
396
397 moz_container = MOZ_CONTAINER(container);
398
399 child = moz_container_get_child(moz_container, child_widget);
400 g_return_if_fail(child);
401
402 /* gtk_widget_unparent will remove the parent window (as well as the
403 * parent widget), but, in Mozilla's window hierarchy, the parent window
404 * may need to be kept because it may be part of a GdkWindow sub-hierarchy
405 * that is being moved to another MozContainer.
406 *
407 * (In a conventional GtkWidget hierarchy, GdkWindows being reparented
408 * would have their own GtkWidget and that widget would be the one being
409 * reparented. In Mozilla's hierarchy, the parent_window needs to be
410 * retained so that the GdkWindow sub-hierarchy is maintained.)
411 */
412 parent_window = gtk_widget_get_parent_window(child_widget);
413 if (parent_window) g_object_ref(parent_window);
414
415 gtk_widget_unparent(child_widget);
416
417 if (parent_window) {
418 /* The child_widget will always still exist because g_signal_emit,
419 * which invokes this function, holds a reference.
420 *
421 * If parent_window is the container's root window then it will not be
422 * the parent_window if the child_widget is placed in another
423 * container.
424 */
425 if (parent_window != gtk_widget_get_window(GTK_WIDGET(container)))
426 gtk_widget_set_parent_window(child_widget, parent_window);
427
428 g_object_unref(parent_window);
429 }
430
431 moz_container->children = g_list_remove(moz_container->children, child);
432 g_free(child);
433 }
434
moz_container_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)435 void moz_container_forall(GtkContainer *container, gboolean include_internals,
436 GtkCallback callback, gpointer callback_data) {
437 MozContainer *moz_container;
438 GList *tmp_list;
439
440 g_return_if_fail(IS_MOZ_CONTAINER(container));
441 g_return_if_fail(callback != NULL);
442
443 moz_container = MOZ_CONTAINER(container);
444
445 tmp_list = moz_container->children;
446 while (tmp_list) {
447 MozContainerChild *child;
448 child = static_cast<MozContainerChild *>(tmp_list->data);
449 tmp_list = tmp_list->next;
450 (*callback)(child->widget, callback_data);
451 }
452 }
453
moz_container_allocate_child(MozContainer * container,MozContainerChild * child)454 static void moz_container_allocate_child(MozContainer *container,
455 MozContainerChild *child) {
456 GtkAllocation allocation;
457
458 gtk_widget_get_allocation(child->widget, &allocation);
459 allocation.x = child->x;
460 allocation.y = child->y;
461
462 gtk_widget_size_allocate(child->widget, &allocation);
463 }
464
moz_container_get_child(MozContainer * container,GtkWidget * child_widget)465 MozContainerChild *moz_container_get_child(MozContainer *container,
466 GtkWidget *child_widget) {
467 GList *tmp_list;
468
469 tmp_list = container->children;
470 while (tmp_list) {
471 MozContainerChild *child;
472
473 child = static_cast<MozContainerChild *>(tmp_list->data);
474 tmp_list = tmp_list->next;
475
476 if (child->widget == child_widget) return child;
477 }
478
479 return NULL;
480 }
481
moz_container_add(GtkContainer * container,GtkWidget * widget)482 static void moz_container_add(GtkContainer *container, GtkWidget *widget) {
483 moz_container_put(MOZ_CONTAINER(container), widget, 0, 0);
484 }
485
486 #ifdef MOZ_WAYLAND
moz_container_get_wl_surface(MozContainer * container)487 struct wl_surface *moz_container_get_wl_surface(MozContainer *container) {
488 if (!container->subsurface || !container->surface) {
489 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
490 if (!gdk_window_is_visible(window)) return nullptr;
491
492 moz_container_map_surface(container);
493 }
494
495 return container->surface;
496 }
497 #endif
498