2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
6 * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
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.
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.
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
26 * SECTION:hildon-remote-texture
27 * @short_description: Widget representing a Clutter/GLES texture created
28 * from a shared memory area.
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.
36 #include <X11/Xatom.h>
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"
44 G_DEFINE_TYPE (HildonRemoteTexture, hildon_remote_texture, GTK_TYPE_WINDOW);
46 static GdkFilterReturn
47 hildon_remote_texture_event_filter (GdkXEvent *xevent,
51 hildon_remote_texture_update_ready (HildonRemoteTexture *self);
53 hildon_remote_texture_send_pending_messages (HildonRemoteTexture *self);
55 hildon_remote_texture_send_all_messages (HildonRemoteTexture *self);
57 hildon_remote_texture_parent_map_event (GtkWidget *parent,
61 hildon_remote_texture_map_event (GtkWidget *widget,
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;
73 static gboolean atoms_initialized = FALSE;
76 hildon_remote_texture_realize (GtkWidget *widget)
79 Atom wm_type, applet_type;
81 GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->realize (widget);
83 /* Set remote texture window type. */
85 display = gdk_drawable_get_display (widget->window);
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");
90 XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window), wm_type,
91 XA_ATOM, 32, PropModeReplace,
92 (unsigned char *) &applet_type, 1);
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. */
98 if (!atoms_initialized)
101 gdk_x11_get_xatom_by_name_for_display
102 (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_SHM");
104 gdk_x11_get_xatom_by_name_for_display
105 (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_DAMAGE");
107 gdk_x11_get_xatom_by_name_for_display
108 (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_SHOW");
110 gdk_x11_get_xatom_by_name_for_display
111 (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_POSITION");
113 gdk_x11_get_xatom_by_name_for_display
114 (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_SCALE");
116 gdk_x11_get_xatom_by_name_for_display
117 (display, "_HILDON_TEXTURE_CLIENT_MESSAGE_PARENT");
119 gdk_x11_get_xatom_by_name_for_display
120 (display, "_HILDON_TEXTURE_CLIENT_READY");
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);
131 atoms_initialized = TRUE;
134 /* Wait for a ready message */
136 gdk_window_add_filter (widget->window,
137 hildon_remote_texture_event_filter,
142 hildon_remote_texture_unrealize (GtkWidget *widget)
144 gdk_window_remove_filter (widget->window,
145 hildon_remote_texture_event_filter,
148 GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->unrealize (widget);
152 hildon_remote_texture_show (GtkWidget *widget)
154 HildonRemoteTexture *self = HILDON_REMOTE_TEXTURE (widget);
156 GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->show (widget);
157 hildon_remote_texture_set_show (self, 1);
161 hildon_remote_texture_hide (GtkWidget *widget)
163 HildonRemoteTexture *self = HILDON_REMOTE_TEXTURE (widget);
165 hildon_remote_texture_set_show (self, 0);
166 GTK_WIDGET_CLASS (hildon_remote_texture_parent_class)->hide (widget);
170 hildon_remote_texture_class_init (HildonRemoteTextureClass *klass)
172 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
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;
179 g_type_class_add_private (klass, sizeof (HildonRemoteTexturePrivate));
183 hildon_remote_texture_init (HildonRemoteTexture *self)
185 HildonRemoteTexturePrivate
186 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
188 /* Default non-zero values for the private variables */
192 priv->opacity = 0xff;
196 * hildon_remote_texture_new:
198 * Creates a new #HildonRemoteTexture.
200 * Return value: A #HildonRemoteTexture
203 hildon_remote_texture_new (void)
205 HildonRemoteTexture *newwindow = g_object_new (HILDON_TYPE_REMOTE_TEXTURE, NULL);
207 gtk_window_set_decorated (GTK_WINDOW (newwindow), FALSE);
209 return GTK_WIDGET (newwindow);
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.
219 static GdkFilterReturn
220 hildon_remote_texture_event_filter (GdkXEvent *xevent,
224 HildonRemoteTexture *self = HILDON_REMOTE_TEXTURE (data);
225 XAnyEvent *any = xevent;
227 if (any->type == PropertyNotify)
229 XPropertyEvent *property = xevent;
231 if (property->atom == ready_atom)
233 hildon_remote_texture_update_ready (self);
237 return GDK_FILTER_CONTINUE;
241 * Check for the ready atom on the remote texture X11 window.
242 * If present, send all pending remote texture messages to the
246 hildon_remote_texture_update_ready (HildonRemoteTexture *self)
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);
259 unsigned long nitems, bytes_after;
260 unsigned char *prop = NULL;
262 /* Check for the "ready" property */
264 gdk_error_trap_push ();
265 status = XGetWindowProperty (display, window,
268 &actual_type, &actual_format,
269 &nitems, &bytes_after, &prop);
270 xerror = gdk_error_trap_pop();
274 /* We do not actually use the property value for anything,
275 * it is enough that the property is set. */
281 (status != Success) || (actual_type != XA_ATOM) ||
282 (actual_format != 32) || (nitems != 1))
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. */
295 priv->map_event_cb_id =
296 g_signal_connect (G_OBJECT (self),
298 G_CALLBACK(hildon_remote_texture_map_event),
301 if (GTK_WIDGET_MAPPED (GTK_WIDGET (self)))
303 gtk_widget_unmap (GTK_WIDGET (self));
304 gtk_widget_map (GTK_WIDGET (self));
312 /* Send all pending messages */
314 hildon_remote_texture_send_pending_messages (self);
318 hildon_remote_texture_send_pending_messages (HildonRemoteTexture *self)
320 HildonRemoteTexturePrivate
321 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
324 hildon_remote_texture_set_image(self,
326 priv->shm_width, priv->shm_height,
329 if (priv->set_damage)
330 hildon_remote_texture_update_area (self,
333 priv->damage_x2 - priv->damage_x1,
334 priv->damage_y2 - priv->damage_y1);
336 if (priv->set_position)
337 hildon_remote_texture_set_position (self,
342 hildon_remote_texture_set_scale (self,
346 if (priv->set_parent)
347 hildon_remote_texture_set_parent (self,
351 hildon_remote_texture_set_show_full (self,
352 priv->show, priv->opacity);
356 hildon_remote_texture_send_all_messages (HildonRemoteTexture *self)
358 HildonRemoteTexturePrivate
359 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
362 priv->set_damage = 1;
363 priv->set_position = 1;
365 priv->set_parent = 1;
368 hildon_remote_texture_send_pending_messages (self);
371 /* ------------------------------------------------------------- */
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.
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).
387 * This is an internal utility function that application will
388 * not need to call directly.
391 hildon_remote_texture_send_message (HildonRemoteTexture *self,
392 guint32 message_type,
399 GtkWidget *widget = GTK_WIDGET (self);
400 Display *display = GDK_WINDOW_XDISPLAY (widget->window);
401 Window window = GDK_WINDOW_XID (widget->window);
403 XEvent event = { 0 };
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;
416 g_debug ("%lu (%lu %lu %lu %lu %lu) -> %lu\n",
422 XSendEvent (display, window, True,
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
438 hildon_remote_texture_set_image (HildonRemoteTexture *self,
444 HildonRemoteTexturePrivate
445 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
446 GtkWidget *widget = GTK_WIDGET (self);
450 priv->shm_width = width;
451 priv->shm_height = height;
454 if (GTK_WIDGET_MAPPED (widget) && priv->ready)
456 hildon_remote_texture_send_message (self,
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
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
480 hildon_remote_texture_update_area (HildonRemoteTexture *self,
486 HildonRemoteTexturePrivate
487 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
488 GtkWidget *widget = GTK_WIDGET (self);
490 if (priv->damage_x1==priv->damage_x2 || priv->damage_y1==priv->damage_y2)
494 priv->damage_x2 = x+width;
495 priv->damage_y2 = y+height;
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;
504 priv->set_damage = 1;
506 if (GTK_WIDGET_MAPPED (widget) && priv->ready)
508 hildon_remote_texture_send_message (self,
512 priv->damage_x2 - priv->damage_x1,
513 priv->damage_y2 - priv->damage_y1,
515 priv->set_damage = 0;
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
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).
536 * Furthermore, if a widget is parented, its final visibility will be
537 * affected by that of the parent window.
539 * The opacity setting ranges from zero (0), being completely transparent
540 * to 255 (0xff) being fully opaque.
542 * If the remote texture WM-counterpart is not ready, the show message
543 * will be queued until the WM is ready for it.
546 hildon_remote_texture_set_show_full (HildonRemoteTexture *self,
550 HildonRemoteTexturePrivate
551 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
552 GtkWidget *widget = GTK_WIDGET (self);
561 priv->opacity = opacity;
564 if (GTK_WIDGET_MAPPED (widget) && priv->ready)
566 /* Defer show messages until the remote texture is parented
567 * and the parent window is mapped */
569 if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
572 hildon_remote_texture_send_message (self,
581 * hildon_remote_texture_set_show:
582 * @self: A #HildonRemoteTexture
583 * @show: A boolean flag setting the visibility of the remote texture.
585 * This function is a shortcut for hildon_remote_texture_set_show_full(),
586 * setting the overall actor visibility without changing it's opacity
590 hildon_remote_texture_set_show (HildonRemoteTexture *self,
593 HildonRemoteTexturePrivate
594 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
596 hildon_remote_texture_set_show_full (self,
597 show, priv->opacity);
601 * hildon_remote_texture_set_opacity:
602 * @self: A #HildonRemoteTexture
603 * @opacity: Desired opacity setting
605 * This function is a shortcut for hildon_remote_texture_set_show_full(),
606 * setting actor opacity without changing it's overall visibility.
608 * See hildon_remote_texture_set_show_full() for description of the range
609 * of values @opacity argument takes.
612 hildon_remote_texture_set_opacity (HildonRemoteTexture *self,
615 HildonRemoteTexturePrivate
616 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
618 hildon_remote_texture_set_show_full (self,
619 priv->show, opacity);
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)
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).
634 * If the remote texture WM-counterpart is not ready, the show message
635 * will be queued until the WM is ready for it.
638 hildon_remote_texture_set_position (HildonRemoteTexture *self,
642 HildonRemoteTexturePrivate
643 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
644 GtkWidget *widget = GTK_WIDGET (self);
646 priv->position_x = x;
647 priv->position_y = y;
648 priv->set_position = 1;
650 if (GTK_WIDGET_MAPPED (widget) && priv->ready)
652 hildon_remote_texture_send_message (self,
654 (gint)(x*65536), (gint)(y*65536),
656 priv->set_position = 0;
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
667 hildon_remote_texture_set_scale (HildonRemoteTexture *self,
671 HildonRemoteTexturePrivate
672 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
673 GtkWidget *widget = GTK_WIDGET (self);
675 priv->scale_x = x_scale;
676 priv->scale_y = y_scale;
679 if (GTK_WIDGET_MAPPED (widget) && priv->ready)
681 hildon_remote_texture_send_message (self,
683 priv->scale_x * (1 << 16),
684 priv->scale_y * (1 << 16),
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.
697 hildon_remote_texture_parent_map_event (GtkWidget *parent,
701 hildon_remote_texture_set_parent (HILDON_REMOTE_TEXTURE (user_data),
702 GTK_WINDOW (parent));
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.
712 hildon_remote_texture_map_event (GtkWidget *widget,
717 *self = HILDON_REMOTE_TEXTURE (user_data);
718 HildonRemoteTexturePrivate
719 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
721 hildon_remote_texture_send_all_messages (self);
723 /* Disconnect the "map-event" handler after the "emergency resend all
724 * actor parameters" drill is over. */
726 if (priv->map_event_cb_id)
728 g_signal_handler_disconnect (self,
729 priv->map_event_cb_id);
730 priv->map_event_cb_id = 0;
737 * hildon_remote_texture_set_parent:
738 * @self: A #HildonRemoteTexture
739 * @parent: A #GtkWindow that the actor will be parented to.
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.
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.
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.
755 * If the remote texture WM-counterpart is not ready, the show message
756 * will be queued until the WM is ready for it.
759 hildon_remote_texture_set_parent (HildonRemoteTexture *self,
762 HildonRemoteTexturePrivate
763 *priv = HILDON_REMOTE_TEXTURE_GET_PRIVATE (self);
764 GtkWidget *widget = GTK_WIDGET (self);
766 gtk_window_set_transient_for (GTK_WINDOW (self), parent);
768 if (priv->parent != parent)
770 /* Setting a new parent */
774 if (priv->parent_map_event_cb_id)
775 g_signal_handler_disconnect (priv->parent,
776 priv->parent_map_event_cb_id);
778 /* Might need a synchronized "parent(0)" or "parent(new parent)"
779 * message here before we can safely decrease the reference count. */
781 g_object_unref (priv->parent);
784 priv->parent = parent;
785 priv->set_parent = 1;
789 /* The widget is being (re)parented, not unparented. */
791 g_object_ref (parent);
793 priv->parent_map_event_cb_id =
794 g_signal_connect (G_OBJECT (priv->parent),
796 G_CALLBACK(hildon_remote_texture_parent_map_event),
801 priv->parent_map_event_cb_id = 0;
805 if (GTK_WIDGET_MAPPED (widget) && priv->ready)
809 /* If the remote texture is being unparented or parented to an
810 * unmapped widget, force its visibility to "hidden". */
812 if (!priv->parent || !GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
814 hildon_remote_texture_send_message (self,
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. */
827 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (priv->parent)))
830 GdkWindow *gdk = GTK_WIDGET (parent)->window;
831 win = GDK_WINDOW_XID (gdk);
834 hildon_remote_texture_send_message (self,
838 priv->set_parent = 0;
840 /* Set remote texture visibility to desired value (in case it was
841 * forced off when the actor was parented into an unmapped widget). */
843 hildon_remote_texture_send_message (self,
845 priv->show, priv->opacity,