1 /*
2 * Copyright (C) 2010 Marco Diego Aurélio Mesquita
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * Authors:
19 * Marco Diego Aurélio Mesquita <marcodiegomesquita@gmail.com>
20 */
21
22 #include <config.h>
23
24 /**
25 * SECTION:glade-preview
26 * @Short_Description: The glade preview launch/kill interface.
27 *
28 * This object owns all data that is needed to keep a preview. It stores
29 * the GIOChannel used for comunnication between glade and glade-previewer,
30 * the event source id for a watch (in the case a watch is used to monitor
31 * the communication channel), the previewed widget and the pid of the
32 * corresponding glade-previewer.
33 *
34 */
35
36
37 #include <string.h>
38 #include <stdlib.h>
39 #include <glib.h>
40 #include <glib/gi18n-lib.h>
41 #include <glib/gstdio.h>
42
43 #include "glade.h"
44 #include "glade-preview.h"
45 #include "glade-project.h"
46 #include "glade-app.h"
47
48 #include "glade-preview-tokens.h"
49
50 #ifdef G_OS_WIN32
51 #define GLADE_PREVIEWER "glade-previewer.exe"
52 #else
53 #define GLADE_PREVIEWER "glade-previewer"
54 #endif
55
56 /* Private data for glade-preview */
57 struct _GladePreviewPrivate
58 {
59 GIOChannel *channel; /* Channel user for communication between glade and glade-previewer */
60 guint watch; /* Event source id used to monitor the channel */
61 GladeWidget *previewed_widget;
62 GPid pid; /* Pid of the corresponding glade-previewer process */
63 };
64
65 G_DEFINE_TYPE_WITH_PRIVATE (GladePreview, glade_preview, G_TYPE_OBJECT);
66
67 enum
68 {
69 PREVIEW_EXITS,
70 LAST_SIGNAL
71 };
72
73 static guint glade_preview_signals[LAST_SIGNAL] = { 0 };
74
75 /**
76 * glade_preview_kill
77 * @preview: a #GladePreview that will be killed.
78 *
79 * Uses the communication channel and protocol to send the "<quit>" token to the
80 * glade-previewer telling it to commit suicide.
81 *
82 */
83 static void
glade_preview_kill(GladePreview * preview)84 glade_preview_kill (GladePreview *preview)
85 {
86 const gchar *quit = QUIT_TOKEN;
87 GIOChannel *channel;
88 GError *error = NULL;
89 gsize size;
90
91 channel = preview->priv->channel;
92 g_io_channel_write_chars (channel, quit, strlen (quit), &size, &error);
93
94 if (size != strlen (quit) && error != NULL)
95 {
96 g_warning ("Error passing quit signal trough pipe: %s", error->message);
97 g_error_free (error);
98 }
99
100 g_io_channel_flush (channel, &error);
101 if (error != NULL)
102 {
103 g_warning ("Error flushing channel: %s", error->message);
104 g_error_free (error);
105 }
106
107 g_io_channel_shutdown (channel, TRUE, &error);
108 if (error != NULL)
109 {
110 g_warning ("Error shutting down channel: %s", error->message);
111 g_error_free (error);
112 }
113 }
114
115 static void
glade_preview_dispose(GObject * gobject)116 glade_preview_dispose (GObject *gobject)
117 {
118 GladePreview *self = GLADE_PREVIEW (gobject);
119
120 if (self->priv->watch)
121 {
122 g_source_remove (self->priv->watch);
123 glade_preview_kill (self);
124 }
125
126 if (self->priv->channel)
127 {
128 g_io_channel_unref (self->priv->channel);
129 self->priv->channel = NULL;
130 }
131
132 G_OBJECT_CLASS (glade_preview_parent_class)->dispose (gobject);
133 }
134
135 /* We have to use finalize because of the signal that is sent in dispose */
136 static void
glade_preview_finalize(GObject * gobject)137 glade_preview_finalize (GObject *gobject)
138 {
139 G_OBJECT_CLASS (glade_preview_parent_class)->finalize (gobject);
140 }
141
142 static void
glade_preview_class_init(GladePreviewClass * klass)143 glade_preview_class_init (GladePreviewClass *klass)
144 {
145 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
146
147 gobject_class->dispose = glade_preview_dispose;
148 gobject_class->finalize = glade_preview_finalize;
149
150 /**
151 * GladePreview::exits:
152 * @gladepreview: the #GladePreview which received the signal.
153 * @gladeproject: the #GladeProject associated with the preview.
154 *
155 * Emitted when @preview exits.
156 */
157 glade_preview_signals[PREVIEW_EXITS] =
158 g_signal_new ("exits",
159 G_TYPE_FROM_CLASS (gobject_class),
160 G_SIGNAL_RUN_FIRST,
161 0, NULL, NULL,
162 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
163 }
164
165 static void
glade_preview_init(GladePreview * self)166 glade_preview_init (GladePreview *self)
167 {
168 GladePreviewPrivate *priv;
169
170 self->priv = priv = glade_preview_get_instance_private (self);
171 priv->channel = NULL;
172 }
173
174 static void
glade_preview_internal_watch(GPid pid,gint status,gpointer data)175 glade_preview_internal_watch (GPid pid, gint status, gpointer data)
176 {
177 GladePreview *preview = GLADE_PREVIEW (data);
178
179 preview->priv->watch = 0;
180
181 /* This means a preview exited. We'll now signal it */
182 g_signal_emit (preview, glade_preview_signals[PREVIEW_EXITS], 0);
183 }
184
185 /**
186 * glade_preview_launch:
187 * @widget: Pointer to a local instance of the widget that will be previewed.
188 * @buffer: Contents of an xml definition of the interface which will be previewed
189 *
190 * Creates a new #GladePreview and launches glade-previewer to preview it.
191 *
192 * Returns: a new #GladePreview or NULL if launch fails.
193 *
194 */
195 GladePreview *
glade_preview_launch(GladeWidget * widget,const gchar * buffer)196 glade_preview_launch (GladeWidget *widget, const gchar *buffer)
197 {
198 GPid pid;
199 GError *error = NULL;
200 gchar *argv[10], *executable;
201 gint child_stdin;
202 gsize bytes_written;
203 GIOChannel *output;
204 GladePreview *preview = NULL;
205 const gchar *css_provider, *filename;
206 GladeProject *project;
207 gchar *name;
208 gint i;
209
210 g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
211
212 executable = g_find_program_in_path (GLADE_PREVIEWER);
213
214 project = glade_widget_get_project (widget);
215 filename = glade_project_get_path (project);
216 name = (filename) ? NULL : glade_project_get_name (project);
217
218 argv[0] = executable;
219 argv[1] = "--listen";
220 argv[2] = "--toplevel";
221 argv[3] = (gchar *) glade_widget_get_name (widget);
222 argv[4] = "--filename";
223 argv[5] = (filename) ? (gchar *) filename : name;
224
225 i = 5;
226 if (glade_project_get_template (project))
227 argv[++i] = "--template";
228
229 argv[++i] = NULL;
230
231 css_provider = glade_project_get_css_provider_path (glade_widget_get_project (widget));
232 if (css_provider)
233 {
234 argv[i] = "--css";
235 argv[++i] = (gchar *) css_provider;
236 argv[++i] = NULL;
237 }
238
239 if (g_spawn_async_with_pipes (NULL,
240 argv,
241 NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
242 &pid, &child_stdin, NULL, NULL,
243 &error) == FALSE)
244 {
245 g_warning (_("Error launching previewer: %s\n"), error->message);
246 glade_util_ui_message (glade_app_get_window (),
247 GLADE_UI_ERROR, NULL,
248 _("Failed to launch preview: %s.\n"),
249 error->message);
250 g_error_free (error);
251 g_free (executable);
252 g_free (name);
253 return NULL;
254 }
255
256 #ifdef G_OS_WIN32
257 output = g_io_channel_win32_new_fd (child_stdin);
258 #else
259 output = g_io_channel_unix_new (child_stdin);
260 #endif
261
262 g_io_channel_write_chars (output, buffer, strlen (buffer), &bytes_written,
263 &error);
264
265 if (bytes_written != strlen (buffer) && error != NULL)
266 {
267 g_warning ("Error passing UI trough pipe: %s", error->message);
268 g_error_free (error);
269 }
270
271 g_io_channel_flush (output, &error);
272 if (error != NULL)
273 {
274 g_warning ("Error flushing UI trough pipe: %s", error->message);
275 g_error_free (error);
276 }
277
278 /* Setting up preview data */
279 preview = g_object_new (GLADE_TYPE_PREVIEW, NULL);
280 preview->priv->channel = output;
281 preview->priv->previewed_widget = widget;
282 preview->priv->pid = pid;
283
284 preview->priv->watch =
285 g_child_watch_add (preview->priv->pid,
286 glade_preview_internal_watch,
287 preview);
288
289 g_free (executable);
290 g_free (name);
291
292 return preview;
293 }
294
295 void
glade_preview_update(GladePreview * preview,const gchar * buffer)296 glade_preview_update (GladePreview *preview, const gchar *buffer)
297 {
298 const gchar *update_token = UPDATE_TOKEN;
299 gchar *update;
300 GIOChannel *channel;
301 GError *error = NULL;
302 gsize size;
303 gsize bytes_written;
304 GladeWidget *gwidget;
305
306 g_return_if_fail (GLADE_IS_PREVIEW (preview));
307 g_return_if_fail (buffer && buffer[0]);
308
309 gwidget = glade_preview_get_widget (preview);
310
311 update = g_strdup_printf ("%s%s\n", update_token,
312 glade_widget_get_name (gwidget));
313
314 channel = preview->priv->channel;
315 g_io_channel_write_chars (channel, update, strlen (update), &size, &error);
316
317 if (size != strlen (update) && error != NULL)
318 {
319 g_warning ("Error passing quit signal trough pipe: %s", error->message);
320 g_error_free (error);
321 }
322
323 g_io_channel_flush (channel, &error);
324 if (error != NULL)
325 {
326 g_warning ("Error flushing channel: %s", error->message);
327 g_error_free (error);
328 }
329
330 /* We'll now send the interface: */
331 g_io_channel_write_chars (channel, buffer, strlen (buffer), &bytes_written,
332 &error);
333
334 if (bytes_written != strlen (buffer) && error != NULL)
335 {
336 g_warning ("Error passing UI trough pipe: %s", error->message);
337 g_error_free (error);
338 }
339
340 g_io_channel_flush (channel, &error);
341 if (error != NULL)
342 {
343 g_warning ("Error flushing UI trough pipe: %s", error->message);
344 g_error_free (error);
345 }
346
347 g_free (update);
348 }
349
350 GladeWidget *
glade_preview_get_widget(GladePreview * preview)351 glade_preview_get_widget (GladePreview *preview)
352 {
353 g_return_val_if_fail (GLADE_IS_PREVIEW (preview), NULL);
354 return preview->priv->previewed_widget;
355 }
356
357 GPid
glade_preview_get_pid(GladePreview * preview)358 glade_preview_get_pid (GladePreview *preview)
359 {
360 g_return_val_if_fail (GLADE_IS_PREVIEW (preview), 0);
361 return preview->priv->pid;
362 }
363