Commit aadad237 authored by Jasper St. Pierre's avatar Jasper St. Pierre

gsm: Add a new system to get information and frob Shell Extensions

This functionality will be used to allow the user to enable and disable GNOME
Shell Extensions in the fail whale dialog after a crash.

https://bugzilla.gnome.org/show_bug.cgi?id=656747
parent f41c07f6
......@@ -36,6 +36,7 @@ GLIB_REQUIRED=2.28.0
GTK3_REQUIRED=2.90.7
DBUS_GLIB_REQUIRED=0.76
UPOWER_REQUIRED=0.9.0
JSON_GLIB_REQUIRED=0.10
dnl ====================================================================
dnl Dependency Checks
......@@ -51,6 +52,7 @@ PKG_CHECK_MODULES(GNOME_SESSION,
gtk+-3.0 >= $GTK3_REQUIRED
dbus-glib-1 >= $DBUS_GLIB_REQUIRED
upower-glib >= $UPOWER_REQUIRED
json-glib-1.0 >= $JSON_GLIB_REQUIRED
)
PKG_CHECK_MODULES(SESSION_PROPERTIES,
......
......@@ -58,6 +58,8 @@ gnome_session_SOURCES = \
gsm-session-fill.h \
gsm-session-save.c \
gsm-session-save.h \
gsm-shell-extensions.c \
gsm-shell-extensions.h \
gsm-shell.c \
gsm-shell.h \
gsm-xsmp-server.c \
......
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2011 Red Hat, 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, 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 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.
*
* Authors:
* Jasper St. Pierre <jstpierre@mecheye.net>
*/
#include <glib.h>
#include <gio/gio.h>
#include <json-glib/json-glib.h>
#include "gsm-shell-extensions.h"
struct _GsmShellExtension
{
gchar *uuid;
gchar *name;
gchar *description;
gboolean enabled;
};
static void
gsm_shell_extension_free (gpointer data)
{
GsmShellExtension *extension = (GsmShellExtension *)data;
g_free (extension->uuid);
g_free (extension->name);
g_free (extension->description);
}
gchar *
gsm_shell_extension_get_uuid (GsmShellExtension *extension)
{
return extension->uuid;
}
gchar *
gsm_shell_extension_get_name (GsmShellExtension *extension)
{
return extension->name;
}
gchar *
gsm_shell_extension_get_description (GsmShellExtension *extension)
{
return extension->description;
}
gboolean
gsm_shell_extension_get_is_enabled (GsmShellExtension *extension)
{
return extension->enabled;
}
#define SHELL_SCHEMA "org.gnome.shell"
#define ENABLED_EXTENSIONS_KEY "enabled-extensions"
#define SHELL_EXTENSIONS_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsPrivate))
struct _GsmShellExtensionsPrivate
{
GSettings *settings;
/* uuid => GsmShellExtension */
GHashTable *uuid_to_extension;
};
G_DEFINE_TYPE (GsmShellExtensions, gsm_shell_extensions, G_TYPE_OBJECT);
/**
* gsm_shell_extensions_finalize:
* @object: (in): A #GsmShellExtensions.
*
* Finalizer for a #GsmShellExtensions instance. Frees any resources held by
* the instance.
*/
static void
gsm_shell_extensions_finalize (GObject *object)
{
GsmShellExtensions *extensions = GSM_SHELL_EXTENSIONS (object);
GsmShellExtensionsPrivate *priv = extensions->priv;
if (priv->settings != NULL)
{
g_object_unref (priv->settings);
priv->settings = NULL;
}
if (priv->uuid_to_extension != NULL)
{
g_hash_table_unref (priv->uuid_to_extension);
priv->uuid_to_extension = NULL;
}
G_OBJECT_CLASS (gsm_shell_extensions_parent_class)->finalize (object);
}
/**
* gsm_shell_extensions_class_init:
* @klass: (in): A #GsmShellExtensionsClass.
*
* Initializes the #GsmShellExtensionsClass and prepares the vtable.
*/
static void
gsm_shell_extensions_class_init (GsmShellExtensionsClass *klass)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsm_shell_extensions_finalize;
g_type_class_add_private (object_class, sizeof (GsmShellExtensionsPrivate));
}
static void
gsm_shell_extensions_fetch_enabled (GsmShellExtensions *self)
{
gchar **enabled_uuids;
gchar **uuids;
enabled_uuids = g_settings_get_strv (self->priv->settings, ENABLED_EXTENSIONS_KEY);
uuids = enabled_uuids;
while (*uuids != '\0')
{
GsmShellExtension *extension;
extension = g_hash_table_lookup (self->priv->uuid_to_extension, *uuids);
if (extension != NULL)
extension->enabled = TRUE;
uuids ++;
}
g_strfreev (enabled_uuids);
}
static void
gsm_shell_extensions_scan_dir (GsmShellExtensions *self,
GFile *dir)
{
GFileEnumerator *enumerator;
GFileInfo *info;
JsonParser *metadata_parser;
metadata_parser = json_parser_new ();
enumerator = g_file_enumerate_children (dir,
"standard::*",
G_FILE_QUERY_INFO_NONE,
NULL,
NULL);
if (enumerator == NULL)
return;
while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
{
GsmShellExtension *extension = g_slice_new (GsmShellExtension);
gchar *metadata_filename;
gchar *metadata_uuid;
JsonObject *metadata_root;
extension->uuid = (char *) g_file_info_get_name (info);
metadata_filename = g_build_filename (g_file_get_path (dir),
extension->uuid,
"metadata.json",
NULL);
if (!json_parser_load_from_file (metadata_parser, metadata_filename, NULL))
continue;
g_free (metadata_filename);
metadata_root = json_node_get_object (json_parser_get_root (metadata_parser));
metadata_uuid = g_strdup (json_object_get_string_member (metadata_root, "uuid"));
if (!g_str_equal (metadata_uuid, extension->uuid))
{
g_warning ("Extension with dirname '%s' does not match metadata's UUID of '%s'. Skipping.",
extension->uuid,
metadata_uuid);
continue;
}
extension->enabled = FALSE;
extension->name = g_strdup (json_object_get_string_member (metadata_root, "name"));
extension->description = g_strdup (json_object_get_string_member (metadata_root, "description"));
g_hash_table_insert (self->priv->uuid_to_extension,
g_strdup (extension->uuid), extension);
}
}
static void
gsm_shell_extensions_scan (GsmShellExtensions *self)
{
gchar *dirname;
GFile *dir;
const gchar * const * system_data_dirs;
/* User data dir first. */
dirname = g_build_filename (g_get_user_data_dir (), "gnome-shell", "extensions", NULL);
dir = g_file_new_for_path (dirname);
g_free (dirname);
gsm_shell_extensions_scan_dir (self, dir);
g_object_unref (dir);
system_data_dirs = g_get_system_data_dirs ();
while ((*system_data_dirs) != '\0')
{
dirname = g_build_filename (*system_data_dirs, "gnome-shell", "extensions", NULL);
dir = g_file_new_for_path (dirname);
g_free (dirname);
gsm_shell_extensions_scan_dir (self, dir);
g_object_unref (dir);
system_data_dirs ++;
}
}
/**
* gsm_shell_extensions_init:
* @self: (in): A #GsmShellExtensions.
*
* Initializes the newly created #GsmShellExtensions instance.
*/
static void
gsm_shell_extensions_init (GsmShellExtensions *self)
{
gchar * const * schemas;
self->priv = SHELL_EXTENSIONS_PRIVATE (self);
/* Unfortunately, gsettings does not have a way to test
* for the existance of a schema, so hack around it. */
schemas = g_settings_list_schemas ();
while (schemas != '\0')
{
if (g_str_equal (*schemas, SHELL_SCHEMA))
{
self->priv->settings = g_settings_new (SHELL_SCHEMA);
break;
}
schemas ++;
}
if (self->priv->settings)
{
self->priv->uuid_to_extension = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
gsm_shell_extension_free);
gsm_shell_extensions_scan (self);
gsm_shell_extensions_fetch_enabled (self);
}
}
gboolean
gsm_shell_extensions_set_enabled (GsmShellExtensions *self,
gchar *uuid,
gboolean enabled)
{
gsize i, length;
gchar **uuids;
const gchar **new_uuids;
GsmShellExtension *extension;
if (self->priv->settings == NULL)
return FALSE;
extension = g_hash_table_lookup (self->priv->uuid_to_extension,
uuid);
if (extension == NULL)
return FALSE;
if (extension->enabled == enabled)
return TRUE;
uuids = g_settings_get_strv (self->priv->settings, ENABLED_EXTENSIONS_KEY);
length = g_strv_length (uuids);
if (enabled)
{
new_uuids = g_new (const gchar *, length + 2); /* New key, NULL */
for (i = 0; i < length; i ++)
new_uuids[i] = g_strdup (uuids[i]);
new_uuids[i++] = g_strdup (uuid);
new_uuids[i] = NULL;
}
else
{
gsize j = 0;
new_uuids = g_new (const gchar *, length);
for (i = 0; i < length; i ++)
{
if (g_str_equal (uuids[i], uuid))
continue;
new_uuids[j] = g_strdup (uuids[i]);
j ++;
}
new_uuids[j] = NULL;
}
g_strfreev (uuids);
if (g_settings_set_strv (self->priv->settings,
ENABLED_EXTENSIONS_KEY,
new_uuids))
{
extension->enabled = enabled;
return TRUE;
}
return FALSE;
}
void
gsm_shell_extensions_foreach (GsmShellExtensions *self,
GsmShellExtensionFunc func,
gpointer user_data)
{
GHashTableIter iter;
GsmShellExtension *extension;
if (self->priv->settings == NULL)
return;
g_hash_table_iter_init (&iter, self->priv->uuid_to_extension);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &extension))
(*func) (self, extension, user_data);
}
GsmShellExtension *
gsm_shell_extensions_get_for_uuid (GsmShellExtensions *self,
gchar *uuid)
{
if (self->priv->settings == NULL)
return NULL;
return g_hash_table_lookup (self->priv->uuid_to_extension, uuid);
}
guint
gsm_shell_extensions_n_extensions (GsmShellExtensions *self)
{
if (self->priv->settings == NULL)
return 0;
return g_hash_table_size (self->priv->uuid_to_extension);
}
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2011 Red Hat, 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, 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 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.
*
* Authors:
* Jasper St. Pierre <jstpierre@mecheye.net>
*/
#ifndef __GSM_SHELL_EXTENSIONS_H
#define __GSM_SHELL_EXTENSIONS_H
#include <glib-object.h>
G_BEGIN_DECLS
/* Opaque struct to represent one shell extension. */
typedef struct _GsmShellExtension GsmShellExtension;
gchar * gsm_shell_extension_get_uuid (GsmShellExtension *extension);
gchar * gsm_shell_extension_get_name (GsmShellExtension *extension);
gchar * gsm_shell_extension_get_description (GsmShellExtension *extension);
gboolean gsm_shell_extension_get_is_enabled (GsmShellExtension *extension);
#define GSM_TYPE_SHELL_EXTENSIONS (gsm_shell_extensions_get_type ())
#define GSM_SHELL_EXTENSIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensions))
#define GSM_SHELL_EXTENSIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsClass))
#define GSM_IS_SHELL_EXTENSIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SHELL_EXTENSIONS))
#define GSM_IS_SHELL_EXTENSIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_SHELL_EXTENSIONS))
#define GSM_SHELL_EXTENSIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_SHELL_EXTENSIONS, GsmShellExtensionsClass))
typedef struct _GsmShellExtensions GsmShellExtensions;
typedef struct _GsmShellExtensionsClass GsmShellExtensionsClass;
typedef struct _GsmShellExtensionsPrivate GsmShellExtensionsPrivate;
typedef void (* GsmShellExtensionFunc) (GsmShellExtensions *extensions,
GsmShellExtension *extension,
gpointer user_data);
struct _GsmShellExtensions
{
GObject parent;
/*< private >*/
GsmShellExtensionsPrivate *priv;
};
struct _GsmShellExtensionsClass
{
GObjectClass parent_class;
};
GType gsm_shell_extensions_get_type (void) G_GNUC_CONST;
gboolean gsm_shell_extensions_set_enabled (GsmShellExtensions *self,
gchar *uuid,
gboolean value);
void gsm_shell_extensions_foreach (GsmShellExtensions *self,
GsmShellExtensionFunc func,
gpointer user_data);
guint gsm_shell_extensions_n_extensions (GsmShellExtensions *self);
GsmShellExtension * gsm_shell_extensions_get_for_uuid (GsmShellExtensions *self,
gchar *uuid);
G_END_DECLS
#endif /* __GSM_SHELL_EXTENSIONS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment