/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Intel code. * * The Initial Developer of the Original Code is Chris Lord * . Portions created by the Initial Developer * are Copyright (C) 2008 Intel. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "moz-drawing-area.h" #include "moz-drawing-area-marshal.h" #include #include #ifdef MOZ_ENABLE_GTK2_PLUGINS #include #endif #ifdef MOZ_X11 #include "cairo-xlib.h" #endif G_DEFINE_TYPE (MozDrawingArea, moz_drawing_area, G_TYPE_OBJECT) #define DRAWING_AREA_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MOZ_TYPE_DRAWING_AREA, \ MozDrawingAreaPrivate)) struct _MozDrawingAreaPrivate { MozDrawingArea *parent; gulong surface_notify; gpointer surface_target; gint surface_width; gint surface_height; gint surface_stride; #ifdef MOZ_X11 Drawable surface_drawable; Visual *surface_visual; Display *surface_dpy; #endif gint x; gint y; gint z; gint window_z; gint width; gint height; gboolean transparent; gboolean popup; gboolean visible; gboolean is_visible; gboolean frozen; gboolean needs_redraw; gboolean ignore_redraw; GList *children; MozDrawingAreaRect damage; gboolean damaged; gboolean scroll_damaged; gulong idle_redraw; /*gboolean child_bounds_valid; gint last_child_width; gint last_child_height;*/ MozHeadlessCursorType cursor; MozDrawingArea *focus; }; enum { PROP_0, PROP_PARENT, PROP_X, PROP_Y, PROP_Z, PROP_WIDTH, PROP_HEIGHT, PROP_POPUP, PROP_VISIBLE, PROP_SURFACE, PROP_CURSOR }; enum { UPDATED, PRE_EXPOSE, EXPOSE, CHILD_BOUNDS, MOTION, BUTTON_PRESS, BUTTON_RELEASE, KEY_PRESS, KEY_RELEASE, #ifdef SUPPORT_IM IME_COMMIT, IME_PREEDIT_CHANGE, IME_RESET, IME_ENABLE, IME_FOCUS_CHANGE, IME_SET_CURSOR, #endif PLUGIN_ADDED, PLUGIN_UPDATED, PLUGIN_VISIBILITY, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0, }; static void moz_drawing_area_send_expose (MozDrawingArea *area, gboolean popup) { GList *c; MozDrawingAreaPrivate *priv = area->priv; if (priv->popup == popup) { priv->damaged = FALSE; g_signal_emit (area, signals[EXPOSE], 0); } for (c = priv->children; c; c = c->next) { gint x, y, n; const MozDrawingAreaRect *damage; MozDrawingArea *child = (MozDrawingArea *)c->data; if (!moz_drawing_area_get_visible (child)) continue; if (!popup && child->priv->popup) continue; moz_drawing_area_send_expose (child, (priv->popup == popup) ? child->priv->popup : popup); /* We don't know if a child is damaged until all its children are drawn */ if (!moz_drawing_area_is_damaged (child)) continue; /* Add the child's damage to ours */ moz_drawing_area_get_bounds (child, &x, &y, NULL, NULL); damage = moz_drawing_area_get_damage (child); if (priv->damaged) { n = damage->x + x; if (n < priv->damage.x) { priv->damage.width += priv->damage.x - n; priv->damage.x = n; } n = damage->y + y; if (n < priv->damage.y) { priv->damage.height += priv->damage.y - n; priv->damage.y = n; } n = (damage->width + damage->x + x) - priv->damage.x; if (n > priv->damage.width) priv->damage.width = n; n = (damage->height + damage->y + y) - priv->damage.y; if (n > priv->damage.height) priv->damage.height = n; } else { priv->damage = *damage; priv->damage.x += x; priv->damage.y += y; priv->damaged = TRUE; } } return; } static void moz_drawing_area_send_pre_expose (MozDrawingArea *area) { GList *c; MozDrawingAreaPrivate *priv = area->priv; /* Fire off pre-expose signals to invalidate sibling windows */ for (c = priv->children; c; c = c->next) { g_signal_emit (c->data, signals[PRE_EXPOSE], 0); moz_drawing_area_send_pre_expose ((MozDrawingArea *)c->data); } } static gboolean moz_drawing_area_begin_redraw (MozDrawingArea *area) { GList *c; MozDrawingAreaPrivate *priv = area->priv; /*printf ("Pre-redraw\n");*/ /* Delay drawing when updates are frozen */ priv->idle_redraw = 0; if (priv->frozen) { priv->needs_redraw = TRUE; return FALSE; } else priv->needs_redraw = FALSE; /* Add a ref in case any of the signal handlers unref us */ g_object_ref (area); /* Fire off pre-expose signals to invalidate sibling windows */ priv->ignore_redraw = TRUE; moz_drawing_area_send_pre_expose (area); priv->ignore_redraw = FALSE; /* Begin real redraw */ moz_drawing_area_send_expose (area, FALSE); moz_drawing_area_send_expose (area, TRUE); /* Send update signal */ if (priv->damaged && (priv->surface_target #ifdef MOZ_X11 || priv->surface_drawable #endif )) { gint x, y; /* Sanitise the values in priv->damage */ x = MIN (priv->surface_width, MAX (0, priv->damage.x)); y = MIN (priv->surface_height, MAX (0, priv->damage.y)); priv->damage.width -= (priv->damage.x - x); priv->damage.height -= (priv->damage.y - y); priv->damage.x = x; priv->damage.y = y; priv->damage.width = MIN (priv->surface_width - x, priv->damage.width); priv->damage.height = MIN (priv->surface_height - y, priv->damage.height); if ((priv->damage.width > 0) && (priv->damage.height > 0)) { /*printf ("Update: %d, %d, %dx%d\n", priv->damage.x, priv->damage.y, priv->damage.width, priv->damage.height);*/ g_signal_emit (area, signals[UPDATED], 0, &priv->damage); } moz_drawing_area_reset_damage (area); } g_object_unref (area); return FALSE; } void moz_drawing_area_redraw (MozDrawingArea *area, gboolean now) { MozDrawingAreaPrivate *priv; if (!moz_drawing_area_get_is_visible (area)) { return; } area = moz_drawing_area_get_toplevel (area); priv = area->priv; if (priv->ignore_redraw) return; if (!priv->surface_target #ifdef MOZ_X11 && !priv->surface_drawable #endif ) return; if (!now) { if (!priv->idle_redraw) priv->idle_redraw = g_idle_add ((GSourceFunc)moz_drawing_area_begin_redraw, area); return; } if (priv->idle_redraw) { g_source_remove (priv->idle_redraw); priv->idle_redraw = 0; } /* Delay drawing when updates are frozen */ if (priv->frozen) { priv->needs_redraw = TRUE; return; } moz_drawing_area_begin_redraw (area); } static void moz_drawing_area_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MozDrawingArea *area = MOZ_DRAWING_AREA (object); MozDrawingAreaPrivate *priv = area->priv; switch (property_id) { case PROP_PARENT : g_value_set_object (value, priv->parent); break; case PROP_X : g_value_set_int (value, priv->x); break; case PROP_Y : g_value_set_int (value, priv->y); break; case PROP_Z : g_value_set_int (value, priv->z); break; case PROP_WIDTH : g_value_set_int (value, priv->width); break; case PROP_HEIGHT : g_value_set_int (value, priv->height); break; case PROP_POPUP : g_value_set_boolean (value, priv->popup); break; case PROP_VISIBLE : g_value_set_boolean (value, priv->visible); break; case PROP_SURFACE : g_value_set_pointer (value, moz_drawing_area_get_surface (area, NULL, NULL, NULL, NULL, NULL)); case PROP_CURSOR : g_value_set_uint (value, priv->cursor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void moz_drawing_area_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MozDrawingArea *area = MOZ_DRAWING_AREA (object); switch (property_id) { case PROP_PARENT : moz_drawing_area_set_parent (area, g_value_get_object (value)); break; case PROP_X : moz_drawing_area_set_position (area, g_value_get_int (value), area->priv->y); break; case PROP_Y : moz_drawing_area_set_position (area, area->priv->x, g_value_get_int (value)); break; case PROP_Z : moz_drawing_area_set_z (area, g_value_get_int (value)); break; case PROP_WIDTH : moz_drawing_area_set_size (area, g_value_get_int (value), area->priv->height); break; case PROP_HEIGHT : moz_drawing_area_set_size (area, area->priv->width, g_value_get_int (value)); break; case PROP_POPUP : area->priv->popup = g_value_get_boolean (value); break; case PROP_VISIBLE : moz_drawing_area_set_visible (area, g_value_get_boolean (value)); break; case PROP_CURSOR : moz_drawing_area_set_cursor (area, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); return; } } static void moz_drawing_area_dispose (GObject *object) { MozDrawingArea *area = MOZ_DRAWING_AREA (object); MozDrawingAreaPrivate *priv = area->priv; /* Remove weak reference to focused area */ if (priv->focus) { g_object_remove_weak_pointer (G_OBJECT (priv->focus), (gpointer *)(&priv->focus)); priv->focus = NULL; } /* Remove idle redraw source */ if (priv->idle_redraw) { g_source_remove (priv->idle_redraw); priv->idle_redraw = 0; } /* Unparent */ if (priv->parent) moz_drawing_area_set_parent (area, NULL); /* Unparent children */ while (priv->children) moz_drawing_area_set_parent ((MozDrawingArea *)priv->children->data, NULL); G_OBJECT_CLASS (moz_drawing_area_parent_class)->dispose (object); } static void moz_drawing_area_finalize (GObject *object) { MozDrawingAreaPrivate *priv = MOZ_DRAWING_AREA (object)->priv; G_OBJECT_CLASS (moz_drawing_area_parent_class)->finalize (object); } static void moz_drawing_area_class_init (MozDrawingAreaClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MozDrawingAreaPrivate)); object_class->get_property = moz_drawing_area_get_property; object_class->set_property = moz_drawing_area_set_property; object_class->dispose = moz_drawing_area_dispose; object_class->finalize = moz_drawing_area_finalize; g_object_class_install_property (object_class, PROP_PARENT, g_param_spec_object ("parent", "Parent", "Parent drawing area.", MOZ_TYPE_DRAWING_AREA, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_X, g_param_spec_int ("x", "X", "X boundary.", -G_MAXINT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_Y, g_param_spec_int ("y", "Y", "Y boundary.", -G_MAXINT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_Z, g_param_spec_int ("z", "Z", "Depth.", -G_MAXINT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_WIDTH, g_param_spec_int ("width", "Width", "Boundary width.", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_HEIGHT, g_param_spec_int ("height", "Height", "Boundary height.", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_POPUP, g_param_spec_boolean ("popup", "Pop-up", "Is a pop-up window.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_VISIBLE, g_param_spec_boolean ("visible", "Visible", "Surface visibility.", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_SURFACE, g_param_spec_pointer ("surface", "Surface", "Cairo surface.", G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); g_object_class_install_property (object_class, PROP_CURSOR, g_param_spec_uint ("cursor", "Cursor", "Current cursor", 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); signals[UPDATED] = g_signal_new ("updated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, updated), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[PRE_EXPOSE] = g_signal_new ("pre-expose", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, pre_expose), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[EXPOSE] = g_signal_new ("expose", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, expose), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[CHILD_BOUNDS] = g_signal_new ("child-bounds", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, child_bounds), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[MOTION] = g_signal_new ("motion", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, motion), NULL, NULL, mozdrawingarea_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); signals[BUTTON_PRESS] = g_signal_new ("button-press", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, button_press), NULL, NULL, mozdrawingarea_marshal_VOID__INT_INT_INT_INT_UINT, G_TYPE_NONE, 5, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_UINT); signals[BUTTON_RELEASE] = g_signal_new ("button-release", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, button_release), NULL, NULL, mozdrawingarea_marshal_VOID__INT_INT_INT_UINT, G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_UINT); signals[KEY_PRESS] = g_signal_new ("key-press", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, key_press), NULL, NULL, mozdrawingarea_marshal_VOID__UINT_UINT_UINT_UINT, G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); signals[KEY_RELEASE] = g_signal_new ("key-release", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, key_release), NULL, NULL, mozdrawingarea_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); #ifdef SUPPORT_IM signals[IME_COMMIT] = g_signal_new ("ime-commit", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, ime_commit), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[IME_PREEDIT_CHANGE] = g_signal_new ("ime-preedit-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, ime_preedit_changed), NULL, NULL, mozdrawingarea_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); signals[IME_RESET] = g_signal_new ("ime-reset", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, ime_reset), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[IME_ENABLE] = g_signal_new ("ime-enable", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, ime_enable), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[IME_FOCUS_CHANGE] = g_signal_new ("ime-focus-change", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, ime_focus_change), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[IME_SET_CURSOR] = g_signal_new ("ime-set-cursor", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, ime_set_cursor), NULL, NULL, mozdrawingarea_marshal_VOID__INT_INT_INT_INT, G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); #endif #ifdef MOZ_ENABLE_GTK2_PLUGINS signals[PLUGIN_ADDED] = g_signal_new ("plugin-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, plugin_added), NULL, NULL, mozdrawingarea_marshal_VOID__UINT_INT_INT_INT_INT, G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); signals[PLUGIN_UPDATED] = g_signal_new ("plugin-updated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, plugin_updated), NULL, NULL, mozdrawingarea_marshal_VOID__UINT_INT_INT_INT_INT, G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); signals[PLUGIN_VISIBILITY] = g_signal_new ("plugin-visibility", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MozDrawingAreaClass, plugin_visibility), NULL, NULL, mozdrawingarea_marshal_VOID__UINT_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_BOOLEAN); #endif } #ifdef SUPPORT_IM void moz_drawing_area_ime_reset (MozDrawingArea *area) { MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); g_signal_emit (parent, signals[IME_RESET], 0); } void moz_drawing_area_ime_enable (MozDrawingArea *area, gboolean enabled) { MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); g_signal_emit (parent, signals[IME_ENABLE], 0, enabled); } void moz_drawing_area_ime_focus_change (MozDrawingArea *area, gboolean in) { MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); g_signal_emit (parent, signals[IME_FOCUS_CHANGE], 0, in); } void moz_drawing_area_ime_set_cursor (MozDrawingArea *area, gint x, gint y, gint width, gint height) { MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); g_signal_emit (parent, signals[IME_SET_CURSOR], 0, x, y, width, height); } #endif static void moz_drawing_area_init (MozDrawingArea *self) { /*MozDrawingAreaPrivate *priv = */self->priv = DRAWING_AREA_PRIVATE (self); } MozDrawingArea* moz_drawing_area_new (MozDrawingArea *parent, gboolean popup) { return g_object_new (MOZ_TYPE_DRAWING_AREA, "parent", parent, "popup", popup, NULL); } static MozDrawingArea * _moz_drawing_area_get_surface (MozDrawingArea *area, gint *x, gint *y, gint *width, gint *height) { if (x) *x = 0; if (y) *y = 0; while (area->priv->parent) { if (x) *x += area->priv->x; if (y) *y += area->priv->y; area = area->priv->parent; } if (x) *x += area->priv->x; if (y) *y += area->priv->y; if (width) *width = area->priv->surface_width; if (height) *height = area->priv->surface_height; return area; } gpointer moz_drawing_area_get_surface (MozDrawingArea *area, gint *x, gint *y, gint *width, gint *height, gint *stride) { area = _moz_drawing_area_get_surface (area, x, y, width, height); if (stride) *stride = area->priv->surface_stride; return area->priv->surface_target; } #ifdef MOZ_X11 Drawable moz_drawing_area_get_xsurface (MozDrawingArea *area, gint *x, gint *y, gint *width, gint *height, Visual **visual, Display **dpy) { area = _moz_drawing_area_get_surface (area, x, y, width, height); if (visual) *visual = area->priv->surface_visual; if (dpy) *dpy = area->priv->surface_dpy; return area->priv->surface_drawable; } #endif cairo_surface_t * moz_drawing_area_create_cairo (MozDrawingArea *area, gint *x, gint *y) { gpointer target; cairo_surface_t *surface; gint width, height, stride; #ifdef MOZ_X11 Display *dpy; Visual *visual; Drawable drawable; drawable = moz_drawing_area_get_xsurface (area, x, y, &width, &height, &visual, &dpy); if (drawable) { surface = cairo_xlib_surface_create (dpy, drawable, visual, width, height); } else { #endif target = moz_drawing_area_get_surface (area, x, y, &width, &height, &stride); surface = cairo_image_surface_create_for_data (target, moz_drawing_area_get_transparent (area) ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height, stride); #ifdef MOZ_X11 } #endif return surface; } MozDrawingArea * moz_drawing_area_get_parent (MozDrawingArea *area) { MozDrawingAreaPrivate *priv = area->priv; return priv->parent; } MozDrawingArea * moz_drawing_area_get_toplevel (MozDrawingArea *area) { while (area->priv->parent) area = area->priv->parent; return area; } static void moz_drawing_area_parent_notify_surface (MozDrawingArea *parent, GParamSpec *pspec, MozDrawingArea *area) { g_object_notify (G_OBJECT (area), "surface"); } static gint moz_drawing_area_compare_func (const MozDrawingArea *a, const MozDrawingArea *b) { if (b->priv->z == a->priv->z) return a->priv->window_z - b->priv->window_z; else return a->priv->z - b->priv->z; } static void _moz_drawing_area_update_visibility (MozDrawingArea *area, gboolean parent_visible) { GList *c; MozDrawingAreaPrivate *priv = area->priv; if (priv->is_visible == (parent_visible && priv->visible)) return; priv->is_visible = parent_visible; for (c = priv->children; c; c = c->next) _moz_drawing_area_update_visibility ((MozDrawingArea *)c->data, parent_visible); } void moz_drawing_area_set_parent (MozDrawingArea *area, MozDrawingArea *parent) { MozDrawingAreaPrivate *priv = area->priv; if (priv->parent == parent) return; /* Remove any idle redraw handler */ if (priv->idle_redraw) { g_source_remove (priv->idle_redraw); priv->idle_redraw = 0; } /* Disconnect from old parent */ if (priv->parent) { MozDrawingAreaPrivate *old_ppriv = priv->parent->priv; g_signal_handler_disconnect (priv->parent, priv->surface_notify); priv->surface_notify = 0; old_ppriv->children = g_list_remove (old_ppriv->children, area); } /* Connect to new parent */ if (parent) { GList *c; gint window_z = 0; MozDrawingAreaPrivate *ppriv = parent->priv; priv->parent = parent; priv->surface_notify = g_signal_connect (priv->parent, "notify::surface", G_CALLBACK (moz_drawing_area_parent_notify_surface), area); /* Go through the child list and put this surface above the others * that have the same z-index if we're a top-level */ for (c = ppriv->children; c; c = c->next) { MozDrawingAreaPrivate *cpriv = ((MozDrawingArea *)c->data)->priv; if ((cpriv->z == priv->z) && (cpriv->window_z >= window_z)) window_z = cpriv->window_z + 1; } priv->window_z = window_z; ppriv->children = g_list_insert_sorted (ppriv->children, area, (GCompareFunc) moz_drawing_area_compare_func); } else { priv->parent = NULL; } /* Update cached visibility if its changed */ if (parent && (parent->priv->is_visible != priv->is_visible)) _moz_drawing_area_update_visibility (area, parent->priv->is_visible); g_object_notify (G_OBJECT (area), "parent"); moz_drawing_area_redraw (area, FALSE); } /* static void moz_drawing_area_update_child_bounds (MozDrawingArea *area) { gint width, height; MozDrawingAreaPrivate *priv = area->priv; area = moz_drawing_area_get_toplevel (area); if (priv->child_bounds_valid) { priv->child_bounds_valid = FALSE; moz_drawing_area_get_child_bounds (area, &width, &height); if ((width == priv->last_child_width) && (height == priv->last_child_height)) return; } else moz_drawing_area_get_child_bounds (area, &width, &height); priv->child_bounds_valid = TRUE; priv->last_child_width = width; priv->last_child_height = height; g_debug ("Child bounds: %dx%d", width, height); g_signal_emit (area, signals[CHILD_BOUNDS], 0); } */ void moz_drawing_area_set_size (MozDrawingArea *area, gint width, gint height) { GObject *self = G_OBJECT (area); MozDrawingAreaPrivate *priv = area->priv; gboolean notify_width, notify_height; notify_width = notify_height = FALSE; if (priv->width != width) { priv->width = width; notify_width = TRUE; } if (priv->height != height) { priv->height = height; notify_height = TRUE; } g_object_freeze_notify (self); if (notify_width) g_object_notify (self, "width"); if (notify_height) g_object_notify (self, "height"); g_object_thaw_notify (self); /*moz_drawing_area_update_child_bounds (area);*/ } void moz_drawing_area_set_position (MozDrawingArea *area, gint x, gint y) { GObject *self = G_OBJECT (area); MozDrawingAreaPrivate *priv = area->priv; gboolean notify_x, notify_y; notify_x = notify_y = FALSE; if (priv->x != x) { priv->x = x; notify_x = TRUE; } if (priv->y != y) { priv->y = y; notify_y = TRUE; } g_object_freeze_notify (self); if (notify_x) g_object_notify (self, "x"); if (notify_y) g_object_notify (self, "y"); g_object_thaw_notify (self); /*moz_drawing_area_update_child_bounds (area);*/ } void moz_drawing_area_set_bounds (MozDrawingArea *area, gint x, gint y, gint width, gint height) { GObject *self = G_OBJECT (area); MozDrawingAreaPrivate *priv = area->priv; gboolean notify_x, notify_y, notify_width, notify_height; notify_x = notify_y = notify_width = notify_height = FALSE; if (priv->x != x) { priv->x = x; notify_x = TRUE; } if (priv->y != y) { priv->y = y; notify_y = TRUE; } if (priv->width != width) { priv->width = width; notify_width = TRUE; } if (priv->height != height) { priv->height = height; notify_height = TRUE; } g_object_freeze_notify (self); if (notify_x) g_object_notify (self, "x"); if (notify_y) g_object_notify (self, "y"); if (notify_width) g_object_notify (self, "width"); if (notify_height) g_object_notify (self, "height"); /*printf ("\n\nset_bounds: (%p) %d, %d, %dx%d\n\n", (void *)area, x, y, width, height);*/ g_object_thaw_notify (self); /*moz_drawing_area_update_child_bounds (area);*/ } void moz_drawing_area_get_bounds (MozDrawingArea *area, gint *x, gint *y, gint *width, gint *height) { MozDrawingAreaPrivate *priv = area->priv; if (x) *x = priv->x; if (y) *y = priv->y; if (width) *width = priv->width; if (height) *height = priv->height; } static void moz_drawing_area_get_abs_pos (MozDrawingArea *area, gint *x, gint *y) { if (x) *x = 0; if (y) *y = 0; while (area) { if (x) *x += area->priv->x; if (y) *y += area->priv->y; area = area->priv->parent; } } void moz_drawing_area_get_abs_bounds (MozDrawingArea *area, gint *x, gint *y, gint *width, gint *height) { MozDrawingAreaPrivate *priv = area->priv; if (width) *width = priv->width; if (height) *height = priv->height; if (x || y) moz_drawing_area_get_abs_pos (area, x, y); } static void _moz_drawing_area_get_child_bounds (MozDrawingArea *area, gint *x, gint *y, gint *width, gint *height) { GList *c; MozDrawingAreaPrivate *priv = area->priv; *x += priv->x; *y += priv->y; if (priv->parent) { if (width) *width = MAX (*x + priv->width, *width); if (height) *height = MAX (*y + priv->height, *height); } for (c = priv->children; c; c = c->next) { MozDrawingArea *child; gint x_copy = *x; gint y_copy = *y; child = (MozDrawingArea *)c->data; _moz_drawing_area_get_child_bounds (child, x, y, width, height); *x = x_copy; *y = y_copy; } } void moz_drawing_area_get_child_bounds (MozDrawingArea *area, gint *width, gint *height) { gint x, y; MozDrawingAreaPrivate *priv = area->priv; /*if (priv->child_bounds_valid) { if (width) *width = priv->last_child_width; if (height) *height = priv->last_child_height; return; }*/ if (width) *width = 0; if (height) *height = 0; if (width || height) { x = y = 0; _moz_drawing_area_get_child_bounds (area, &x, &y, width, height); } } void moz_drawing_area_set_visible (MozDrawingArea *area, gboolean visible) { MozDrawingAreaPrivate *priv = area->priv; if (priv->visible != visible) { gboolean is_visible; priv->visible = visible; g_object_notify (G_OBJECT (area), "visible"); /* Update cached visibility state */ if (visible && (!priv->parent || priv->parent->priv->is_visible)) is_visible = TRUE; else is_visible = FALSE; if (priv->is_visible != is_visible) _moz_drawing_area_update_visibility (area, is_visible); if (visible && is_visible) moz_drawing_area_redraw (area, FALSE); } } gboolean moz_drawing_area_get_visible (MozDrawingArea *area) { MozDrawingAreaPrivate *priv = area->priv; return priv->visible && !priv->frozen; } gboolean moz_drawing_area_get_is_visible (MozDrawingArea *area) { MozDrawingAreaPrivate *priv = area->priv; return priv->is_visible; } static void _moz_drawing_area_print_tree (MozDrawingArea *area, gint depth) { gint i; GList *c; MozDrawingAreaPrivate *priv = area->priv; for (i = 0; i < depth; i++) printf ("\t"); printf ("Window (%p, %d) (+%d+%d, %dx%d)\n", g_object_get_data (G_OBJECT (area), "nsWindow"), priv->visible, priv->x, priv->y, priv->width, priv->height); for (c = priv->children; c; c = c->next) _moz_drawing_area_print_tree ((MozDrawingArea *)c->data, depth + 1); } void moz_drawing_area_print_tree (MozDrawingArea *area) { _moz_drawing_area_print_tree (area, 1); } void moz_drawing_area_set_z (MozDrawingArea *area, gint z) { MozDrawingAreaPrivate *priv = area->priv; if (priv->z != z) { priv->z = z; if (priv->parent) { MozDrawingAreaPrivate *ppriv = priv->parent->priv; ppriv->children = g_list_sort (ppriv->children, (GCompareFunc) moz_drawing_area_compare_func); /*if (moz_drawing_area_get_is_visible (area)) moz_drawing_area_redraw (area);*/ } g_object_notify (G_OBJECT (area), "z"); } } gint moz_drawing_area_get_z (MozDrawingArea *area) { return area->priv->z; } void moz_drawing_area_set_damage (MozDrawingArea *area, MozDrawingAreaRect *rect) { area->priv->damage = *rect; area->priv->damaged = TRUE; } const MozDrawingAreaRect * moz_drawing_area_get_damage (MozDrawingArea *area) { MozDrawingAreaPrivate *priv = area->priv; /* If we've been scrolled, we can assume the entire area is invalid */ if (priv->scroll_damaged) { priv->damage.x = 0; priv->damage.y = 0; priv->damage.width = priv->width; priv->damage.height = priv->height; priv->damaged = TRUE; } return &priv->damage; } void moz_drawing_area_reset_damage (MozDrawingArea *area) { GList *c; for (c = area->priv->children; c; c = c->next) moz_drawing_area_reset_damage ((MozDrawingArea *)c->data); area->priv->scroll_damaged = FALSE; area->priv->damaged = FALSE; } gboolean moz_drawing_area_is_damaged (MozDrawingArea *area) { return area->priv->damaged || area->priv->scroll_damaged; } MozDrawingArea * moz_drawing_area_get_area_at_point_cb (MozDrawingArea *area, gint *x, gint *y, gboolean *popup, gboolean in_popup) { GList *c; gint old_x, old_y; MozDrawingArea *returnval = NULL; MozDrawingAreaPrivate *priv = area->priv; if (!x || !y) return NULL; if (priv->popup) in_popup = TRUE; old_x = *x; old_y = *y; for (c = g_list_last (priv->children); c; c = c->prev) { MozDrawingArea *child = (MozDrawingArea *)c->data; MozDrawingAreaPrivate *child_priv = child->priv; if (!child_priv->visible) continue; /* Check if we're inside the bounds, but also if we're not a popup, * we allow popup windows in the same place to override us, as they're * meant to be always-on-top over normal windows. */ if ((old_x > child_priv->x) && (old_x < child_priv->x + child_priv->width) && (old_y > child_priv->y) && (old_y < child_priv->y + child_priv->height) && (!returnval || (!(*popup) && (in_popup || child_priv->popup)))) { *x = old_x - child_priv->x; *y = old_y - child_priv->y; if (child_priv->popup) *popup = TRUE; returnval = moz_drawing_area_get_area_at_point_cb (child, x, y, popup, in_popup); /* If we're in a popup, return immediately */ if (*popup) return returnval; } } return returnval ? returnval : area; } MozDrawingArea * moz_drawing_area_get_area_at_point (MozDrawingArea *area, gint *x, gint *y) { gboolean popup = FALSE; return moz_drawing_area_get_area_at_point_cb (area, x, y, &popup, FALSE); } void moz_drawing_area_scroll (MozDrawingArea *area, cairo_rectangle_list_t *dest, gint dx, gint dy) { gint x, y; cairo_t *cr; MozDrawingAreaRect bounds; gboolean dont_scroll = FALSE; MozDrawingAreaPrivate *priv = area->priv; MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); MozDrawingAreaPrivate *ppriv = parent->priv; cairo_surface_t *surface = moz_drawing_area_create_cairo (area, &x, &y); bounds.x = x; bounds.y = y; bounds.width = priv->width; bounds.height = priv->height; cr = cairo_create (surface); cairo_surface_set_device_offset (surface, 0, 0); /* Clip to area boundaries */ cairo_rectangle (cr, x, y, priv->width, priv->height); cairo_clip (cr); /* Clip to destination */ if (dest) { gint i; for (i = 0; i < dest->num_rectangles; i++) { cairo_rectangle (cr, dest->rectangles[i].x, dest->rectangles[i].y, dest->rectangles[i].width, dest->rectangles[i].height); } } cairo_clip (cr); cairo_set_source_surface (cr, surface, dx, dy); /* Need to push/pop group or scrolling upwards won't work */ cairo_push_group (cr); cairo_paint (cr); cairo_pop_group_to_source (cr); cairo_paint (cr); cairo_destroy (cr); cairo_surface_destroy (surface); priv->scroll_damaged = TRUE; } void moz_drawing_area_set_surface (MozDrawingArea *area, gpointer target, gint width, gint height, gint stride) { MozDrawingAreaPrivate *priv = area->priv; if (priv->parent) g_warning ("Setting surface on area with parent"); priv->surface_target = target; priv->surface_width = width; priv->surface_height = height; priv->surface_stride = stride; g_object_notify (G_OBJECT (area), "surface"); } #ifdef MOZ_X11 void moz_drawing_area_set_xsurface (MozDrawingArea *area, Display *dpy, Drawable drawable, Visual *visual, gint width, gint height) { MozDrawingAreaPrivate *priv = area->priv; if (priv->parent) g_warning ("Setting surface on area with parent"); priv->surface_dpy = dpy; priv->surface_drawable = drawable; priv->surface_visual = visual; priv->surface_width = width; priv->surface_height = height; g_object_notify (G_OBJECT (area), "surface"); } #endif void moz_drawing_area_set_transparent (MozDrawingArea *area, gboolean transparent) { MozDrawingAreaPrivate *priv; area = moz_drawing_area_get_toplevel (area); priv = area->priv; if (priv->transparent == transparent) return; priv->transparent = transparent; } gboolean moz_drawing_area_get_transparent (MozDrawingArea *area) { area = moz_drawing_area_get_toplevel (area); return area->priv->transparent; } void moz_drawing_area_freeze_updates (MozDrawingArea *area, gboolean frozen) { gboolean redraw; MozDrawingAreaPrivate *priv; area = moz_drawing_area_get_toplevel (area); priv = area->priv; if (priv->frozen && priv->visible && !frozen && priv->needs_redraw) redraw = TRUE; else redraw = FALSE; priv->frozen = frozen; if (redraw) moz_drawing_area_redraw (area, TRUE); } #ifdef MOZ_ENABLE_GTK2_PLUGINS void moz_drawing_area_add_plugin (MozDrawingArea *area, guint plug_id, gint x, gint y, gint width, gint height) { MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); g_signal_emit (parent, signals[PLUGIN_ADDED], 0, plug_id, x, y, width, height); } void moz_drawing_area_update_plugin_bounds (MozDrawingArea *area, guint plug_id, gint x, gint y, gint width, gint height) { MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); g_signal_emit (parent, signals[PLUGIN_UPDATED], 0, plug_id, x, y, width, height); } void moz_drawing_area_update_plugin_visibility (MozDrawingArea *area, guint plug_id, gboolean visible) { MozDrawingArea *parent = moz_drawing_area_get_toplevel (area); g_signal_emit (parent, signals[PLUGIN_VISIBILITY], 0, plug_id, visible); } #endif void moz_drawing_area_set_cursor (MozDrawingArea *area, MozHeadlessCursorType type) { area = moz_drawing_area_get_toplevel (area); if (area->priv->cursor != type) { area->priv->cursor = type; g_object_notify (G_OBJECT (area), "cursor"); } } MozHeadlessCursorType moz_drawing_area_get_cursor (MozDrawingArea *area) { return area->priv->cursor; } const GList *moz_drawing_area_get_children (MozDrawingArea *area) { return area->priv->children; } gboolean moz_drawing_area_has_visible_children (MozDrawingArea *area) { GList *c; MozDrawingAreaPrivate *priv = area->priv; for (c = priv->children; c; c = c->next) { if (((MozDrawingArea *)c->data)->priv->visible) return TRUE; } return FALSE; } gboolean moz_drawing_area_get_popup (MozDrawingArea *area) { return area->priv->popup; } gboolean moz_drawing_area_get_is_popup (MozDrawingArea *area) { MozDrawingAreaPrivate *priv = area->priv; if (priv->popup) return TRUE; else if (priv->parent) return moz_drawing_area_get_is_popup (priv->parent); else return FALSE; } MozDrawingArea * moz_drawing_area_get_focus (MozDrawingArea *area) { MozDrawingArea *toplevel = moz_drawing_area_get_toplevel (area); MozDrawingAreaPrivate *priv = toplevel->priv; if (priv->focus) return priv->focus; else return toplevel; } void moz_drawing_area_set_focus (MozDrawingArea *area) { MozDrawingArea *toplevel = moz_drawing_area_get_toplevel (area); MozDrawingAreaPrivate *priv = toplevel->priv; if (priv->focus) g_object_remove_weak_pointer (G_OBJECT (priv->focus), (gpointer *)(&priv->focus)); priv->focus = area; if (area) g_object_add_weak_pointer (G_OBJECT (area), (gpointer *)(&priv->focus)); }