Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar/gsm-app.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sugar/gsm-app.c')
-rw-r--r--src/sugar/gsm-app.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/src/sugar/gsm-app.c b/src/sugar/gsm-app.c
new file mode 100644
index 0000000..3a63bac
--- /dev/null
+++ b/src/sugar/gsm-app.c
@@ -0,0 +1,396 @@
+/* app.c
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "gsm-app.h"
+
+enum {
+ EXITED,
+ REGISTERED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+
+ PROP_DESKTOP_FILE,
+ PROP_CLIENT_ID,
+
+ LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec);
+static void dispose (GObject *object);
+
+static const char *get_basename (GsmApp *app);
+static pid_t launch (GsmApp *app, GError **err);
+
+G_DEFINE_TYPE (GsmApp, gsm_app, G_TYPE_OBJECT)
+
+static void
+gsm_app_init (GsmApp *app)
+{
+ app->pid = -1;
+}
+
+static void
+gsm_app_class_init (GsmAppClass *app_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (app_class);
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+
+ app_class->get_basename = get_basename;
+ app_class->launch = launch;
+
+ g_object_class_install_property (object_class,
+ PROP_DESKTOP_FILE,
+ g_param_spec_string ("desktop-file",
+ "Desktop file",
+ "Freedesktop .desktop file",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_CLIENT_ID,
+ g_param_spec_string ("client-id",
+ "Client ID",
+ "Session management client ID",
+ NULL,
+ G_PARAM_READWRITE));
+
+ signals[EXITED] =
+ g_signal_new ("exited",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmAppClass, exited),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals[REGISTERED] =
+ g_signal_new ("registered",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsmAppClass, registered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ GsmApp *app = GSM_APP (object);
+ const char *desktop_file;
+ char *phase;
+ GError *error = NULL;
+
+ switch (prop_id)
+ {
+ case PROP_DESKTOP_FILE:
+ if (app->desktop_file)
+ egg_desktop_file_free (app->desktop_file);
+ desktop_file = g_value_get_string (value);
+ if (!desktop_file)
+ {
+ app->desktop_file = NULL;
+ break;
+ }
+
+ app->desktop_file = egg_desktop_file_new (desktop_file, &error);
+ if (!app->desktop_file)
+ {
+ g_warning ("Could not parse desktop file %s: %s",
+ desktop_file, error->message);
+ g_error_free (error);
+ break;
+ }
+
+ phase = egg_desktop_file_get_string (app->desktop_file,
+ "X-GNOME-Autostart-Phase", NULL);
+ if (phase)
+ {
+ if (!strcmp (phase, "Initialization"))
+ app->phase = GSM_SESSION_PHASE_INITIALIZATION;
+ else if (!strcmp (phase, "WindowManager"))
+ app->phase = GSM_SESSION_PHASE_WINDOW_MANAGER;
+ else if (!strcmp (phase, "Panel"))
+ app->phase = GSM_SESSION_PHASE_PANEL;
+ else if (!strcmp (phase, "Desktop"))
+ app->phase = GSM_SESSION_PHASE_DESKTOP;
+ else
+ app->phase = GSM_SESSION_PHASE_APPLICATION;
+
+ g_free (phase);
+ }
+ else
+ app->phase = GSM_SESSION_PHASE_APPLICATION;
+ break;
+
+ case PROP_CLIENT_ID:
+ g_free (app->client_id);
+ app->client_id = g_value_dup_string (value);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GsmApp *app = GSM_APP (object);
+
+ switch (prop_id)
+ {
+ case PROP_DESKTOP_FILE:
+ if (app->desktop_file)
+ g_value_set_string (value, egg_desktop_file_get_source (app->desktop_file));
+ else
+ g_value_set_string (value, NULL);
+ break;
+
+ case PROP_CLIENT_ID:
+ g_value_set_string (value, app->client_id);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+dispose(GObject *object)
+{
+ GsmApp *app = GSM_APP (object);
+
+ if (app->desktop_file)
+ {
+ egg_desktop_file_free (app->desktop_file);
+ app->desktop_file = NULL;
+ }
+
+ if (app->startup_id)
+ {
+ g_free (app->startup_id);
+ app->startup_id = NULL;
+ }
+
+ if (app->client_id)
+ {
+ g_free (app->client_id);
+ app->client_id = NULL;
+ }
+}
+
+/**
+ * gsm_app_get_basename:
+ * @app: a %GsmApp
+ *
+ * Returns an identifying name for @app, e.g. the basename of the path to
+ * @app's desktop file (if any).
+ *
+ * Return value: an identifying name for @app, or %NULL.
+ **/
+const char *
+gsm_app_get_basename (GsmApp *app)
+{
+ return GSM_APP_GET_CLASS (app)->get_basename (app);
+}
+
+static const char *
+get_basename (GsmApp *app)
+{
+ const char *location, *slash;
+
+ if (!app->desktop_file)
+ return NULL;
+
+ location = egg_desktop_file_get_source (app->desktop_file);
+
+ slash = strrchr (location, '/');
+ if (slash)
+ return slash + 1;
+ else
+ return location;
+}
+
+/**
+ * gsm_app_get_phase:
+ * @app: a %GsmApp
+ *
+ * Returns @app's startup phase.
+ *
+ * Return value: @app's startup phase
+ **/
+GsmSessionPhase
+gsm_app_get_phase (GsmApp *app)
+{
+ g_return_val_if_fail (GSM_IS_APP (app), GSM_SESSION_PHASE_APPLICATION);
+
+ return app->phase;
+}
+
+/**
+ * gsm_app_is_disabled:
+ * @app: a %GsmApp
+ *
+ * Tests if @app is disabled
+ *
+ * Return value: whether or not @app is disabled
+ **/
+gboolean
+gsm_app_is_disabled (GsmApp *app)
+{
+ g_return_val_if_fail (GSM_IS_APP (app), FALSE);
+
+ if (GSM_APP_GET_CLASS (app)->is_disabled)
+ return GSM_APP_GET_CLASS (app)->is_disabled (app);
+ else
+ return FALSE;
+}
+
+gboolean
+gsm_app_provides (GsmApp *app, const char *service)
+{
+ char **provides;
+ gsize len, i;
+
+ g_return_val_if_fail (GSM_IS_APP (app), FALSE);
+
+ if (!app->desktop_file)
+ return FALSE;
+
+ provides = egg_desktop_file_get_string_list (app->desktop_file,
+ "X-GNOME-Provides",
+ &len, NULL);
+ if (!provides)
+ return FALSE;
+
+ for (i = 0; i < len; i++)
+ {
+ if (!strcmp (provides[i], service))
+ {
+ g_strfreev (provides);
+ return TRUE;
+ }
+ }
+
+ g_strfreev (provides);
+ return FALSE;
+}
+
+static void
+app_exited (GPid pid, gint status, gpointer data)
+{
+ if (WIFEXITED (status))
+ g_signal_emit (GSM_APP (data), signals[EXITED], 0);
+}
+
+static pid_t
+launch (GsmApp *app,
+ GError **err)
+{
+ char *env[2] = { NULL, NULL };
+ gboolean success;
+
+ g_return_val_if_fail (app->desktop_file != NULL, (pid_t)-1);
+
+ if (egg_desktop_file_get_boolean (app->desktop_file,
+ "X-GNOME-Autostart-Notify", NULL) ||
+ egg_desktop_file_get_boolean (app->desktop_file,
+ "AutostartNotify", NULL))
+ env[0] = g_strdup_printf ("DESKTOP_AUTOSTART_ID=%s", app->client_id);
+
+#if 0
+ g_debug ("launching %s with client_id %s\n",
+ gsm_app_get_basename (app), app->client_id);
+#endif
+
+ success =
+ egg_desktop_file_launch (app->desktop_file, NULL, err,
+ EGG_DESKTOP_FILE_LAUNCH_PUTENV, env,
+ EGG_DESKTOP_FILE_LAUNCH_FLAGS, G_SPAWN_DO_NOT_REAP_CHILD,
+ EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, &app->pid,
+ EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID, &app->startup_id,
+ NULL);
+
+ g_free (env[0]);
+
+ if (success)
+ {
+ /* In case the app belongs to Initialization phase, we monitor
+ * if it exits to emit proper "exited" signal to session. */
+ if (app->phase == GSM_SESSION_PHASE_INITIALIZATION)
+ g_child_watch_add ((GPid) app->pid, app_exited, app);
+
+ return app->pid;
+ }
+ else
+ return (pid_t) -1;
+}
+
+/**
+ * gsm_app_launch:
+ * @app: a %GsmApp
+ * @err: an error pointer
+ *
+ * Launches @app
+ *
+ * Return value: the pid of the new process, or -1 on error
+ **/
+pid_t
+gsm_app_launch (GsmApp *app, GError **err)
+{
+ return GSM_APP_GET_CLASS (app)->launch (app, err);
+}
+
+/**
+ * gsm_app_registered:
+ * @app: a %GsmApp
+ *
+ * Emits "registered" signal in @app
+ **/
+void
+gsm_app_registered (GsmApp *app)
+{
+ g_return_if_fail (GSM_IS_APP (app));
+
+ g_signal_emit (app, signals[REGISTERED], 0);
+}
+