* src/hildon-remote-texture.c * src/hildon-remote-texture.h * src/hildon-remote-textu...
[hildon] / src / hildon-remote-texture.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2008 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-remote-texture
27  * @short_description: Widget representing a Clutter/GLES texture created
28  * from a shared memory area.
29  *
30  * The #HildonRemoteTexture is a GTK+ widget which allows the rendering of
31  * a shared memory area within hildon-desktop. It allows the memory area to
32  * be positioned and scaled, without altering its' contents.
33  */
34
35 #include                                        <X11/X.h>
36 #include                                        <X11/Xatom.h>
37
38 #include                                        "hildon-remote-texture.h"
39 #include                                        "hildon-remote-texture-private.h"
40 #include                                        "hildon-program.h"
41 #include                                        "hildon-window-private.h"
42 #include                                        "hildon-program-private.h"
43
44 G_DEFINE_TYPE (HildonRemoteTexture, hildon_remote_texture, GTK_TYPE_WINDOW);
45
46 static GdkFilterReturn
47 hildon_remote_texture_event_filter (GdkXEvent *xevent,
48                                      GdkEvent *event,
49                                      gpointer data);
50 static void
51 hildon_remote_texture_update_ready (HildonRemoteTexture *self);
52 static void
53 hildon_remote_texture_send_pending_messages (HildonRemoteTexture *self);
54 static void
55 hildon_remote_texture_send_all_messages (HildonRemoteTexture *self);
56 static gboolean
57 hildon_remote_texture_parent_map_event (GtkWidget *parent,
58                                          GdkEvent *event,
59                                          gpointer user_data);
60 static gboolean
61 hildon_remote_texture_map_event (GtkWidget *widget,
62                                   GdkEvent *event,
63                                   gpointer user_data);
64
65 static guint32 shm_atom;
66 static guint32 damage_atom;
67 static guint32 show_atom;
68 static guint32 position_atom;
69 static guint32 offset_atom;
70 static guint32 scale_atom;
71 static guint32 parent_atom;
72 static guint32 ready_atom;
73
74 static gboolean atoms_initialized = FALSE;
75
76 static void
77 hildon_remote_texture_realize                 (GtkWidget *widget)
78 {
79     GdkDisplay *display;
80     Atom wm_type, applet_type;
81
82     GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->realize (widget);
83
84     /* Set remote texture window type. */
85
86     display = gdk_drawable_get_display (widget->window);
87
88     wm_type = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE");
89     applet_type = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_WM_WINDOW_TYPE_REMOTE_TEXTURE");
90
91     XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window), wm_type,
92                      XA_ATOM, 32, PropModeReplace,
93                      (unsigned char *) &applet_type, 1);
94
95     /* This is a bit of a hack, but for the sake of speed (it is assumed that
96      * once HildonRemoteTexture is created, a lot of ClientMessages will
97      * follow), we cache all ClientMessages atoms in static variables. */
98
99     if (!atoms_initialized)
100     {
101         shm_atom =
102             gdk_x11_get_xatom_by_name_for_display
103             (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_SHM");
104         damage_atom =
105             gdk_x11_get_xatom_by_name_for_display
106             (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_DAMAGE");
107         show_atom =
108             gdk_x11_get_xatom_by_name_for_display
109             (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_SHOW");
110         position_atom =
111             gdk_x11_get_xatom_by_name_for_display
112             (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_POSITION");
113         offset_atom =
114             gdk_x11_get_xatom_by_name_for_display
115             (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_OFFSET");
116         scale_atom =
117             gdk_x11_get_xatom_by_name_for_display
118             (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_SCALE");
119         parent_atom =
120             gdk_x11_get_xatom_by_name_for_display
121             (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_PARENT");
122         ready_atom =
123             gdk_x11_get_xatom_by_name_for_display
124             (display, "_HILDON_TEXTURE_CLIENT_READY");
125 #if 0
126         g_debug ("shm atom = %lu\n", shm_atom);
127         g_debug ("damage atom = %lu\n", damage_atom);
128         g_debug ("show atom = %lu\n", show_atom);
129         g_debug ("position atom = %lu\n", position_atom);
130         g_debug ("offset atom = %lu\n", offset_atom);
131         g_debug ("scale atom = %lu\n", scale_atom);
132         g_debug ("parent atom = %lu\n", parent_atom);
133         g_debug ("ready atom = %lu\n", ready_atom);
134 #endif
135
136         atoms_initialized = TRUE;
137     }
138
139     /* Wait for a ready message */
140
141     gdk_window_add_filter (widget->window,
142                            hildon_remote_texture_event_filter,
143                            widget);
144 }
145
146 static void
147 hildon_remote_texture_unrealize               (GtkWidget *widget)
148 {
149     gdk_window_remove_filter (widget->window,
150                               hildon_remote_texture_event_filter,
151                               widget);
152
153     GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->unrealize (widget);
154 }
155
156 static void
157 hildon_remote_texture_show                    (GtkWidget *widget)
158 {
159     HildonRemoteTexture        *self = HILDON_REMOTE_TEXTURE (widget);
160
161     GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->show (widget);
162     hildon_remote_texture_set_show (self, 1);
163 }
164
165 static void
166 hildon_remote_texture_hide                    (GtkWidget *widget)
167 {
168     HildonRemoteTexture        *self = HILDON_REMOTE_TEXTURE (widget);
169
170     hildon_remote_texture_set_show (self, 0);
171     GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->hide (widget);
172 }
173
174 static void
175 hildon_remote_texture_class_init              (HildonRemoteTextureClass *klass)
176 {
177     GtkWidgetClass    *widget_class = GTK_WIDGET_CLASS (klass);
178
179     widget_class->realize           = hildon_remote_texture_realize;
180     widget_class->unrealize         = hildon_remote_texture_unrealize;
181     widget_class->show              = hildon_remote_texture_show;
182     widget_class->hide              = hildon_remote_texture_hide;
183
184     g_type_class_add_private (klass, sizeof (HildonRemoteTexturePrivate));
185 }
186
187 static void
188 hildon_remote_texture_init                    (HildonRemoteTexture *self)
189 {
190     HildonRemoteTexturePrivate
191                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
192
193     /* Default non-zero values for the private variables */
194
195     priv->scale_x = 1;
196     priv->scale_y = 1;
197     priv->opacity = 0xff;
198 }
199
200 /**
201  * hildon_remote_texture_new:
202  *
203  * Creates a new #HildonRemoteTexture.
204  *
205  * Return value: A #HildonRemoteTexture
206  **/
207 GtkWidget*
208 hildon_remote_texture_new                     (void)
209 {
210     HildonRemoteTexture *newwindow = g_object_new (HILDON_TYPE_REMOTE_TEXTURE, NULL);
211
212     gtk_window_set_decorated (GTK_WINDOW (newwindow), FALSE);
213
214     return GTK_WIDGET (newwindow);
215 }
216
217 /*
218  * An filter for GDK X11 events, waiting for PropertyNotify (window property
219  * changes) events, keeping track of remote texture ready atom.
220  * Having the ready atom set on the window by the window manager will trigger
221  * updates of actor parameters (position/rotation/etc...) to be sent off
222  * to the window manager for processing.
223  */
224 static GdkFilterReturn
225 hildon_remote_texture_event_filter             (GdkXEvent *xevent,
226                                                  GdkEvent *event,
227                                                  gpointer data)
228 {
229     HildonRemoteTexture *self = HILDON_REMOTE_TEXTURE (data);
230     XAnyEvent *any = xevent;
231
232     if (any->type == PropertyNotify)
233     {
234         XPropertyEvent *property = xevent;
235
236         if (property->atom == ready_atom)
237         {
238             hildon_remote_texture_update_ready (self);
239         }
240     }
241
242     return GDK_FILTER_CONTINUE;
243 }
244
245 /*
246  * Check for the ready atom on the remote texture X11 window.
247  * If present, send all pending remote texture messages to the
248  * window manager.
249  */
250 static void
251 hildon_remote_texture_update_ready (HildonRemoteTexture *self)
252 {
253     HildonRemoteTexturePrivate
254                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
255     GtkWidget          *widget = GTK_WIDGET (self);
256     Display            *display = GDK_WINDOW_XDISPLAY (widget->window);
257     Window              window = GDK_WINDOW_XID (widget->window);
258
259     int status;
260     gint xerror;
261
262     Atom actual_type;
263     int  actual_format;
264     unsigned long nitems, bytes_after;
265     unsigned char *prop = NULL;
266
267     /* Check for the "ready" property */
268
269     gdk_error_trap_push ();
270     status = XGetWindowProperty (display, window,
271                                  ready_atom, 0, 32,
272                                  False, XA_ATOM,
273                                  &actual_type, &actual_format,
274                                  &nitems, &bytes_after, &prop);
275     xerror = gdk_error_trap_pop();
276
277     if (prop)
278     {
279         /* We do not actually use the property value for anything,
280          * it is enough that the property is set. */
281
282         XFree (prop);
283     }
284
285     if (xerror ||
286         (status != Success) || (actual_type != XA_ATOM) ||
287         (actual_format != 32) || (nitems != 1))
288     {
289         priv->ready = 0;
290         return;
291     }
292
293     if (priv->ready)
294     {
295         /* The ready flag has been set once already. This means that
296          * the WM has restarted. Trigger re-mapping of the widget to
297          * update the texture actor first. Then push all remote
298          * texture settings anew. */
299
300         priv->map_event_cb_id =
301             g_signal_connect (G_OBJECT (self),
302                               "map-event",
303                               G_CALLBACK(hildon_remote_texture_map_event),
304                               self);
305
306         if (GTK_WIDGET_MAPPED (GTK_WIDGET (self)))
307         {
308             gtk_widget_unmap (GTK_WIDGET (self));
309             gtk_widget_map (GTK_WIDGET (self));
310         }
311
312         return;
313     }
314
315     priv->ready = 1;
316
317     /* Send all pending messages */
318
319     hildon_remote_texture_send_pending_messages (self);
320 }
321
322 static void
323 hildon_remote_texture_send_pending_messages (HildonRemoteTexture *self)
324 {
325     HildonRemoteTexturePrivate
326                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
327
328     if (priv->set_shm)
329       hildon_remote_texture_set_image(self,
330                                       priv->shm_key,
331                                       priv->shm_width, priv->shm_height,
332                                       priv->shm_bpp);
333
334     if (priv->set_damage)
335       hildon_remote_texture_update_area (self,
336                                          priv->damage_x1,
337                                          priv->damage_y1,
338                                          priv->damage_x2 - priv->damage_x1,
339                                          priv->damage_y2 - priv->damage_y1);
340
341     if (priv->set_position)
342         hildon_remote_texture_set_position (self,
343                                             priv->x,
344                                             priv->y,
345                                             priv->width,
346                                             priv->height);
347
348     if (priv->set_offset)
349         hildon_remote_texture_set_offset (self,
350                                           priv->offset_x,
351                                           priv->offset_y);
352
353     if (priv->set_scale)
354         hildon_remote_texture_set_scale (self,
355                                          priv->scale_x,
356                                          priv->scale_y);
357
358     if (priv->set_parent)
359         hildon_remote_texture_set_parent (self,
360                                            priv->parent);
361
362     if (priv->set_show)
363         hildon_remote_texture_set_show_full (self,
364                                               priv->show, priv->opacity);
365 }
366
367 static void
368 hildon_remote_texture_send_all_messages (HildonRemoteTexture *self)
369 {
370     HildonRemoteTexturePrivate
371                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
372
373     priv->set_shm = 1;
374     priv->set_damage = 1;
375     priv->set_position = 1;
376     priv->set_scale = 1;
377     priv->set_parent = 1;
378     priv->set_show = 1;
379
380     hildon_remote_texture_send_pending_messages (self);
381 }
382
383 /* ------------------------------------------------------------- */
384
385 /**
386  * hildon_remote_texture_send_message:
387  * @self: A #HildonRemoteTexture
388  * @message_type: Message id for the remote texture message.
389  * @l0: 1st remote texture message parameter.
390  * @l1: 2nd remote texture message parameter.
391  * @l2: 3rd remote texture message parameter.
392  * @l3: 4th remote texture message parameter.
393  * @l4: 5th remote texture message parameter.
394  *
395  * Sends an X11 ClientMessage event to the window manager with
396  * the specified parameters -- id (@message_type) and data (@l0,
397  * @l1, @l2, @l3, @l4).
398  *
399  * This is an internal utility function that application will
400  * not need to call directly.
401  **/
402 void
403 hildon_remote_texture_send_message (HildonRemoteTexture *self,
404                                      guint32 message_type,
405                                      guint32 l0,
406                                      guint32 l1,
407                                      guint32 l2,
408                                      guint32 l3,
409                                      guint32 l4)
410 {
411     GtkWidget          *widget = GTK_WIDGET (self);
412     Display            *display = GDK_WINDOW_XDISPLAY (widget->window);
413     Window              window = GDK_WINDOW_XID (widget->window);
414
415     XEvent event = { 0 };
416
417     event.xclient.type = ClientMessage;
418     event.xclient.window = window;
419     event.xclient.message_type = (Atom)message_type;
420     event.xclient.format = 32;
421     event.xclient.data.l[0] = l0;
422     event.xclient.data.l[1] = l1;
423     event.xclient.data.l[2] = l2;
424     event.xclient.data.l[3] = l3;
425     event.xclient.data.l[4] = l4;
426
427 #if 0
428     g_debug ("%lu (%lu %lu %lu %lu %lu) -> %lu\n",
429              message_type,
430              l0, l1, l2, l3, l4,
431              window);
432 #endif
433
434     XSendEvent (display, window, True,
435                 StructureNotifyMask,
436                 (XEvent *)&event);
437 }
438
439 /**
440  * hildon_remote_texture_set_image:
441  * @self: A #HildonRemoteTexture
442  * @key: The key that would be used with shmget in hildon-desktop. The key
443  * should probably be created with ftok, and the relevant shared memory
444  * area should be created before this call.
445  * @width: width of image in pixels
446  * @height: height of image in pixels
447  * @bpp: BYTES per pixel - usually 2,3 or 4
448  */
449 void
450 hildon_remote_texture_set_image (HildonRemoteTexture *self,
451                                  key_t key,
452                                  guint width,
453                                  guint height,
454                                  guint bpp)
455 {
456   HildonRemoteTexturePrivate
457                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
458   GtkWidget          *widget = GTK_WIDGET (self);
459
460   priv->set_shm = 1;
461   priv->shm_key = key;
462   priv->shm_width = width;
463   priv->shm_height = height;
464   priv->shm_bpp = bpp;
465
466   if (GTK_WIDGET_MAPPED (widget) && priv->ready)
467     {
468        /* Defer messages until the remote texture is parented
469         * and the parent window is mapped */
470         if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
471             return;
472         hildon_remote_texture_send_message (self,
473                                             shm_atom,
474                                             priv->shm_key,
475                                             priv->shm_width,
476                                             priv->shm_height,
477                                             priv->shm_bpp,
478                                             0);
479         priv->set_shm = 0;
480     }
481 }
482
483 /**
484  * hildon_remote_texture_update_area:
485  * @self: A #HildonRemoteTexture
486  * @x: offset of damaged area in pixels
487  * @y: offset of damaged area in pixels
488  * @width: width of damaged area in pixels
489  * @height: height of damaged area in pixels
490  *
491  * This signals to hildon-desktop that a specific region of the memory area
492  * has changed. This will trigger a redraw and will update the relevant tiles
493  * of the texture.
494  */
495 void
496 hildon_remote_texture_update_area (HildonRemoteTexture *self,
497                                  gint x,
498                                  gint y,
499                                  gint width,
500                                  gint height)
501 {
502   HildonRemoteTexturePrivate
503                      *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
504   GtkWidget          *widget = GTK_WIDGET (self);
505
506   if (priv->damage_x1==priv->damage_x2 || priv->damage_y1==priv->damage_y2)
507     {
508       priv->damage_x1 = x;
509       priv->damage_y1 = y;
510       priv->damage_x2 = x+width;
511       priv->damage_y2 = y+height;
512     }
513   else
514     {
515       if (x<priv->damage_x1) priv->damage_x1 = x;
516       if (y<priv->damage_y1) priv->damage_y1 = y;
517       if (x+width>priv->damage_x2) priv->damage_x2 = x+width;
518       if (y+height>priv->damage_y2) priv->damage_y2 = y+height;
519     }
520   priv->set_damage = 1;
521
522   if (GTK_WIDGET_MAPPED (widget) && priv->ready)
523   {
524      /* Defer messages until the remote texture is parented
525       * and the parent window is mapped */
526       if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
527           return;
528       hildon_remote_texture_send_message (self,
529                                           damage_atom,
530                                           priv->damage_x1,
531                                           priv->damage_y1,
532                                           priv->damage_x2 - priv->damage_x1,
533                                           priv->damage_y2 - priv->damage_y1,
534                                           0);
535       priv->set_damage = 0;
536       priv->damage_x1 = 0;
537       priv->damage_y1 = 0;
538       priv->damage_x2 = 0;
539       priv->damage_y2 = 0;
540   }
541 }
542
543 /**
544  * hildon_remote_texture_set_show_full:
545  * @self: A #HildonRemoteTexture
546  * @show: A boolean flag setting the visibility of the remote texture.
547  * @opacity: Desired opacity setting
548  *
549  * Send a message to the window manager setting the visibility of
550  * the remote texture. This will only affect the visibility of
551  * the remote texture set by the compositing window manager in its own
552  * rendering pipeline, after X has drawn the window to the off-screen
553  * buffer. This setting, naturally, has no effect if the #HildonRemoteTexture
554  * widget is not visible in X11 terms (i.e. realized and mapped).
555  *
556  * Furthermore, if a widget is parented, its final visibility will be
557  * affected by that of the parent window.
558  *
559  * The opacity setting ranges from zero (0), being completely transparent
560  * to 255 (0xff) being fully opaque.
561  *
562  * If the remote texture WM-counterpart is not ready, the show message
563  * will be queued until the WM is ready for it.
564  **/
565 void
566 hildon_remote_texture_set_show_full (HildonRemoteTexture *self,
567                                       gint show,
568                                       gint opacity)
569 {
570     HildonRemoteTexturePrivate
571                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
572     GtkWidget          *widget = GTK_WIDGET (self);
573
574     if (opacity > 255)
575         opacity = 255;
576
577     if (opacity < 0)
578         opacity = 0;
579
580     priv->show = show;
581     priv->opacity = opacity;
582     priv->set_show = 1;
583
584     if (GTK_WIDGET_MAPPED (widget) && priv->ready)
585     {
586         /* Defer show messages until the remote texture is parented
587          * and the parent window is mapped */
588         if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
589             return;
590         hildon_remote_texture_send_message (self,
591                                              show_atom,
592                                              show, opacity,
593                                              0, 0, 0);
594         priv->set_show = 0;
595     }
596 }
597
598 /**
599  * hildon_remote_texture_set_show:
600  * @self: A #HildonRemoteTexture
601  * @show: A boolean flag setting the visibility of the remote texture.
602  *
603  * This function is a shortcut for hildon_remote_texture_set_show_full(),
604  * setting the overall actor visibility without changing it's opacity
605  * setting.
606  **/
607 void
608 hildon_remote_texture_set_show (HildonRemoteTexture *self,
609                                  gint show)
610 {
611     HildonRemoteTexturePrivate
612                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
613
614     hildon_remote_texture_set_show_full (self,
615                                           show, priv->opacity);
616 }
617
618 /**
619  * hildon_remote_texture_set_opacity:
620  * @self: A #HildonRemoteTexture
621  * @opacity: Desired opacity setting
622  *
623  * This function is a shortcut for hildon_remote_texture_set_show_full(),
624  * setting actor opacity without changing it's overall visibility.
625  *
626  * See hildon_remote_texture_set_show_full() for description of the range
627  * of values @opacity argument takes.
628  **/
629 void
630 hildon_remote_texture_set_opacity (HildonRemoteTexture *self,
631                                     gint opacity)
632 {
633     HildonRemoteTexturePrivate
634                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
635
636     hildon_remote_texture_set_show_full (self,
637                                           priv->show, opacity);
638 }
639
640 /**
641  * hildon_remote_texture_set_position:
642  * @self: A #HildonRemoteTexture
643  * @x: Desired X coordinate
644  * @y: Desired Y coordinate
645  * @width: Desired width
646  * @height: Desired height
647  *
648  * Send a message to the window manager setting the offset of the remote
649  * texture in the window (in Remote texture's pixels). The texture
650  * is also subject to the animation effects rendered by the compositing
651  * window manager on that window (like those by task switcher).
652  *
653  * If the remote texture WM-counterpart is not ready, the show message
654  * will be queued until the WM is ready for it.
655  **/
656 void
657 hildon_remote_texture_set_position (HildonRemoteTexture *self,
658                                         gint x,
659                                         gint y,
660                                         gint width,
661                                         gint height)
662 {
663     HildonRemoteTexturePrivate
664                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
665     GtkWidget          *widget = GTK_WIDGET (self);
666
667     priv->x = x;
668     priv->y = y;
669     priv->width = width;
670     priv->height = height;
671     priv->set_position = 1;
672
673     if (GTK_WIDGET_MAPPED (widget) && priv->ready)
674     {
675         /* Defer messages until the remote texture is parented
676          * and the parent window is mapped */
677
678         if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
679             return;
680         hildon_remote_texture_send_message (self,
681                                             position_atom,
682                                             x, y,
683                                             width, height, 0);
684         priv->set_position = 0;
685     }
686 }
687
688 /**
689  * hildon_remote_texture_set_offset:
690  * @self: A #HildonRemoteTexture
691  * @x: Desired X offset
692  * @y: Desired Y offset
693  *
694  * Send a message to the window manager setting the offset of the remote
695  * texture in the window (in Remote texture's pixels). The texture
696  * is also subject to the animation effects rendered by the compositing
697  * window manager on that window (like those by task switcher).
698  *
699  * If the remote texture WM-counterpart is not ready, the show message
700  * will be queued until the WM is ready for it.
701  **/
702 void
703 hildon_remote_texture_set_offset (HildonRemoteTexture *self,
704                                     double x,
705                                     double y)
706 {
707     HildonRemoteTexturePrivate
708                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
709     GtkWidget          *widget = GTK_WIDGET (self);
710
711     priv->offset_x = x;
712     priv->offset_y = y;
713     priv->set_offset = 1;
714
715     if (GTK_WIDGET_MAPPED (widget) && priv->ready)
716     {
717         /* Defer messages until the remote texture is parented
718          * and the parent window is mapped */
719
720         if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
721             return;
722         hildon_remote_texture_send_message (self,
723                                             offset_atom,
724                                             (gint)(x*65536), (gint)(y*65536),
725                                             0, 0, 0);
726         priv->set_offset = 0;
727     }
728 }
729
730 /**
731  * hildon_remote_texture_set_scalex:
732  * @self: A #HildonRemoteTexture
733  * @x_scale: The scale factor for the memory area to be rendered in the X-axis
734  * @y_scale: The scale factor for the memory area to be rendered in the X-axis
735  **/
736 void
737 hildon_remote_texture_set_scale (HildonRemoteTexture *self,
738                                double x_scale,
739                                double y_scale)
740 {
741     HildonRemoteTexturePrivate
742                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
743     GtkWidget          *widget = GTK_WIDGET (self);
744
745     priv->scale_x = x_scale;
746     priv->scale_y = y_scale;
747     priv->set_scale = 1;
748
749     if (GTK_WIDGET_MAPPED (widget) && priv->ready)
750     {
751         /* Defer messages until the remote texture is parented
752          * and the parent window is mapped */
753         if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
754             return;
755         hildon_remote_texture_send_message (self,
756                                              scale_atom,
757                                              priv->scale_x * (1 << 16),
758                                              priv->scale_y * (1 << 16),
759                                              0, 0, 0);
760         priv->set_scale = 0;
761     }
762 }
763
764 /*
765  * This callback will be triggered by the parent widget of
766  * an remote texture when it is mapped. The compositing
767  * window manager is now ready to parent the remote texture
768  * into the target parent window.
769  */
770 static gboolean
771 hildon_remote_texture_parent_map_event (GtkWidget *parent,
772                                          GdkEvent *event,
773                                          gpointer user_data)
774 {
775     hildon_remote_texture_set_parent (HILDON_REMOTE_TEXTURE (user_data),
776                                        GTK_WINDOW (parent));
777     return FALSE;
778 }
779
780 /*
781  * This callback will be triggered by the widget re-mapping
782  * itself in case of WM restarting. The point is to push all
783  * remote texture parameters anew to the WM.
784  */
785 static gboolean
786 hildon_remote_texture_map_event (GtkWidget *widget,
787                                   GdkEvent *event,
788                                   gpointer user_data)
789 {
790     HildonRemoteTexture
791                        *self = HILDON_REMOTE_TEXTURE (user_data);
792     HildonRemoteTexturePrivate
793                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
794
795     hildon_remote_texture_send_all_messages (self);
796
797     /* Disconnect the "map-event" handler after the "emergency resend all
798      * actor parameters" drill is over. */
799
800     if (priv->map_event_cb_id)
801     {
802         g_signal_handler_disconnect (self,
803                                      priv->map_event_cb_id);
804         priv->map_event_cb_id = 0;
805     }
806
807     return FALSE;
808 }
809
810 /**
811  * hildon_remote_texture_set_parent:
812  * @self: A #HildonRemoteTexture
813  * @parent: A #GtkWindow that the actor will be parented to.
814  *
815  * Send a message to the window manager setting the parent window
816  * for the remote texture. Parenting an actor will not affect the
817  * X window that the HildonRemoteTexture represents, but it's off-screen
818  * bitmap as it is handled by the compositing window manager.
819  *
820  * Parenting an remote texture will affect its visibility as set
821  * by the gtk_widget_show(), gtk_widget_hide() and
822  * hildon_remote_texture_set_show(). The remote texture will only be
823  * visible when the top-level window it is parented is visible.
824  *
825  * Passing %NULL as a @parent argument will unparent the remote texture.
826  * This will restore the actor's visibility if it was suppressed by
827  * being unparented or parented to an unmapped window.
828  *
829  * If the remote texture WM-counterpart is not ready, the show message
830  * will be queued until the WM is ready for it.
831  **/
832 void
833 hildon_remote_texture_set_parent (HildonRemoteTexture *self,
834                                    GtkWindow *parent)
835 {
836     HildonRemoteTexturePrivate
837                        *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
838     GtkWidget          *widget = GTK_WIDGET (self);
839
840     gtk_window_set_transient_for (GTK_WINDOW (self), parent);
841
842     if (priv->parent != parent)
843     {
844         /* Setting a new parent */
845
846         if (priv->parent)
847         {
848             if (priv->parent_map_event_cb_id)
849                 g_signal_handler_disconnect (priv->parent,
850                                              priv->parent_map_event_cb_id);
851
852             /* Might need a synchronized "parent(0)" or "parent(new parent)"
853              * message here before we can safely decrease the reference count. */
854
855             g_object_unref (priv->parent);
856         }
857
858         priv->parent = parent;
859         priv->set_parent = 1;
860
861         if (parent != 0)
862         {
863             /* The widget is being (re)parented, not unparented. */
864
865             g_object_ref (parent);
866
867             priv->parent_map_event_cb_id =
868                 g_signal_connect (G_OBJECT (priv->parent),
869                                   "map-event",
870                                   G_CALLBACK(hildon_remote_texture_parent_map_event),
871                                   self);
872         }
873         else
874         {
875             priv->parent_map_event_cb_id = 0;
876         }
877     }
878
879     if (GTK_WIDGET_MAPPED (widget) && priv->ready)
880     {
881         Window win = 0;
882
883         /* If the remote texture is being unparented or parented to an
884          * unmapped widget, force its visibility to "hidden". */
885
886         if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
887         {
888             hildon_remote_texture_send_message (self,
889                                                  show_atom,
890                                                  0, priv->opacity,
891                                                  0, 0, 0);
892         }
893
894         /* If the widget is being parented (parent != 0), only proceed when
895          * the parent widget is realized, since we need the X window id of
896          * the parent. If the widget is being unparented (parent == 0), pass
897          * the "special" window id of 0 in the message. */
898
899         if (priv->parent)
900         {
901             if (!GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
902                 return;
903
904             GdkWindow *gdk = GTK_WIDGET (parent)->window;
905             win = GDK_WINDOW_XID (gdk);
906         }
907
908         hildon_remote_texture_send_message (self,
909                                              parent_atom,
910                                              win,
911                                              0, 0, 0, 0);
912         priv->set_parent = 0;
913
914         /* Set remote texture visibility to desired value (in case it was
915          * forced off when the actor was parented into an unmapped widget). */
916
917         hildon_remote_texture_send_message (self,
918                                              show_atom,
919                                              priv->show, priv->opacity,
920                                              0, 0, 0);
921         priv->set_show = 0;
922     }
923 }
924