You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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);
}