aboutsummaryrefslogtreecommitdiffstats
path: root/applets/launcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'applets/launcher.c')
-rw-r--r--applets/launcher.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/applets/launcher.c b/applets/launcher.c
new file mode 100644
index 0000000..aa5912b
--- /dev/null
+++ b/applets/launcher.c
@@ -0,0 +1,367 @@
+/*
+ * (C) 2006 OpenedHand Ltd.
+ *
+ * Author: Jorn Baayen <jorn@openedhand.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkbutton.h>
+#include <gdk/gdkx.h>
+#include <string.h>
+#include <unistd.h>
+#include <matchbox-panel/mb-panel.h>
+
+#ifdef USE_LIBSN
+ #define SN_API_NOT_YET_FROZEN 1
+ #include <libsn/sn.h>
+#endif
+
+typedef struct {
+ gboolean use_sn;
+
+ char *name;
+ char **argv;
+} LauncherData;
+
+static void
+launcher_data_free (LauncherData *data)
+{
+ g_free (data->name);
+ g_strfreev (data->argv);
+
+ g_slice_free (LauncherData, data);
+}
+
+#define MAX_ARGS 255
+
+/* Convert command line to argv array, stripping % conversions on the way */
+static char **
+exec_to_argv (const char *exec)
+{
+ const char *p;
+ char *buf, *bufp, **argv;
+ int nargs;
+ gboolean escape, single_quote, double_quote;
+
+ argv = g_new (char *, MAX_ARGS + 1);
+ buf = g_alloca (strlen (exec) + 1);
+ bufp = buf;
+ nargs = 0;
+ escape = single_quote = double_quote = FALSE;
+
+ for (p = exec; *p; p++) {
+ if (escape) {
+ *bufp++ = *p;
+
+ escape = FALSE;
+ } else {
+ switch (*p) {
+ case '\\':
+ escape = TRUE;
+
+ break;
+ case '%':
+ /* Strip '%' conversions */
+ if (p[1] && p[1] == '%')
+ *bufp++ = *p;
+
+ p++;
+
+ break;
+ case '\'':
+ if (double_quote)
+ *bufp++ = *p;
+ else
+ single_quote = !single_quote;
+
+ break;
+ case '\"':
+ if (single_quote)
+ *bufp++ = *p;
+ else
+ double_quote = !double_quote;
+
+ break;
+ case ' ':
+ if (single_quote || double_quote)
+ *bufp++ = *p;
+ else {
+ *bufp = 0;
+
+ if (nargs < MAX_ARGS)
+ argv[nargs++] = g_strdup (buf);
+
+ bufp = buf;
+ }
+
+ break;
+ default:
+ *bufp++ = *p;
+ break;
+ }
+ }
+ }
+
+ if (bufp != buf) {
+ *bufp = 0;
+
+ if (nargs < MAX_ARGS)
+ argv[nargs++] = g_strdup (buf);
+ }
+
+ argv[nargs] = NULL;
+
+ return argv;
+}
+
+/* Button clicked */
+static void
+clicked_cb (GtkButton *button,
+ LauncherData *data)
+{
+ pid_t child_pid = 0;
+#ifdef USE_LIBSN
+ SnLauncherContext *context;
+
+ context = NULL;
+
+ if (data->use_sn) {
+ SnDisplay *sn_dpy;
+ Display *display;
+ int screen;
+
+ display = gdk_x11_display_get_xdisplay
+ (gtk_widget_get_display (GTK_WIDGET (button)));
+ sn_dpy = sn_display_new (display, NULL, NULL);
+
+ screen = gdk_screen_get_number
+ (gtk_widget_get_screen (GTK_WIDGET (button)));
+ context = sn_launcher_context_new (sn_dpy, screen);
+ sn_display_unref (sn_dpy);
+
+ sn_launcher_context_set_name (context, data->name);
+ sn_launcher_context_set_binary_name (context,
+ data->argv[0]);
+
+ sn_launcher_context_initiate (context,
+ "matchbox-panel",
+ data->argv[0],
+ CurrentTime);
+ }
+#endif
+
+ switch ((child_pid = fork ())) {
+ case -1:
+ g_warning ("Fork failed");
+ break;
+ case 0:
+#ifdef USE_LIBSN
+ if (data->use_sn)
+ sn_launcher_context_setup_child_process (context);
+#endif
+ execvp (data->argv[0], data->argv);
+
+ g_warning ("Failed to execvp() %s", data->argv[0]);
+ _exit (1);
+
+ break;
+ }
+
+#ifdef USE_LIBSN
+ if (data->use_sn)
+ sn_launcher_context_unref (context);
+#endif
+}
+
+G_MODULE_EXPORT GtkWidget *
+mb_panel_applet_create (const char *id,
+ int panel_width,
+ int panel_height)
+{
+ char *filename;
+ GKeyFile *key_file;
+ GtkWidget *button, *image;
+ GError *error;
+ char *icon, *exec, *name;
+ gboolean use_sn;
+ int shortest_side;
+ LauncherData *data;
+
+ /* Try to find a .desktop file for @id */
+ key_file = g_key_file_new ();
+
+ filename = g_strdup_printf ("applications/%s.desktop", id);
+ error = NULL;
+ if (!g_key_file_load_from_data_dirs (key_file,
+ filename,
+ NULL,
+ G_KEY_FILE_NONE,
+ &error)) {
+ /* An error occured */
+ g_warning ("%s", error->message);
+
+ g_error_free (error);
+
+ g_free (filename);
+ g_key_file_free (key_file);
+
+ return NULL;
+ }
+
+ g_free (filename);
+
+ /* Found and opened keyfile. Read the values we want. */
+
+ /* Icon */
+ error = NULL;
+ icon = g_key_file_get_string (key_file,
+ "Desktop Entry",
+ "Icon",
+ &error);
+ if (!icon) {
+ /* An error occured */
+ g_warning ("%s", error->message);
+
+ g_error_free (error);
+
+ g_key_file_free (key_file);
+
+ return NULL;
+ }
+
+ icon = g_strstrip (icon);
+
+ /* Exec */
+ error = NULL;
+ exec = g_key_file_get_string (key_file,
+ "Desktop Entry",
+ "Exec",
+ &error);
+ if (!exec) {
+ /* An error occured */
+ g_warning ("%s", error->message);
+
+ g_error_free (error);
+
+ g_free (icon);
+ g_key_file_free (key_file);
+
+ return NULL;
+ }
+
+ exec = g_strstrip (exec);
+
+ /* Name */
+ name = g_key_file_get_string (key_file,
+ "Desktop Entry",
+ "Name",
+ NULL);
+
+ /* StartupNotify */
+ use_sn = g_key_file_get_boolean (key_file,
+ "Desktop Entry",
+ "StartupNotify",
+ NULL);
+
+ /* Close key file */
+ g_key_file_free (key_file);
+
+ /* Create image widget */
+ image = gtk_image_new ();
+
+ /* Load icon */
+ shortest_side = MIN (panel_width, panel_height);
+
+ if (icon[0] == '/') {
+ /* Absolute path specified: load icon directly */
+ GdkPixbuf *pixbuf;
+
+ error = NULL;
+ pixbuf = gdk_pixbuf_new_from_file_at_scale (icon,
+ shortest_side,
+ shortest_side,
+ TRUE,
+ &error);
+ if (!pixbuf) {
+ /* An error occured */
+ g_warning ("%s", error->message);
+
+ g_error_free (error);
+
+ g_free (icon);
+ g_free (exec);
+ g_free (name);
+ gtk_widget_destroy (image);
+
+ return NULL;
+ }
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
+ g_object_unref (pixbuf);
+ } else {
+ /* No absolute path given. Load icon from icon theme */
+ char *p;
+
+ /* Strip extension, so that we can feed the filename to
+ * GTK+'s icon name handling. */
+ p = strrchr (icon, '.');
+ if (p)
+ *p = 0;
+
+ gtk_image_set_pixel_size (GTK_IMAGE (image), shortest_side);
+ gtk_image_set_from_icon_name (GTK_IMAGE (image),
+ icon,
+ 0);
+ }
+
+ g_free (icon);
+
+ /* Create button */
+ button = gtk_button_new ();
+
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
+
+ /* Strip %-conversions from exec */
+
+ /* Connect to "clicked" signal */
+ data = g_slice_new (LauncherData);
+
+ data->use_sn = use_sn;
+
+ data->name = name;
+
+ data->argv = exec_to_argv (exec);
+ g_free (exec);
+
+ g_signal_connect_data (button,
+ "clicked",
+ G_CALLBACK (clicked_cb),
+ data,
+ (GClosureNotify) launcher_data_free,
+ 0);
+
+ /* Add image to button */
+ gtk_container_add (GTK_CONTAINER (button), image);
+
+ /* Show! */
+ gtk_widget_show_all (button);
+
+ return button;
+};