1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 #include "config.h"
4
5 #include "shell-embedded-window-private.h"
6
7 #include <gdk/gdkx.h>
8
9 enum {
10 PROP_0,
11
12 PROP_WINDOW
13 };
14
15 struct _ShellGtkEmbedPrivate
16 {
17 ShellEmbeddedWindow *window;
18 };
19
20 G_DEFINE_TYPE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_X11_TYPE_TEXTURE_PIXMAP);
21
22 static void shell_gtk_embed_set_window (ShellGtkEmbed *embed,
23 ShellEmbeddedWindow *window);
24
25 static void
26 shell_gtk_embed_on_window_destroy (GtkWidget *object,
27 ShellGtkEmbed *embed)
28 {
29 shell_gtk_embed_set_window (embed, NULL);
30 }
31
32 static void
33 shell_gtk_embed_on_window_realize (GtkWidget *widget,
34 ShellGtkEmbed *embed)
35 {
36 /* Here automatic=FALSE means to use CompositeRedirectManual.
37 * That is, the X server shouldn't draw the window onto the
38 * screen.
39 */
40 clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed),
41 gdk_x11_window_get_xid (gtk_widget_get_window (widget)),
42 FALSE);
43 }
44
45 static void
46 shell_gtk_embed_set_window (ShellGtkEmbed *embed,
47 ShellEmbeddedWindow *window)
48 {
49
50 if (embed->priv->window)
51 {
52 _shell_embedded_window_set_actor (embed->priv->window, NULL);
53
54 g_object_unref (embed->priv->window);
55
56 clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed),
57 None,
58 FALSE);
59
60 g_signal_handlers_disconnect_by_func (embed->priv->window,
61 (gpointer)shell_gtk_embed_on_window_destroy,
62 embed);
63 g_signal_handlers_disconnect_by_func (embed->priv->window,
64 (gpointer)shell_gtk_embed_on_window_realize,
65 embed);
66 }
67
68 embed->priv->window = window;
69
70 if (embed->priv->window)
71 {
72 g_object_ref (embed->priv->window);
73
74 _shell_embedded_window_set_actor (embed->priv->window, embed);
75
76 g_signal_connect (embed->priv->window, "destroy",
77 G_CALLBACK (shell_gtk_embed_on_window_destroy), embed);
78 g_signal_connect (embed->priv->window, "realize",
79 G_CALLBACK (shell_gtk_embed_on_window_realize), embed);
80
81 if (gtk_widget_get_realized (GTK_WIDGET (window)))
82 shell_gtk_embed_on_window_realize (GTK_WIDGET (embed->priv->window), embed);
83 }
84
85 clutter_actor_queue_relayout (CLUTTER_ACTOR (embed));
86 }
87
88 static void
89 shell_gtk_embed_set_property (GObject *object,
90 guint prop_id,
91 const GValue *value,
92 GParamSpec *pspec)
93 {
94 ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
95
96 switch (prop_id)
97 {
98 case PROP_WINDOW:
99 shell_gtk_embed_set_window (embed, (ShellEmbeddedWindow *)g_value_get_object (value));
100 break;
101
102 default:
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
104 break;
105 }
106 }
107
108 static void
109 shell_gtk_embed_get_property (GObject *object,
110 guint prop_id,
111 GValue *value,
112 GParamSpec *pspec)
113 {
114 ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
115
116 switch (prop_id)
117 {
118 case PROP_WINDOW:
119 g_value_set_object (value, embed->priv->window);
120 break;
121
122 default:
123 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
124 break;
125 }
126 }
127
128 static void
129 shell_gtk_embed_get_preferred_width (ClutterActor *actor,
130 float for_height,
131 float *min_width_p,
132 float *natural_width_p)
133 {
134 ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
135
136 if (embed->priv->window
137 && gtk_widget_get_visible (GTK_WIDGET (embed->priv->window)))
138 {
139 GtkRequisition min_req, natural_req;
140 gtk_widget_get_preferred_size (GTK_WIDGET (embed->priv->window), &min_req, &natural_req);
141
142 *min_width_p = min_req.width;
143 *natural_width_p = natural_req.width;
144 }
145 else
146 *min_width_p = *natural_width_p = 0;
147 }
148
149 static void
150 shell_gtk_embed_get_preferred_height (ClutterActor *actor,
151 float for_width,
152 float *min_height_p,
153 float *natural_height_p)
154 {
155 ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
156
157 if (embed->priv->window
158 && gtk_widget_get_visible (GTK_WIDGET (embed->priv->window)))
159 {
160 GtkRequisition min_req, natural_req;
161 gtk_widget_get_preferred_size (GTK_WIDGET (embed->priv->window), &min_req, &natural_req);
162
163 *min_height_p = min_req.height;
164 *natural_height_p = natural_req.height;
165 }
166 else
167 *min_height_p = *natural_height_p = 0;
168 }
169
170 static void
171 shell_gtk_embed_allocate (ClutterActor *actor,
172 const ClutterActorBox *box,
173 ClutterAllocationFlags flags)
174 {
175 ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
176 float wx = 0.0, wy = 0.0, x, y, ax, ay;
177
178 CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->
179 allocate (actor, box, flags);
180
181 /* Find the actor's new coordinates in terms of the stage (which is
182 * priv->window's parent window.
183 */
184 while (actor)
185 {
186 clutter_actor_get_position (actor, &x, &y);
187 clutter_actor_get_anchor_point (actor, &ax, &ay);
'clutter_actor_get_anchor_point' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-actor.h:140)
(emitted by gcc)
188
189 wx += x - ax;
190 wy += y - ay;
191
192 actor = clutter_actor_get_parent (actor);
193 }
194
195 _shell_embedded_window_allocate (embed->priv->window,
196 (int)(0.5 + wx), (int)(0.5 + wy),
197 box->x2 - box->x1,
198 box->y2 - box->y1);
199 }
200
201 static void
202 shell_gtk_embed_map (ClutterActor *actor)
203 {
204 ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
205
206 _shell_embedded_window_map (embed->priv->window);
207
208 CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->map (actor);
209 }
210
211 static void
212 shell_gtk_embed_unmap (ClutterActor *actor)
213 {
214 ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
215
216 _shell_embedded_window_unmap (embed->priv->window);
217
218 CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->unmap (actor);
219 }
220
221 static void
222 shell_gtk_embed_dispose (GObject *object)
223 {
224 ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
225
226 G_OBJECT_CLASS (shell_gtk_embed_parent_class)->dispose (object);
227
228 shell_gtk_embed_set_window (embed, NULL);
229 }
230
231 static void
232 shell_gtk_embed_class_init (ShellGtkEmbedClass *klass)
233 {
234 GObjectClass *object_class = G_OBJECT_CLASS (klass);
235 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
236
237 g_type_class_add_private (klass, sizeof (ShellGtkEmbedPrivate));
238
239 object_class->get_property = shell_gtk_embed_get_property;
240 object_class->set_property = shell_gtk_embed_set_property;
241 object_class->dispose = shell_gtk_embed_dispose;
242
243 actor_class->get_preferred_width = shell_gtk_embed_get_preferred_width;
244 actor_class->get_preferred_height = shell_gtk_embed_get_preferred_height;
245 actor_class->allocate = shell_gtk_embed_allocate;
246 actor_class->map = shell_gtk_embed_map;
247 actor_class->unmap = shell_gtk_embed_unmap;
248
249 g_object_class_install_property (object_class,
250 PROP_WINDOW,
251 g_param_spec_object ("window",
252 "Window",
253 "ShellEmbeddedWindow to embed",
254 SHELL_TYPE_EMBEDDED_WINDOW,
255 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
256 }
257
258 static void
259 shell_gtk_embed_init (ShellGtkEmbed *embed)
260 {
261 embed->priv = G_TYPE_INSTANCE_GET_PRIVATE (embed, SHELL_TYPE_GTK_EMBED,
262 ShellGtkEmbedPrivate);
263
264 /* automatic here means whether ClutterX11TexturePixmap should
265 * process damage update and refresh the pixmap itself.
266 */
267 clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (embed), TRUE);
268 }
269
270 /*
271 * Public API
272 */
273 ClutterActor *
274 shell_gtk_embed_new (ShellEmbeddedWindow *window)
275 {
276 g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL);
277
278 return g_object_new (SHELL_TYPE_GTK_EMBED,
279 "window", window,
280 NULL);
281 }