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