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