Updates for 1.0 API changes in Clutter
[clutter-gtk] / clutter-gtk / gtk-clutter-viewport.c
1 /**
2  * SECTION:gtk-clutter-viewport
3  * @short_description: A scrollable actor
4  *
5  * #GtkClutterViewport is a scrollable actor that can contain a single
6  * #ClutterActor. Using two #GtkAdjustment<!-- -->s it is possible to
7  * control the visible area of the child actor if the size of the viewport
8  * is smaller than the size of the child.
9  *
10  * The #GtkAdjustment<!-- -->s used to control the horizontal and
11  * vertical scrolling can be attached to a #GtkScrollbar subclass,
12  * like #GtkHScrollbar or #GtkVScrollbar.
13  *
14  * The #GtkClutterViewport can be used inside any #ClutterContainer
15  * implementation.
16  *
17  * #GtkClutterViewport is available since Clutter-GTK 1.0
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <cogl/cogl.h>
25
26 #include "gtk-clutter-scrollable.h"
27 #include "gtk-clutter-util.h"
28 #include "gtk-clutter-viewport.h"
29
30 /* XXX - GtkAdjustment accessors have been added with GTK+ 2.14,
31  * but I want Clutter-GTK to be future-proof, so let's do this
32  * little #define dance.
33  */
34 #if !GTK_CHECK_VERSION (2, 14, 0)
35 #define gtk_adjustment_set_page_size(a,v)       ((a)->page_size = (v))
36 #define gtk_adjustment_set_upper(a,v)           ((a)->upper = (v))
37 #define gtk_adjustment_set_page_increment(a,v)  ((a)->page_increment = (v))
38 #define gtk_adjustment_set_step_increment(a,v)  ((a)->step_increment = (v))
39 #define gtk_adjustment_set_lower(a,v)           ((a)->lower = (v))
40
41 #define gtk_adjustment_get_upper(a)             ((a)->upper)
42 #define gtk_adjustment_get_page_size(a)         ((a)->page_size)
43 #endif
44
45 #define GET_PRIVATE(obj)        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_CLUTTER_TYPE_VIEWPORT, GtkClutterViewportPrivate))
46
47 #define I_(str) (g_intern_static_string ((str)))
48
49 static void clutter_container_iface_init      (gpointer g_iface);
50 static void gtk_clutter_scrollable_iface_init (gpointer g_iface);
51
52 G_DEFINE_TYPE_WITH_CODE (GtkClutterViewport,
53                          gtk_clutter_viewport,
54                          CLUTTER_TYPE_ACTOR,
55                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
56                                                 clutter_container_iface_init)
57                          G_IMPLEMENT_INTERFACE (GTK_CLUTTER_TYPE_SCROLLABLE,
58                                                 gtk_clutter_scrollable_iface_init));
59
60 struct _GtkClutterViewportPrivate
61 {
62   ClutterVertex origin;
63
64   ClutterActor *child;
65
66   GtkAdjustment *h_adjustment;
67   GtkAdjustment *v_adjustment;
68 };
69
70 enum
71 {
72   PROP_0,
73
74   PROP_CHILD,
75   PROP_ORIGIN,
76   PROP_H_ADJUSTMENT,
77   PROP_V_ADJUSTMENT
78 };
79
80 static void
81 gtk_clutter_viewport_add (ClutterContainer *container,
82                           ClutterActor     *actor)
83 {
84   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (container)->priv;
85
86   if (priv->child)
87     clutter_actor_unparent (priv->child);
88
89   clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
90   priv->child = actor;
91
92   clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
93
94   g_signal_emit_by_name (container, "actor-added", actor);
95   g_object_notify (G_OBJECT (container), "child");
96 }
97
98 static void
99 gtk_clutter_viewport_remove (ClutterContainer *container,
100                              ClutterActor     *actor)
101 {
102   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (container)->priv;
103
104   if (G_LIKELY (priv->child == actor))
105     {
106       g_object_ref (actor);
107
108       clutter_actor_unparent (actor);
109       priv->child = NULL;
110
111       clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
112
113       g_signal_emit_by_name (container, "actor-removed", actor);
114
115       g_object_unref (actor);
116     }
117 }
118
119 static void
120 gtk_clutter_viewport_foreach (ClutterContainer *container,
121                               ClutterCallback   callback,
122                               gpointer          callback_data)
123 {
124   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (container)->priv;
125
126   if (G_LIKELY (priv->child))
127     callback (priv->child, callback_data);
128 }
129
130 static void
131 clutter_container_iface_init (gpointer g_iface)
132 {
133   ClutterContainerIface *iface = g_iface;
134
135   iface->add = gtk_clutter_viewport_add;
136   iface->remove = gtk_clutter_viewport_remove;
137   iface->foreach = gtk_clutter_viewport_foreach;
138 }
139
140 static void
141 viewport_adjustment_value_changed (GtkAdjustment      *adjustment,
142                                    GtkClutterViewport *viewport)
143 {
144   GtkClutterViewportPrivate *priv = viewport->priv;
145
146   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
147     {
148       GtkAdjustment *h_adjust = priv->h_adjustment;
149       GtkAdjustment *v_adjust = priv->v_adjustment;
150       gfloat new_x, new_y;
151
152       new_x = gtk_adjustment_get_value (h_adjust);
153       new_y = gtk_adjustment_get_value (v_adjust);
154
155       /* change the origin and queue a relayout */
156       if (new_x != priv->origin.x || new_y != priv->origin.y)
157         {
158           priv->origin.x = new_x;
159           priv->origin.y = new_y;
160
161           clutter_actor_queue_relayout (CLUTTER_ACTOR (viewport));
162         }
163     }
164 }
165
166 static gboolean
167 viewport_reclamp_adjustment (GtkAdjustment *adjustment)
168 {
169   gdouble value = gtk_adjustment_get_value (adjustment);
170   gdouble limit;
171
172   limit = gtk_adjustment_get_upper (adjustment)
173         - gtk_adjustment_get_page_size (adjustment);
174
175   value = CLAMP (value, 0, limit);
176   if (value != gtk_adjustment_get_value (adjustment))
177     {
178       gtk_adjustment_set_value (adjustment, value);
179       return TRUE;
180     }
181   else
182     return FALSE;
183 }
184
185 static gboolean
186 viewport_set_hadjustment_values (GtkClutterViewport *viewport,
187                                  gfloat              width)
188 {
189   GtkClutterViewportPrivate *priv = viewport->priv;
190   GtkAdjustment *h_adjust = priv->h_adjustment;
191
192   gtk_adjustment_set_page_size (h_adjust, width);
193   gtk_adjustment_set_step_increment (h_adjust, width * 0.1);
194   gtk_adjustment_set_page_increment (h_adjust, width * 0.9);
195   gtk_adjustment_set_lower (h_adjust, 0);
196
197   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
198     {
199       gfloat natural_width;
200
201       clutter_actor_get_preferred_size (priv->child,
202                                         NULL, NULL,
203                                         &natural_width, NULL);
204
205       gtk_adjustment_set_upper (h_adjust, MAX (natural_width, width));
206     }
207   else
208     gtk_adjustment_set_upper (h_adjust, width);
209
210   return viewport_reclamp_adjustment (h_adjust);
211 }
212
213 static gboolean
214 viewport_set_vadjustment_values (GtkClutterViewport *viewport,
215                                  gfloat              height)
216 {
217   GtkClutterViewportPrivate *priv = viewport->priv;
218   GtkAdjustment *v_adjust = priv->v_adjustment;
219
220   height = clutter_actor_get_height (CLUTTER_ACTOR (viewport));
221
222   gtk_adjustment_set_page_size (v_adjust, height);
223   gtk_adjustment_set_step_increment (v_adjust, height * 0.1);
224   gtk_adjustment_set_page_increment (v_adjust, height * 0.9);
225   gtk_adjustment_set_lower (v_adjust, 0);
226
227   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
228     {
229       gfloat natural_height;
230
231       clutter_actor_get_preferred_size (priv->child,
232                                         NULL, NULL,
233                                         NULL, &natural_height);
234
235       gtk_adjustment_set_upper (v_adjust, MAX (natural_height, height));
236     }
237   else
238     gtk_adjustment_set_upper (v_adjust, height);
239
240   return viewport_reclamp_adjustment (v_adjust);
241 }
242
243 static inline void
244 disconnect_adjustment (GtkClutterViewport *viewport,
245                        GtkOrientation      orientation)
246 {
247   GtkClutterViewportPrivate *priv = viewport->priv;
248   GtkAdjustment **adj_p;
249
250   adj_p = (orientation == GTK_ORIENTATION_HORIZONTAL) ? &priv->h_adjustment
251                                                       : &priv->v_adjustment;
252
253   if (*adj_p)
254     {
255       g_signal_handlers_disconnect_by_func (*adj_p,
256                                             viewport_adjustment_value_changed,
257                                             viewport);
258       g_object_unref (*adj_p);
259       *adj_p = NULL;
260     }
261 }
262
263 static inline void
264 connect_adjustment (GtkClutterViewport *viewport,
265                     GtkOrientation      orientation,
266                     GtkAdjustment      *adjustment)
267 {
268   GtkClutterViewportPrivate *priv = viewport->priv;
269   GtkAdjustment **adj_p;
270   gboolean value_changed = FALSE;
271   gfloat width, height;
272
273   adj_p = (orientation == GTK_ORIENTATION_HORIZONTAL) ? &priv->h_adjustment
274                                                       : &priv->v_adjustment;
275
276   if (adjustment && adjustment == *adj_p)
277     return;
278
279   if (!adjustment)
280     adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 0, 0, 0, 0));
281
282   disconnect_adjustment (viewport, orientation);
283   *adj_p = g_object_ref_sink (adjustment);
284
285   clutter_actor_get_size (CLUTTER_ACTOR (viewport), &width, &height);
286
287   if (orientation == GTK_ORIENTATION_HORIZONTAL)
288     value_changed = viewport_set_hadjustment_values (viewport, width);
289   else
290     value_changed = viewport_set_vadjustment_values (viewport, height);
291
292   g_signal_connect (adjustment, "value-changed",
293                     G_CALLBACK (viewport_adjustment_value_changed),
294                     viewport);
295
296   gtk_adjustment_changed (adjustment);
297
298   if (value_changed)
299     gtk_adjustment_value_changed (adjustment);
300   else
301     viewport_adjustment_value_changed (adjustment, viewport);
302
303   if (orientation == GTK_ORIENTATION_HORIZONTAL)
304     g_object_notify (G_OBJECT (viewport), "hadjustment");
305   else
306     g_object_notify (G_OBJECT (viewport), "vadjustment");
307 }
308
309 static void
310 gtk_clutter_viewport_set_adjustments (GtkClutterScrollable *scrollable,
311                                       GtkAdjustment        *h_adjust,
312                                       GtkAdjustment        *v_adjust)
313 {
314   g_object_freeze_notify (G_OBJECT (scrollable));
315
316   connect_adjustment (GTK_CLUTTER_VIEWPORT (scrollable),
317                       GTK_ORIENTATION_HORIZONTAL,
318                       h_adjust);
319   connect_adjustment (GTK_CLUTTER_VIEWPORT (scrollable),
320                       GTK_ORIENTATION_VERTICAL,
321                       v_adjust);
322
323   g_object_thaw_notify (G_OBJECT (scrollable));
324 }
325
326 static void
327 gtk_clutter_viewport_get_adjustments (GtkClutterScrollable  *scrollable,
328                                       GtkAdjustment        **h_adjust,
329                                       GtkAdjustment        **v_adjust)
330 {
331   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (scrollable)->priv;
332
333   if (h_adjust)
334     *h_adjust = priv->h_adjustment;
335
336   if (v_adjust)
337     *v_adjust = priv->v_adjustment;
338 }
339
340 static void
341 gtk_clutter_scrollable_iface_init (gpointer g_iface)
342 {
343   GtkClutterScrollableIface *iface = g_iface;
344
345   iface->set_adjustments = gtk_clutter_viewport_set_adjustments;
346   iface->get_adjustments = gtk_clutter_viewport_get_adjustments;
347 }
348
349 static void
350 gtk_clutter_viewport_set_property (GObject      *gobject,
351                                    guint         prop_id,
352                                    const GValue *value,
353                                    GParamSpec   *pspec)
354 {
355   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (gobject)->priv;
356
357   switch (prop_id)
358     {
359     case PROP_CHILD:
360       clutter_container_add_actor (CLUTTER_CONTAINER (gobject),
361                                    g_value_get_object (value));
362       break;
363
364     case PROP_ORIGIN:
365       {
366         ClutterVertex *v = g_value_get_boxed (value);
367
368         priv->origin = *v;
369
370         if (CLUTTER_ACTOR_IS_VISIBLE (gobject))
371           clutter_actor_queue_redraw (CLUTTER_ACTOR (gobject));
372       }
373       break;
374
375     case PROP_H_ADJUSTMENT:
376       connect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
377                           GTK_ORIENTATION_HORIZONTAL,
378                           g_value_get_object (value));
379       break;
380
381     case PROP_V_ADJUSTMENT:
382       connect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
383                           GTK_ORIENTATION_VERTICAL,
384                           g_value_get_object (value));
385       break;
386
387     default:
388       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
389       break;
390     }
391 }
392
393 static void
394 gtk_clutter_viewport_get_property (GObject    *gobject,
395                                    guint       prop_id,
396                                    GValue     *value,
397                                    GParamSpec *pspec)
398 {
399   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (gobject)->priv;
400
401   switch (prop_id)
402     {
403     case PROP_CHILD:
404       g_value_set_object (value, priv->child);
405       break;
406
407     case PROP_ORIGIN:
408       g_value_set_boxed (value, &priv->origin);
409       break;
410
411     case PROP_H_ADJUSTMENT:
412       g_value_set_object (value, priv->h_adjustment);
413       break;
414
415     case PROP_V_ADJUSTMENT:
416       g_value_set_object (value, priv->v_adjustment);
417       break;
418
419     default:
420       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
421       break;
422     }
423 }
424
425 static void
426 gtk_clutter_viewport_dispose (GObject *gobject)
427 {
428   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (gobject)->priv;
429
430   if (priv->child)
431     {
432       clutter_actor_destroy (priv->child);
433       priv->child = NULL;
434     }
435
436   disconnect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
437                          GTK_ORIENTATION_HORIZONTAL);
438   disconnect_adjustment (GTK_CLUTTER_VIEWPORT (gobject),
439                          GTK_ORIENTATION_VERTICAL);
440
441   G_OBJECT_CLASS (gtk_clutter_viewport_parent_class)->dispose (gobject);
442 }
443
444 static void
445 gtk_clutter_viewport_get_preferred_width (ClutterActor *actor,
446                                           gfloat        for_height,
447                                           gfloat       *min_width_p,
448                                           gfloat       *natural_width_p)
449 {
450   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (actor)->priv;
451
452   /* we don't have a minimum size */
453   if (min_width_p)
454     *min_width_p = 0;
455
456   /* if we have a child, we want to be as big as the child
457    * wishes to be; otherwise, we don't have a preferred width
458    */
459   if (priv->child)
460     clutter_actor_get_preferred_width (priv->child, for_height,
461                                        NULL,
462                                        natural_width_p);
463   else
464     {
465       if (natural_width_p)
466         *natural_width_p = 0;
467     }
468 }
469
470 static void
471 gtk_clutter_viewport_get_preferred_height (ClutterActor *actor,
472                                            gfloat        for_width,
473                                            gfloat       *min_height_p,
474                                            gfloat       *natural_height_p)
475 {
476   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (actor)->priv;
477
478   /* we don't have a minimum size */
479   if (min_height_p)
480     *min_height_p = 0;
481
482   /* if we have a child, we want to be as big as the child
483    * wishes to be; otherwise, we don't have a preferred height
484    */
485   if (priv->child)
486     clutter_actor_get_preferred_height (priv->child, for_width,
487                                         NULL,
488                                         natural_height_p);
489   else
490     {
491       if (natural_height_p)
492         *natural_height_p = 0;
493     }
494 }
495
496 static void
497 gtk_clutter_viewport_allocate (ClutterActor           *actor,
498                                const ClutterActorBox  *box,
499                                ClutterAllocationFlags  flags)
500 {
501   GtkClutterViewport *viewport = GTK_CLUTTER_VIEWPORT (actor);
502   GtkClutterViewportPrivate *priv = viewport->priv;
503   ClutterActorClass *parent_class;
504   gboolean h_adjustment_value_changed, v_adjustment_value_changed;
505   gfloat width, height;
506
507   parent_class = CLUTTER_ACTOR_CLASS (gtk_clutter_viewport_parent_class);
508   parent_class->allocate (actor, box, flags);
509
510   width  = box->x2 - box->x1;
511   height = box->y2 - box->y1;
512
513   h_adjustment_value_changed =
514     viewport_set_hadjustment_values (viewport, width);
515   v_adjustment_value_changed =
516     viewport_set_vadjustment_values (viewport, height);
517
518   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
519     {
520       ClutterActorBox child_allocation = { 0, };
521       gfloat alloc_width, alloc_height;
522
523       /* a viewport is a boundless actor which can contain a child
524        * without constraints; hence, we give any child exactly the
525        * wanted natural size, no matter how small the viewport
526        * actually is.
527        */
528       clutter_actor_get_preferred_size (priv->child,
529                                         NULL, NULL,
530                                         &alloc_width, &alloc_height);
531
532       child_allocation.x1 = clutter_actor_get_x (priv->child);
533       child_allocation.y1 = clutter_actor_get_y (priv->child);
534       child_allocation.x2 = child_allocation.x1 + alloc_width;
535       child_allocation.y2 = child_allocation.y1 + alloc_height;
536
537       clutter_actor_allocate (priv->child, &child_allocation, flags);
538     }
539
540   gtk_adjustment_changed (priv->h_adjustment);
541   gtk_adjustment_changed (priv->v_adjustment);
542
543   if (h_adjustment_value_changed)
544     gtk_adjustment_value_changed (priv->h_adjustment);
545
546   if (v_adjustment_value_changed)
547     gtk_adjustment_value_changed (priv->v_adjustment);
548 }
549
550 static void
551 gtk_clutter_viewport_paint (ClutterActor *actor)
552 {
553   GtkClutterViewportPrivate *priv = GTK_CLUTTER_VIEWPORT (actor)->priv;
554
555   cogl_push_matrix ();
556
557   /* translate the paint environment by the same amount
558    * defined by the origin value
559    */
560   cogl_translate (priv->origin.x * -1,
561                   priv->origin.y * -1,
562                   priv->origin.z * -1);
563
564   /* the child will be painted in the new frame of reference */
565   if (priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
566     clutter_actor_paint (priv->child);
567
568   cogl_pop_matrix ();
569 }
570
571 static void
572 gtk_clutter_viewport_pick (ClutterActor       *actor,
573                            const ClutterColor *pick_color)
574 {
575   /* chain up to get the default pick */
576   CLUTTER_ACTOR_CLASS (gtk_clutter_viewport_parent_class)->pick (actor, pick_color);
577
578   /* this will cause the child (if any) to be painted in pick mode */
579   gtk_clutter_viewport_paint (actor);
580 }
581
582 static void
583 gtk_clutter_viewport_class_init (GtkClutterViewportClass *klass)
584 {
585   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
586   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
587   GParamSpec *pspec;
588
589   g_type_class_add_private (klass, sizeof (GtkClutterViewportPrivate));
590
591   gobject_class->set_property = gtk_clutter_viewport_set_property;
592   gobject_class->get_property = gtk_clutter_viewport_get_property;
593   gobject_class->dispose = gtk_clutter_viewport_dispose;
594
595   actor_class->get_preferred_width = gtk_clutter_viewport_get_preferred_width;
596   actor_class->get_preferred_height = gtk_clutter_viewport_get_preferred_height;
597   actor_class->allocate = gtk_clutter_viewport_allocate;
598   actor_class->paint = gtk_clutter_viewport_paint;
599   actor_class->pick = gtk_clutter_viewport_pick;
600
601   /**
602    * GtkClutterViewport:child:
603    *
604    * The #ClutterActor inside the viewport.
605    *
606    * Since: 1.0
607    */
608   pspec = g_param_spec_object ("child",
609                                "Child",
610                                "The ClutterActor inside the viewport",
611                                CLUTTER_TYPE_ACTOR,
612                                G_PARAM_READWRITE);
613   g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
614
615   /**
616    * GtkClutterViewport:origin:
617    *
618    * The current origin of the viewport.
619    *
620    * Since: 1.0
621    */
622   pspec = g_param_spec_boxed ("origin",
623                               "Origin",
624                               "The current origin of the viewport",
625                               CLUTTER_TYPE_VERTEX,
626                               G_PARAM_READABLE);
627   g_object_class_install_property (gobject_class, PROP_ORIGIN, pspec);
628
629   /* GtkClutterScrollable properties */
630   g_object_class_override_property (gobject_class, PROP_H_ADJUSTMENT, "hadjustment");
631   g_object_class_override_property (gobject_class, PROP_V_ADJUSTMENT, "vadjustment");
632 }
633
634 static void
635 gtk_clutter_viewport_init (GtkClutterViewport *viewport)
636 {
637   GtkClutterViewportPrivate *priv;
638
639   viewport->priv = priv = GET_PRIVATE (viewport);
640 }
641
642 /**
643  * gtk_clutter_viewport_new:
644  * @h_adjust: horizontal adjustment, or %NULL
645  * @v_adjust: vertical adjustment, or %NULL
646  *
647  * Creates a new #GtkClutterViewport with the given adjustments.
648  *
649  * Return value: the newly created viewport actor
650  *
651  * Since: 1.0
652  */
653 ClutterActor *
654 gtk_clutter_viewport_new (GtkAdjustment *h_adjust,
655                           GtkAdjustment *v_adjust)
656 {
657   return g_object_new (GTK_CLUTTER_TYPE_VIEWPORT,
658                        "hadjustment", h_adjust,
659                        "vadjustment", v_adjust,
660                        NULL);
661 }
662
663 /**
664  * gtk_clutter_viewport_get_origin:
665  * @viewport: a #GtkClutterViewport
666  * @x: return location for the X origin in pixels, or %NULL
667  * @y: return location for the Y origin in pixels, or %NULL
668  * @z: return location for the Z origin in pixels, or %NULL
669  *
670  * Retrieves the current translation factor ("origin") used when
671  * displaying the child of @viewport.
672  *
673  * Since: 1.0.
674  */
675 void
676 gtk_clutter_viewport_get_origin (GtkClutterViewport *viewport,
677                                  gfloat             *x,
678                                  gfloat             *y,
679                                  gfloat             *z)
680 {
681   GtkClutterViewportPrivate *priv;
682
683   g_return_if_fail (GTK_CLUTTER_IS_VIEWPORT (viewport));
684
685   priv = viewport->priv;
686
687   if (x)
688     *x = priv->origin.x;
689
690   if (y)
691     *y = priv->origin.y;
692
693   if (z)
694     *z = priv->origin.z;
695 }