diff --git a/js/ui/altTab.js b/js/ui/altTab.js index 46e723dc3..865e1f0eb 100644 --- a/js/ui/altTab.js +++ b/js/ui/altTab.js @@ -695,9 +695,9 @@ AppSwitcher.prototype = { let n = this._arrows.length; let arrow = new St.DrawingArea(); - arrow.connect('redraw', Lang.bind(this, - function (area, texture) { - Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN, + arrow.connect('repaint', Lang.bind(this, + function (area) { + Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN, TRANSPARENT_COLOR, this._curApp == n ? POPUP_ARROW_COLOR : POPUP_UNFOCUSED_ARROW_COLOR); })); diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 7ae0b08f0..fb41bb4ad 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -633,8 +633,8 @@ AppIconMenu.prototype = { this._windowContainerBox.connect('style-changed', Lang.bind(this, this._onStyleChanged)); this._arrow = new St.DrawingArea(); - this._arrow.connect('redraw', Lang.bind(this, function (area, texture) { - Shell.draw_box_pointer(texture, + this._arrow.connect('repaint', Lang.bind(this, function (area) { + Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT, this._borderColor, this._backgroundColor); @@ -884,7 +884,7 @@ AppIconMenu.prototype = { if (themeNode.get_border_color(St.Side.LEFT, color)) { this._borderColor = color; } - this._arrow.emit_redraw(); + this._arrow.queue_repaint(); } }; Signals.addSignalMethods(AppIconMenu.prototype); diff --git a/js/ui/widget.js b/js/ui/widget.js index b7e8ca335..d6fe8fe6d 100644 --- a/js/ui/widget.js +++ b/js/ui/widget.js @@ -9,6 +9,7 @@ const Lang = imports.lang; const Pango = imports.gi.Pango; const Shell = imports.gi.Shell; const Signals = imports.signals; +const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; @@ -117,10 +118,9 @@ ClockWidget.prototype = { // it's the same in both modes height: COLLAPSED_WIDTH }); - this.collapsedActor = new Clutter.CairoTexture({ width: COLLAPSED_WIDTH, - height: COLLAPSED_WIDTH, - surface_width: COLLAPSED_WIDTH, - surface_height: COLLAPSED_WIDTH }); + this.collapsedActor = new St.DrawingArea({ width: COLLAPSED_WIDTH, + height: COLLAPSED_WIDTH }); + this.collapsedActor.connect('repaint', Lang.bind(this, this._repaintClock)); this._update(); }, @@ -139,18 +139,18 @@ ClockWidget.prototype = { }, _update: function() { - let time = new Date(); - let msec_remaining = 60000 - (1000 * time.getSeconds() + - time.getMilliseconds()); + this.currentTime = new Date(); + let msec_remaining = 60000 - (1000 * this.currentTime.getSeconds() + + this.currentTime.getMilliseconds()); if (msec_remaining < 500) { - time.setMinutes(time.getMinutes() + 1); + this.currentTime.setMinutes(time.getMinutes() + 1); msec_remaining += 60000; } if (this.state == STATE_COLLAPSED || this.state == STATE_COLLAPSING) - this._updateCairo(time); + this.collapsedActor.queue_repaint(); else - this._updateText(time); + this._updateText(); if (this.timer) Mainloop.source_remove(this.timer); @@ -160,13 +160,13 @@ ClockWidget.prototype = { _updateText: function(time) { // Translators: This is a time format. - this.actor.set_text(time.toLocaleFormat(_("%H:%M"))); + this.actor.set_text(this.currentTime.toLocaleFormat(_("%H:%M"))); }, - _updateCairo: function(time) { - Shell.draw_clock(this.collapsedActor, - time.getHours() % 12, - time.getMinutes()); + _repaintClock: function(area) { + Shell.draw_clock(area, + this.currentTime.getHours() % 12, + this.currentTime.getMinutes()); } }; diff --git a/src/shell-drawing.c b/src/shell-drawing.c index c0e7632c2..01055c99b 100644 --- a/src/shell-drawing.c +++ b/src/shell-drawing.c @@ -6,7 +6,7 @@ #include void -shell_draw_clock (ClutterCairoTexture *texture, +shell_draw_clock (StDrawingArea *area, int hour, int minute) { @@ -15,15 +15,14 @@ shell_draw_clock (ClutterCairoTexture *texture, double xc, yc, radius, hour_radius, minute_radius; double angle; - clutter_cairo_texture_get_surface_size (texture, &width, &height); + st_drawing_area_get_surface_size (area, &width, &height); xc = (double)width / 2; yc = (double)height / 2; radius = (double)(MIN(width, height)) / 2 - 2; minute_radius = radius - 3; hour_radius = radius / 2; - clutter_cairo_texture_clear (texture); - cr = clutter_cairo_texture_create (texture); + cr = st_drawing_area_get_context (area); cairo_set_line_width (cr, 1.0); /* Outline */ @@ -48,8 +47,6 @@ shell_draw_clock (ClutterCairoTexture *texture, xc + minute_radius * cos (angle), yc + minute_radius * sin (angle)); cairo_stroke (cr); - - cairo_destroy (cr); } /** @@ -117,7 +114,7 @@ shell_fade_app_icon (ClutterTexture *source) } void -shell_draw_box_pointer (ClutterCairoTexture *texture, +shell_draw_box_pointer (StDrawingArea *area, ShellPointerDirection direction, ClutterColor *border_color, ClutterColor *background_color) @@ -125,10 +122,9 @@ shell_draw_box_pointer (ClutterCairoTexture *texture, guint width, height; cairo_t *cr; - clutter_cairo_texture_get_surface_size (texture, &width, &height); + st_drawing_area_get_surface_size (area, &width, &height); - clutter_cairo_texture_clear (texture); - cr = clutter_cairo_texture_create (texture); + cr = st_drawing_area_get_context (area); cairo_set_line_width (cr, 1.0); @@ -169,8 +165,6 @@ shell_draw_box_pointer (ClutterCairoTexture *texture, clutter_cairo_set_source_color (cr, background_color); cairo_fill (cr); - - cairo_destroy (cr); } static void diff --git a/src/shell-drawing.h b/src/shell-drawing.h index f60adf41a..ac6edf90a 100644 --- a/src/shell-drawing.h +++ b/src/shell-drawing.h @@ -4,6 +4,7 @@ #define __SHELL_DRAWING_H__ #include +#include "st.h" G_BEGIN_DECLS @@ -14,12 +15,12 @@ typedef enum { SHELL_POINTER_RIGHT } ShellPointerDirection; -void shell_draw_box_pointer (ClutterCairoTexture *texture, +void shell_draw_box_pointer (StDrawingArea *area, ShellPointerDirection direction, ClutterColor *border_color, ClutterColor *background_color); -void shell_draw_clock (ClutterCairoTexture *texture, +void shell_draw_clock (StDrawingArea *area, int hour, int minute); diff --git a/src/st/st-drawing-area.c b/src/st/st-drawing-area.c index 4391ac13e..720c4003d 100644 --- a/src/st/st-drawing-area.c +++ b/src/st/st-drawing-area.c @@ -6,66 +6,154 @@ * * #StDrawingArea is similar to #ClutterCairoTexture in that * it allows drawing via Cairo; the primary difference is that - * it is dynamically sized. To use, connect to the #StDrawingArea::redraw + * it is dynamically sized. To use, connect to the #StDrawingArea::repaint * signal, and inside the signal handler, call - * clutter_cairo_texture_create() to begin drawing. The - * #StDrawingArea::redraw signal will be emitted by default when the area is + * st_drawing_area_get_context() to get the Cairo context to draw to. The + * #StDrawingArea::repaint signal will be emitted by default when the area is * resized or the CSS style changes; you can use the - * st_drawing_area_emit_redraw() as well. + * st_drawing_area_queue_repaint() as well. */ #include "st-drawing-area.h" #include -G_DEFINE_TYPE(StDrawingArea, st_drawing_area, ST_TYPE_BIN); +/* Cairo stores the data in native byte order as ARGB but Cogl's pixel + formats specify the actual byte order. Therefore we need to use a + different format depending on the architecture */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define PIXEL_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE +#else +#define PIXEL_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE +#endif + +G_DEFINE_TYPE(StDrawingArea, st_drawing_area, ST_TYPE_WIDGET); struct _StDrawingAreaPrivate { - ClutterCairoTexture *texture; + CoglHandle *texture; + CoglHandle *material; + cairo_t *context; + guint needs_repaint : 1; + guint in_repaint : 1; }; /* Signals */ enum { - REDRAW, + REPAINT, LAST_SIGNAL }; static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 }; static void -st_drawing_area_allocate (ClutterActor *self, - const ClutterActorBox *box, - ClutterAllocationFlags flags) +st_drawing_area_dispose (GObject *object) +{ + StDrawingArea *area = ST_DRAWING_AREA (object); + StDrawingAreaPrivate *priv = area->priv; + + if (priv->material != COGL_INVALID_HANDLE) + { + cogl_handle_unref (priv->material); + priv->material = COGL_INVALID_HANDLE; + } + + if (priv->texture != COGL_INVALID_HANDLE) + { + cogl_handle_unref (priv->texture); + priv->texture = COGL_INVALID_HANDLE; + } +} + +static void +st_drawing_area_paint (ClutterActor *self) { - StThemeNode *theme_node; - ClutterActorBox content_box; StDrawingArea *area = ST_DRAWING_AREA (self); - int width = box->x2 - box->x1; - int height = box->y2 - box->y1; + StDrawingAreaPrivate *priv = area->priv; + StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self)); + ClutterActorBox allocation_box; + ClutterActorBox content_box; + int width, height; + CoglColor color; + guint8 paint_opacity; - (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class))->allocate (self, box, flags); + (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class))->paint (self); - theme_node = st_widget_get_theme_node (ST_WIDGET (self)); + clutter_actor_get_allocation_box (self, &allocation_box); + st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); - st_theme_node_get_content_box (theme_node, box, &content_box); + width = (int)(0.5 + content_box.x2 - content_box.x1); + height = (int)(0.5 + content_box.y2 - content_box.y1); + + if (priv->material == COGL_INVALID_HANDLE) + priv->material = cogl_material_new (); + + if (priv->texture != COGL_INVALID_HANDLE && + (width != cogl_texture_get_width (priv->texture) || + height != cogl_texture_get_height (priv->texture))) + { + cogl_handle_unref (priv->texture); + priv->texture = COGL_INVALID_HANDLE; + } if (width > 0 && height > 0) { - clutter_cairo_texture_set_surface_size (area->priv->texture, - content_box.x2 - content_box.x1, - content_box.y2 - content_box.y1); - g_signal_emit (G_OBJECT (self), st_drawing_area_signals[REDRAW], 0, - area->priv->texture); + if (priv->texture == COGL_INVALID_HANDLE) + { + priv->texture = cogl_texture_new_with_size (width, height, + COGL_TEXTURE_NONE, + PIXEL_FORMAT); + priv->needs_repaint = TRUE; + } + + if (priv->needs_repaint) + { + cairo_surface_t *surface; + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + priv->context = cairo_create (surface); + priv->in_repaint = TRUE; + + g_signal_emit ((GObject*)area, st_drawing_area_signals[REPAINT], 0); + + priv->in_repaint = FALSE; + cairo_destroy (priv->context); + priv->context = NULL; + + cogl_texture_set_region (priv->texture, 0, 0, 0, 0, width, height, width, height, + PIXEL_FORMAT, + cairo_image_surface_get_stride (surface), + cairo_image_surface_get_data (surface)); + + cairo_surface_destroy (surface); + } + } + + cogl_material_set_layer (priv->material, 0, priv->texture); + + if (priv->texture) + { + paint_opacity = clutter_actor_get_paint_opacity (self); + cogl_color_set_from_4ub (&color, + paint_opacity, paint_opacity, paint_opacity, paint_opacity); + cogl_material_set_color (priv->material, &color); + + cogl_set_source (priv->material); + cogl_rectangle_with_texture_coords (content_box.x1, content_box.y1, + width, height, + 0.0f, 0.0f, 1.0f, 1.0f); } } static void st_drawing_area_style_changed (StWidget *self) { + StDrawingArea *area = ST_DRAWING_AREA (self); + StDrawingAreaPrivate *priv = area->priv; + (ST_WIDGET_CLASS (st_drawing_area_parent_class))->style_changed (self); - st_drawing_area_emit_redraw (ST_DRAWING_AREA (self)); + priv->needs_repaint = TRUE; } static void @@ -75,17 +163,18 @@ st_drawing_area_class_init (StDrawingAreaClass *klass) ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); StWidgetClass *widget_class = ST_WIDGET_CLASS (klass); - actor_class->allocate = st_drawing_area_allocate; + gobject_class->dispose = st_drawing_area_dispose; + actor_class->paint = st_drawing_area_paint; widget_class->style_changed = st_drawing_area_style_changed; - st_drawing_area_signals[REDRAW] = - g_signal_new ("redraw", + st_drawing_area_signals[REPAINT] = + g_signal_new ("repaint", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (StDrawingAreaClass, redraw), + G_STRUCT_OFFSET (StDrawingAreaClass, repaint), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, CLUTTER_TYPE_CAIRO_TEXTURE); + G_TYPE_NONE, 0); g_type_class_add_private (gobject_class, sizeof (StDrawingAreaPrivate)); } @@ -95,31 +184,74 @@ st_drawing_area_init (StDrawingArea *area) { area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, ST_TYPE_DRAWING_AREA, StDrawingAreaPrivate); - area->priv->texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (1, 1)); - clutter_container_add_actor (CLUTTER_CONTAINER (area), CLUTTER_ACTOR (area->priv->texture)); + area->priv->texture = COGL_INVALID_HANDLE; } /** - * st_drawing_area_get_texture: + * st_drawing_area_queue_repaint: + * @area: the #StDrawingArea * - * Return Value: (transfer none): - */ -ClutterCairoTexture * -st_drawing_area_get_texture (StDrawingArea *area) -{ - return area->priv->texture; -} - -/** - * st_drawing_area_emit_redraw: - * @area: A #StDrawingArea - * - * Immediately emit a redraw signal. Useful if - * some parameters for the area being drawn other - * than the size or style have changed. + * Will cause the actor to emit a ::repaint signal before it is next + * drawn to the scene. Useful if some parameters for the area being + * drawn other than the size or style have changed. Note that + * clutter_actor_queue_redraw() will simply result in the same + * contents being drawn to the scene again. */ void -st_drawing_area_emit_redraw (StDrawingArea *area) +st_drawing_area_queue_repaint (StDrawingArea *area) { - g_signal_emit ((GObject*)area, st_drawing_area_signals[REDRAW], 0, area->priv->texture); + StDrawingAreaPrivate *priv; + + g_return_if_fail (ST_IS_DRAWING_AREA (area)); + + priv = area->priv; + + priv->needs_repaint = TRUE; + clutter_actor_queue_redraw (CLUTTER_ACTOR (area)); +} + +/** + * st_drawing_area_get_context: + * @area: the #StDrawingArea + * + * Gets the Cairo context to paint to. This function must only be called + * from a signal hander for the ::repaint signal. + * + * Return Value: (transfer none): the Cairo context for the paint operation + */ +cairo_t * +st_drawing_area_get_context (StDrawingArea *area) +{ + g_return_val_if_fail (ST_IS_DRAWING_AREA (area), NULL); + g_return_val_if_fail (area->priv->in_repaint, NULL); + + return area->priv->context; +} + +/** + * st_drawing_area_get_surface_size: + * @area: the #StDrawingArea + * @width: (out): location to store the width of the painted area + * @height: (out): location to store the height of the painted area + * + * Gets the size of the cairo surface being painted to, which is equal + * to the size of the content area of the widget. This function must + * only be called from a signal hander for the ::repaint signal. + */ +void +st_drawing_area_get_surface_size (StDrawingArea *area, + guint *width, + guint *height) +{ + StDrawingAreaPrivate *priv; + + g_return_if_fail (ST_IS_DRAWING_AREA (area)); + g_return_if_fail (area->priv->in_repaint); + + priv = area->priv; + + if (width) + *width = cogl_texture_get_width (priv->texture); + if (height) + *height = cogl_texture_get_height (priv->texture); } diff --git a/src/st/st-drawing-area.h b/src/st/st-drawing-area.h index 36083729e..429e749b0 100644 --- a/src/st/st-drawing-area.h +++ b/src/st/st-drawing-area.h @@ -2,7 +2,8 @@ #ifndef __ST_DRAWING_AREA_H__ #define __ST_DRAWING_AREA_H__ -#include "st-bin.h" +#include "st-widget.h" +#include #define ST_TYPE_DRAWING_AREA (st_drawing_area_get_type ()) #define ST_DRAWING_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_DRAWING_AREA, StDrawingArea)) @@ -18,22 +19,24 @@ typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate; struct _StDrawingArea { - StBin parent; + StWidget parent; StDrawingAreaPrivate *priv; }; struct _StDrawingAreaClass { - StBinClass parent_class; + StWidgetClass parent_class; - void (*redraw) (StDrawingArea *area, ClutterCairoTexture *texture); + void (*repaint) (StDrawingArea *area); }; GType st_drawing_area_get_type (void) G_GNUC_CONST; -ClutterCairoTexture *st_drawing_area_get_texture (StDrawingArea *area); - -void st_drawing_area_emit_redraw (StDrawingArea *area); +void st_drawing_area_queue_repaint (StDrawingArea *area); +cairo_t *st_drawing_area_get_context (StDrawingArea *area); +void st_drawing_area_get_surface_size (StDrawingArea *area, + guint *width, + guint *height); #endif /* __ST_DRAWING_AREA_H__ */