
The old gtk-2 based module will be present in the 0.94 branch in the sugar-toolkit. Signed-off-by: Simon Schampijer <simon@laptop.org> Acked-by: Sascha Silbe <silbe@activitycentral.com>
397 lines
8.7 KiB
C
397 lines
8.7 KiB
C
/* 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);
|
|
}
|
|
|