summaryrefslogtreecommitdiffstats
path: root/widget/gtk/MozContainer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gtk/MozContainer.cpp')
-rw-r--r--widget/gtk/MozContainer.cpp402
1 files changed, 402 insertions, 0 deletions
diff --git a/widget/gtk/MozContainer.cpp b/widget/gtk/MozContainer.cpp
new file mode 100644
index 0000000000..95d32b57b4
--- /dev/null
+++ b/widget/gtk/MozContainer.cpp
@@ -0,0 +1,402 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MozContainer.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include "mozilla/WidgetUtilsGtk.h"
+#include "nsWindow.h"
+
+#ifdef MOZ_LOGGING
+# include "mozilla/Logging.h"
+# include "nsTArray.h"
+# include "Units.h"
+extern mozilla::LazyLogModule gWidgetLog;
+# define LOGCONTAINER(args) MOZ_LOG(gWidgetLog, mozilla::LogLevel::Debug, args)
+#else
+# define LOGCONTAINER(args)
+#endif /* MOZ_LOGGING */
+
+/* init methods */
+void moz_container_class_init(MozContainerClass* klass);
+static void moz_container_init(MozContainer* container);
+
+/* widget class methods */
+static void moz_container_map(GtkWidget* widget);
+void moz_container_unmap(GtkWidget* widget);
+static void moz_container_size_allocate(GtkWidget* widget,
+ GtkAllocation* allocation);
+static void moz_container_realize(GtkWidget* widget);
+static void moz_container_unrealize(GtkWidget* widget);
+
+/* container class methods */
+static void moz_container_remove(GtkContainer* container,
+ GtkWidget* child_widget);
+static void moz_container_forall(GtkContainer* container,
+ gboolean include_internals,
+ GtkCallback callback, gpointer callback_data);
+static void moz_container_add(GtkContainer* container, GtkWidget* widget);
+
+typedef struct _MozContainerChild MozContainerChild;
+
+struct _MozContainerChild {
+ GtkWidget* widget;
+ gint x;
+ gint y;
+};
+
+static void moz_container_allocate_child(MozContainer* container,
+ MozContainerChild* child);
+static MozContainerChild* moz_container_get_child(MozContainer* container,
+ GtkWidget* child);
+
+/* public methods */
+
+GType moz_container_get_type(void) {
+ static GType moz_container_type = 0;
+
+ if (!moz_container_type) {
+ static GTypeInfo moz_container_info = {
+ sizeof(MozContainerClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc)moz_container_class_init, /* class_init */
+ NULL, /* class_destroy */
+ NULL, /* class_data */
+ sizeof(MozContainer), /* instance_size */
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)moz_container_init, /* instance_init */
+ NULL, /* value_table */
+ };
+
+ moz_container_type =
+ g_type_register_static(GTK_TYPE_CONTAINER, "MozContainer",
+ &moz_container_info, static_cast<GTypeFlags>(0));
+ }
+
+ return moz_container_type;
+}
+
+GtkWidget* moz_container_new(void) {
+ MozContainer* container;
+
+ container =
+ static_cast<MozContainer*>(g_object_new(MOZ_CONTAINER_TYPE, nullptr));
+
+ return GTK_WIDGET(container);
+}
+
+void moz_container_put(MozContainer* container, GtkWidget* child_widget, gint x,
+ gint y) {
+ MozContainerChild* child;
+
+ child = g_new(MozContainerChild, 1);
+
+ child->widget = child_widget;
+ child->x = x;
+ child->y = y;
+
+ /* printf("moz_container_put %p %p %d %d\n", (void *)container,
+ (void *)child_widget, x, y); */
+
+ container->data.children = g_list_append(container->data.children, child);
+
+ /* we assume that the caller of this function will have already set
+ the parent GdkWindow because we can have many anonymous children. */
+ gtk_widget_set_parent(child_widget, GTK_WIDGET(container));
+}
+
+static void moz_container_destroy(GtkWidget* widget) {
+ auto* container = MOZ_CONTAINER(widget);
+ if (container->destroyed) {
+ return; // The destroy signal may run multiple times.
+ }
+ LOGCONTAINER(("moz_container_destroy() [%p]\n",
+ (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget))));
+ container->destroyed = TRUE;
+ container->data.~Data();
+}
+
+void moz_container_class_init(MozContainerClass* klass) {
+ /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */
+ GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass);
+ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+
+ widget_class->realize = moz_container_realize;
+ widget_class->unrealize = moz_container_unrealize;
+ widget_class->destroy = moz_container_destroy;
+
+#ifdef MOZ_WAYLAND
+ if (mozilla::widget::GdkIsWaylandDisplay()) {
+ widget_class->map = moz_container_wayland_map;
+ widget_class->size_allocate = moz_container_wayland_size_allocate;
+ widget_class->map_event = moz_container_wayland_map_event;
+ widget_class->unmap = moz_container_wayland_unmap;
+ } else {
+#endif
+ widget_class->map = moz_container_map;
+ widget_class->size_allocate = moz_container_size_allocate;
+ widget_class->unmap = moz_container_unmap;
+#ifdef MOZ_WAYLAND
+ }
+#endif
+
+ container_class->remove = moz_container_remove;
+ container_class->forall = moz_container_forall;
+ container_class->add = moz_container_add;
+}
+
+void moz_container_init(MozContainer* container) {
+ container->destroyed = FALSE;
+ new (&container->data) MozContainer::Data();
+ gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
+ gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
+ LOGCONTAINER(("%s [%p]\n", __FUNCTION__,
+ (void*)moz_container_get_nsWindow(container)));
+}
+
+void moz_container_map(GtkWidget* widget) {
+ MozContainer* container;
+ GList* tmp_list;
+ GtkWidget* tmp_child;
+
+ g_return_if_fail(IS_MOZ_CONTAINER(widget));
+ container = MOZ_CONTAINER(widget);
+
+ LOGCONTAINER(("moz_container_map() [%p]",
+ (void*)moz_container_get_nsWindow(container)));
+
+ gtk_widget_set_mapped(widget, TRUE);
+
+ tmp_list = container->data.children;
+ while (tmp_list) {
+ tmp_child = ((MozContainerChild*)tmp_list->data)->widget;
+
+ if (gtk_widget_get_visible(tmp_child)) {
+ if (!gtk_widget_get_mapped(tmp_child)) gtk_widget_map(tmp_child);
+ }
+ tmp_list = tmp_list->next;
+ }
+
+ if (gtk_widget_get_has_window(widget)) {
+ gdk_window_show(gtk_widget_get_window(widget));
+ }
+}
+
+void moz_container_unmap(GtkWidget* widget) {
+ g_return_if_fail(IS_MOZ_CONTAINER(widget));
+
+ LOGCONTAINER(("moz_container_unmap() [%p]",
+ (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget))));
+
+ // Disable rendering to MozContainer before we unmap it.
+ nsWindow* window = moz_container_get_nsWindow(MOZ_CONTAINER(widget));
+ window->DisableRendering();
+
+ gtk_widget_set_mapped(widget, FALSE);
+
+ if (gtk_widget_get_has_window(widget)) {
+ gdk_window_hide(gtk_widget_get_window(widget));
+ }
+}
+
+void moz_container_realize(GtkWidget* widget) {
+ GdkWindow* parent = gtk_widget_get_parent_window(widget);
+ GdkWindow* window;
+
+ gtk_widget_set_realized(widget, TRUE);
+
+ GdkWindowAttr attributes;
+ gint attributes_mask = GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation(widget, &allocation);
+ attributes.event_mask = gtk_widget_get_events(widget);
+ attributes.x = allocation.x;
+ attributes.y = allocation.y;
+ attributes.width = allocation.width;
+ attributes.height = allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ MozContainer* container = MOZ_CONTAINER(widget);
+ attributes.visual =
+ container->data.force_default_visual
+ ? gdk_screen_get_system_visual(gtk_widget_get_screen(widget))
+ : gtk_widget_get_visual(widget);
+
+ window = gdk_window_new(parent, &attributes, attributes_mask);
+
+ LOGCONTAINER(("moz_container_realize() [%p] GdkWindow %p\n",
+ (void*)moz_container_get_nsWindow(container), (void*)window));
+
+ gtk_widget_register_window(widget, window);
+ gtk_widget_set_window(widget, window);
+}
+
+void moz_container_unrealize(GtkWidget* widget) {
+ GdkWindow* window = gtk_widget_get_window(widget);
+ LOGCONTAINER(("moz_container_unrealize() [%p] GdkWindow %p\n",
+ (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)),
+ (void*)window));
+
+ if (gtk_widget_get_mapped(widget)) {
+ gtk_widget_unmap(widget);
+ }
+
+ gtk_widget_unregister_window(widget, window);
+ gtk_widget_set_window(widget, nullptr);
+ gdk_window_destroy(window);
+ gtk_widget_set_realized(widget, false);
+}
+
+void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) {
+ MozContainer* container;
+ GList* tmp_list;
+ GtkAllocation tmp_allocation;
+
+ g_return_if_fail(IS_MOZ_CONTAINER(widget));
+
+ LOGCONTAINER(("moz_container_size_allocate [%p] %d,%d -> %d x %d\n",
+ (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)),
+ allocation->x, allocation->y, allocation->width,
+ allocation->height));
+
+ /* short circuit if you can */
+ container = MOZ_CONTAINER(widget);
+ gtk_widget_get_allocation(widget, &tmp_allocation);
+ if (!container->data.children && tmp_allocation.x == allocation->x &&
+ tmp_allocation.y == allocation->y &&
+ tmp_allocation.width == allocation->width &&
+ tmp_allocation.height == allocation->height) {
+ return;
+ }
+
+ gtk_widget_set_allocation(widget, allocation);
+
+ tmp_list = container->data.children;
+
+ while (tmp_list) {
+ MozContainerChild* child = static_cast<MozContainerChild*>(tmp_list->data);
+
+ moz_container_allocate_child(container, child);
+
+ tmp_list = tmp_list->next;
+ }
+
+ if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) {
+ gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
+ allocation->y, allocation->width,
+ allocation->height);
+ }
+}
+
+void moz_container_remove(GtkContainer* container, GtkWidget* child_widget) {
+ MozContainerChild* child;
+ MozContainer* moz_container;
+ GdkWindow* parent_window;
+
+ g_return_if_fail(IS_MOZ_CONTAINER(container));
+ g_return_if_fail(GTK_IS_WIDGET(child_widget));
+
+ moz_container = MOZ_CONTAINER(container);
+
+ child = moz_container_get_child(moz_container, child_widget);
+ g_return_if_fail(child);
+
+ /* gtk_widget_unparent will remove the parent window (as well as the
+ * parent widget), but, in Mozilla's window hierarchy, the parent window
+ * may need to be kept because it may be part of a GdkWindow sub-hierarchy
+ * that is being moved to another MozContainer.
+ *
+ * (In a conventional GtkWidget hierarchy, GdkWindows being reparented
+ * would have their own GtkWidget and that widget would be the one being
+ * reparented. In Mozilla's hierarchy, the parent_window needs to be
+ * retained so that the GdkWindow sub-hierarchy is maintained.)
+ */
+ parent_window = gtk_widget_get_parent_window(child_widget);
+ if (parent_window) g_object_ref(parent_window);
+
+ gtk_widget_unparent(child_widget);
+
+ if (parent_window) {
+ /* The child_widget will always still exist because g_signal_emit,
+ * which invokes this function, holds a reference.
+ *
+ * If parent_window is the container's root window then it will not be
+ * the parent_window if the child_widget is placed in another
+ * container.
+ */
+ if (parent_window != gtk_widget_get_window(GTK_WIDGET(container))) {
+ gtk_widget_set_parent_window(child_widget, parent_window);
+ }
+
+ g_object_unref(parent_window);
+ }
+
+ moz_container->data.children =
+ g_list_remove(moz_container->data.children, child);
+ g_free(child);
+}
+
+void moz_container_forall(GtkContainer* container, gboolean include_internals,
+ GtkCallback callback, gpointer callback_data) {
+ g_return_if_fail(IS_MOZ_CONTAINER(container));
+ g_return_if_fail(callback);
+
+ MozContainer* moz_container = MOZ_CONTAINER(container);
+
+ GList* tmp_list = moz_container->data.children;
+ while (tmp_list) {
+ MozContainerChild* child;
+ child = static_cast<MozContainerChild*>(tmp_list->data);
+ tmp_list = tmp_list->next;
+ (*callback)(child->widget, callback_data);
+ }
+}
+
+static void moz_container_allocate_child(MozContainer* container,
+ MozContainerChild* child) {
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation(child->widget, &allocation);
+ allocation.x = child->x;
+ allocation.y = child->y;
+
+ gtk_widget_size_allocate(child->widget, &allocation);
+}
+
+MozContainerChild* moz_container_get_child(MozContainer* container,
+ GtkWidget* child_widget) {
+ GList* tmp_list = container->data.children;
+ while (tmp_list) {
+ MozContainerChild* child;
+
+ child = static_cast<MozContainerChild*>(tmp_list->data);
+ tmp_list = tmp_list->next;
+
+ if (child->widget == child_widget) return child;
+ }
+ return nullptr;
+}
+
+static void moz_container_add(GtkContainer* container, GtkWidget* widget) {
+ moz_container_put(MOZ_CONTAINER(container), widget, 0, 0);
+}
+
+void moz_container_force_default_visual(MozContainer* container) {
+ container->data.force_default_visual = true;
+}
+
+nsWindow* moz_container_get_nsWindow(MozContainer* container) {
+ gpointer user_data = g_object_get_data(G_OBJECT(container), "nsWindow");
+ return static_cast<nsWindow*>(user_data);
+}
+
+#undef LOGCONTAINER