In the case where "nomodeset" is given to Linux, or we don't have a driver, we'll end up in VESA in most cases. So handle this by parsing the XOrg log, and extract from it what chipset VESA is driving. This is again better than lspci because it corresponds better to what the X server is doing.
1241 lines
34 KiB
C
1241 lines
34 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
|
*
|
|
* Copyright (C) 2010 Red Hat, Inc
|
|
* Copyright (C) 2008 William Jon McCann <jmccann@redhat.com>
|
|
*
|
|
* 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 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.
|
|
*
|
|
*/
|
|
|
|
#include "cc-info-panel.h"
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <gio/gio.h>
|
|
#include <gio/gunixmounts.h>
|
|
|
|
#include <glibtop/fsusage.h>
|
|
#include <glibtop/mountlist.h>
|
|
#include <glibtop/mem.h>
|
|
#include <glibtop/sysinfo.h>
|
|
|
|
#define GNOME_SESSION_MANAGER_SCHEMA "org.gnome.desktop.session"
|
|
#define KEY_SESSION_NAME "session-name"
|
|
|
|
#define WID(b, w) (GtkWidget *) gtk_builder_get_object (b, w)
|
|
|
|
G_DEFINE_DYNAMIC_TYPE (CcInfoPanel, cc_info_panel, CC_TYPE_PANEL)
|
|
|
|
#define INFO_PANEL_PRIVATE(o) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_INFO_PANEL, CcInfoPanelPrivate))
|
|
|
|
typedef struct {
|
|
/* Will be one of the other two below, or "Unknown" */
|
|
const char *hardware_string;
|
|
|
|
char *xorg_vesa_hardware;
|
|
char *glx_renderer;
|
|
} GraphicsData;
|
|
|
|
struct _CcInfoPanelPrivate
|
|
{
|
|
GtkBuilder *builder;
|
|
char *gnome_version;
|
|
char *gnome_distributor;
|
|
char *gnome_date;
|
|
gboolean updates_available;
|
|
gboolean is_fallback;
|
|
|
|
GDBusConnection *session_bus;
|
|
GDBusProxy *pk_proxy;
|
|
GDBusProxy *pk_transaction_proxy;
|
|
GSettings *session_settings;
|
|
|
|
GraphicsData *graphics_data;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
char *major;
|
|
char *minor;
|
|
char *micro;
|
|
char *distributor;
|
|
char *date;
|
|
char **current;
|
|
} VersionData;
|
|
|
|
static void
|
|
version_start_element_handler (GMarkupParseContext *ctx,
|
|
const char *element_name,
|
|
const char **attr_names,
|
|
const char **attr_values,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
VersionData *data = user_data;
|
|
if (g_str_equal (element_name, "platform"))
|
|
data->current = &data->major;
|
|
else if (g_str_equal (element_name, "minor"))
|
|
data->current = &data->minor;
|
|
else if (g_str_equal (element_name, "micro"))
|
|
data->current = &data->micro;
|
|
else if (g_str_equal (element_name, "distributor"))
|
|
data->current = &data->distributor;
|
|
else if (g_str_equal (element_name, "date"))
|
|
data->current = &data->date;
|
|
else
|
|
data->current = NULL;
|
|
}
|
|
|
|
static void
|
|
version_end_element_handler (GMarkupParseContext *ctx,
|
|
const char *element_name,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
VersionData *data = user_data;
|
|
data->current = NULL;
|
|
}
|
|
|
|
static void
|
|
version_text_handler (GMarkupParseContext *ctx,
|
|
const char *text,
|
|
gsize text_len,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
VersionData *data = user_data;
|
|
if (data->current != NULL)
|
|
*data->current = g_strstrip (g_strdup (text));
|
|
}
|
|
|
|
static gboolean
|
|
load_gnome_version (char **version,
|
|
char **distributor,
|
|
char **date)
|
|
{
|
|
GMarkupParser version_parser = {
|
|
version_start_element_handler,
|
|
version_end_element_handler,
|
|
version_text_handler,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
GError *error;
|
|
GMarkupParseContext *ctx;
|
|
char *contents;
|
|
gsize length;
|
|
VersionData *data;
|
|
gboolean ret;
|
|
|
|
ret = FALSE;
|
|
|
|
error = NULL;
|
|
if (!g_file_get_contents (DATADIR "/gnome/gnome-version.xml",
|
|
&contents,
|
|
&length,
|
|
&error))
|
|
return FALSE;
|
|
|
|
data = g_new0 (VersionData, 1);
|
|
ctx = g_markup_parse_context_new (&version_parser, 0, data, NULL);
|
|
|
|
if (!g_markup_parse_context_parse (ctx, contents, length, &error))
|
|
{
|
|
g_warning ("Invalid version file: '%s'", error->message);
|
|
}
|
|
else
|
|
{
|
|
if (version != NULL)
|
|
*version = g_strdup_printf ("%s.%s.%s", data->major, data->minor, data->micro);
|
|
if (distributor != NULL)
|
|
*distributor = g_strdup (data->distributor);
|
|
if (date != NULL)
|
|
*date = g_strdup (data->date);
|
|
|
|
ret = TRUE;
|
|
}
|
|
|
|
g_markup_parse_context_free (ctx);
|
|
g_free (data->major);
|
|
g_free (data->minor);
|
|
g_free (data->micro);
|
|
g_free (data->distributor);
|
|
g_free (data->date);
|
|
g_free (data);
|
|
g_free (contents);
|
|
|
|
return ret;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
char *regex;
|
|
char *replacement;
|
|
} ReplaceStrings;
|
|
|
|
static char *
|
|
prettify_info (const char *info)
|
|
{
|
|
char *pretty;
|
|
int i;
|
|
static const ReplaceStrings rs[] = {
|
|
{ "Mesa DRI ", ""},
|
|
{ "Intel[(]R[)]", "Intel<sup>\302\256</sup>"},
|
|
{ "Core[(]TM[)]", "Core<sup>\342\204\242</sup>"},
|
|
{ "Atom[(]TM[)]", "Atom<sup>\342\204\242</sup>"},
|
|
{ "Graphics Controller", "Graphics"},
|
|
};
|
|
|
|
pretty = g_markup_escape_text (info, -1);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (rs); i++)
|
|
{
|
|
GError *error;
|
|
GRegex *re;
|
|
char *new;
|
|
|
|
error = NULL;
|
|
|
|
re = g_regex_new (rs[i].regex, 0, 0, &error);
|
|
if (re == NULL)
|
|
{
|
|
g_warning ("Error building regex: %s", error->message);
|
|
g_error_free (error);
|
|
continue;
|
|
}
|
|
|
|
new = g_regex_replace_literal (re,
|
|
pretty,
|
|
-1,
|
|
0,
|
|
rs[i].replacement,
|
|
0,
|
|
&error);
|
|
|
|
g_regex_unref (re);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_warning ("Error replacing %s: %s", rs[i].regex, error->message);
|
|
g_error_free (error);
|
|
continue;
|
|
}
|
|
|
|
g_free (pretty);
|
|
pretty = new;
|
|
}
|
|
|
|
return pretty;
|
|
}
|
|
|
|
static void
|
|
graphics_data_free (GraphicsData *gdata)
|
|
{
|
|
g_free (gdata->xorg_vesa_hardware);
|
|
g_free (gdata->glx_renderer);
|
|
g_slice_free (GraphicsData, gdata);
|
|
}
|
|
|
|
static char *
|
|
get_graphics_data_glx_renderer (void)
|
|
{
|
|
GError *error;
|
|
GRegex *re;
|
|
GMatchInfo *match_info;
|
|
char *output;
|
|
char *result;
|
|
GString *info;
|
|
|
|
info = g_string_new (NULL);
|
|
|
|
error = NULL;
|
|
g_spawn_command_line_sync ("glxinfo -l", &output, NULL, NULL, &error);
|
|
if (error != NULL)
|
|
{
|
|
g_warning ("Unable to get graphics info: %s", error->message);
|
|
g_error_free (error);
|
|
return NULL;
|
|
}
|
|
|
|
re = g_regex_new ("^OpenGL renderer string: (.+)$", G_REGEX_MULTILINE, 0, &error);
|
|
if (re == NULL)
|
|
{
|
|
g_warning ("Error building regex: %s", error->message);
|
|
g_error_free (error);
|
|
goto out;
|
|
}
|
|
|
|
g_regex_match (re, output, 0, &match_info);
|
|
while (g_match_info_matches (match_info))
|
|
{
|
|
char *device;
|
|
|
|
device = g_match_info_fetch (match_info, 1);
|
|
g_string_append_printf (info, "%s ", device);
|
|
g_free (device);
|
|
|
|
g_match_info_next (match_info, NULL);
|
|
}
|
|
g_match_info_free (match_info);
|
|
g_regex_unref (re);
|
|
|
|
out:
|
|
g_free (output);
|
|
result = prettify_info (info->str);
|
|
g_string_free (info, TRUE);
|
|
|
|
return result;
|
|
}
|
|
|
|
static char *
|
|
get_graphics_data_xorg_vesa_hardware (void)
|
|
{
|
|
char *display_num;
|
|
char *log_path;
|
|
char *log_contents;
|
|
gsize log_len;
|
|
GError *error = NULL;
|
|
GRegex *re;
|
|
GMatchInfo *match;
|
|
char *result = NULL;
|
|
|
|
{
|
|
const char *display;
|
|
|
|
display = g_getenv ("DISPLAY");
|
|
if (!display)
|
|
return NULL;
|
|
|
|
re = g_regex_new ("^:([0-9]+)", 0, 0, NULL);
|
|
g_assert (re != NULL);
|
|
|
|
g_regex_match (re, display, 0, &match);
|
|
|
|
if (!g_match_info_matches (match))
|
|
{
|
|
g_regex_unref (re);
|
|
g_match_info_free (match);
|
|
return NULL;
|
|
}
|
|
|
|
display_num = g_match_info_fetch (match, 1);
|
|
|
|
g_regex_unref (re);
|
|
re = NULL;
|
|
g_match_info_free (match);
|
|
match = NULL;
|
|
}
|
|
|
|
log_path = g_strdup_printf ("/var/log/Xorg.%s.log", display_num);
|
|
g_free (display_num);
|
|
log_contents = NULL;
|
|
g_file_get_contents (log_path, &log_contents, &log_len, &error);
|
|
g_free (log_path);
|
|
if (!log_contents)
|
|
return NULL;
|
|
|
|
re = g_regex_new ("VESA VBE OEM Product: (.*)$", G_REGEX_MULTILINE, 0, NULL);
|
|
g_assert (re != NULL);
|
|
|
|
g_regex_match (re, log_contents, 0, &match);
|
|
if (g_match_info_matches (match))
|
|
{
|
|
char *tmp;
|
|
char *pretty_tmp;
|
|
tmp = g_match_info_fetch (match, 1);
|
|
pretty_tmp = prettify_info (tmp);
|
|
g_free (tmp);
|
|
/* Translators: VESA is an techncial acronym, don't translate it. */
|
|
result = g_strdup_printf (_("VESA: %s"), pretty_tmp);
|
|
g_free (pretty_tmp);
|
|
}
|
|
g_match_info_free (match);
|
|
g_regex_unref (re);
|
|
|
|
return result;
|
|
}
|
|
|
|
static GraphicsData *
|
|
get_graphics_data (void)
|
|
{
|
|
GraphicsData *result;
|
|
|
|
result = g_slice_new0 (GraphicsData);
|
|
|
|
result->glx_renderer = get_graphics_data_glx_renderer ();
|
|
result->xorg_vesa_hardware = get_graphics_data_xorg_vesa_hardware ();
|
|
|
|
if (result->xorg_vesa_hardware != NULL)
|
|
result->hardware_string = result->xorg_vesa_hardware;
|
|
else if (result->glx_renderer != NULL)
|
|
result->hardware_string = result->glx_renderer;
|
|
else
|
|
result->hardware_string = _("Unknown");
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
get_current_is_fallback (CcInfoPanel *self)
|
|
{
|
|
GError *error;
|
|
GVariant *reply;
|
|
GVariant *reply_str;
|
|
gboolean is_fallback;
|
|
|
|
error = NULL;
|
|
if (!(reply = g_dbus_connection_call_sync (self->priv->session_bus,
|
|
"org.gnome.SessionManager",
|
|
"/org/gnome/SessionManager",
|
|
"org.freedesktop.DBus.Properties",
|
|
"Get",
|
|
g_variant_new ("(ss)", "org.gnome.SessionManager", "session-name"),
|
|
(GVariantType*)"(v)",
|
|
0,
|
|
-1,
|
|
NULL, &error)))
|
|
{
|
|
g_warning ("Failed to get fallback mode: %s", error->message);
|
|
g_clear_error (&error);
|
|
return FALSE;
|
|
}
|
|
|
|
g_variant_get (reply, "(v)", &reply_str);
|
|
is_fallback = g_strcmp0 ("gnome-fallback", g_variant_get_string (reply_str, NULL)) == 0;
|
|
g_variant_unref (reply_str);
|
|
g_variant_unref (reply);
|
|
|
|
return is_fallback;
|
|
}
|
|
|
|
static void
|
|
cc_info_panel_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id)
|
|
{
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_info_panel_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id)
|
|
{
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_info_panel_dispose (GObject *object)
|
|
{
|
|
CcInfoPanelPrivate *priv = CC_INFO_PANEL (object)->priv;
|
|
|
|
if (priv->builder != NULL)
|
|
{
|
|
g_object_unref (priv->builder);
|
|
priv->builder = NULL;
|
|
}
|
|
|
|
if (priv->pk_proxy != NULL)
|
|
{
|
|
g_object_unref (priv->pk_proxy);
|
|
priv->pk_proxy = NULL;
|
|
}
|
|
|
|
if (priv->pk_transaction_proxy != NULL)
|
|
{
|
|
g_object_unref (priv->pk_transaction_proxy);
|
|
priv->pk_transaction_proxy = NULL;
|
|
}
|
|
|
|
if (priv->graphics_data != NULL)
|
|
{
|
|
graphics_data_free (priv->graphics_data);
|
|
priv->graphics_data = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (cc_info_panel_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
cc_info_panel_finalize (GObject *object)
|
|
{
|
|
CcInfoPanelPrivate *priv = CC_INFO_PANEL (object)->priv;
|
|
|
|
g_free (priv->gnome_version);
|
|
g_free (priv->gnome_date);
|
|
g_free (priv->gnome_distributor);
|
|
|
|
G_OBJECT_CLASS (cc_info_panel_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
cc_info_panel_class_init (CcInfoPanelClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (CcInfoPanelPrivate));
|
|
|
|
object_class->get_property = cc_info_panel_get_property;
|
|
object_class->set_property = cc_info_panel_set_property;
|
|
object_class->dispose = cc_info_panel_dispose;
|
|
object_class->finalize = cc_info_panel_finalize;
|
|
}
|
|
|
|
static void
|
|
cc_info_panel_class_finalize (CcInfoPanelClass *klass)
|
|
{
|
|
}
|
|
|
|
static char *
|
|
get_os_type (void)
|
|
{
|
|
int bits;
|
|
|
|
if (GLIB_SIZEOF_VOID_P == 8)
|
|
bits = 64;
|
|
else
|
|
bits = 32;
|
|
|
|
/* translators: This is the type of architecture, for example:
|
|
* "64-bit" or "32-bit" */
|
|
return g_strdup_printf (_("%d-bit"), bits);
|
|
}
|
|
|
|
#define KILOBYTE_FACTOR (G_GOFFSET_CONSTANT (1000))
|
|
#define MEGABYTE_FACTOR (KILOBYTE_FACTOR * KILOBYTE_FACTOR)
|
|
#define GIGABYTE_FACTOR (MEGABYTE_FACTOR * KILOBYTE_FACTOR)
|
|
#define TERABYTE_FACTOR (GIGABYTE_FACTOR * KILOBYTE_FACTOR)
|
|
#define PETABYTE_FACTOR (TERABYTE_FACTOR * KILOBYTE_FACTOR)
|
|
#define EXABYTE_FACTOR (PETABYTE_FACTOR * KILOBYTE_FACTOR)
|
|
|
|
static char *
|
|
format_size_for_display (goffset size)
|
|
{
|
|
if (size < (goffset) KILOBYTE_FACTOR)
|
|
return g_strdup_printf (g_dngettext(NULL, "%u byte", "%u bytes",(guint) size), (guint) size);
|
|
else
|
|
{
|
|
gdouble displayed_size;
|
|
|
|
if (size < (goffset) MEGABYTE_FACTOR)
|
|
{
|
|
displayed_size = (gdouble) size / (gdouble) KILOBYTE_FACTOR;
|
|
return g_strdup_printf (_("%.1f KB"), displayed_size);
|
|
}
|
|
else if (size < (goffset) GIGABYTE_FACTOR)
|
|
{
|
|
displayed_size = (gdouble) size / (gdouble) MEGABYTE_FACTOR;
|
|
return g_strdup_printf (_("%.1f MB"), displayed_size);
|
|
}
|
|
else if (size < (goffset) TERABYTE_FACTOR)
|
|
{
|
|
displayed_size = (gdouble) size / (gdouble) GIGABYTE_FACTOR;
|
|
return g_strdup_printf (_("%.1f GB"), displayed_size);
|
|
}
|
|
else if (size < (goffset) PETABYTE_FACTOR)
|
|
{
|
|
displayed_size = (gdouble) size / (gdouble) TERABYTE_FACTOR;
|
|
return g_strdup_printf (_("%.1f TB"), displayed_size);
|
|
}
|
|
else if (size < (goffset) EXABYTE_FACTOR)
|
|
{
|
|
displayed_size = (gdouble) size / (gdouble) PETABYTE_FACTOR;
|
|
return g_strdup_printf (_("%.1f PB"), displayed_size);
|
|
}
|
|
else
|
|
{
|
|
displayed_size = (gdouble) size / (gdouble) EXABYTE_FACTOR;
|
|
return g_strdup_printf (_("%.1f EB"), displayed_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *
|
|
get_primary_disc_info (void)
|
|
{
|
|
guint64 total_bytes;
|
|
GList *points;
|
|
GList *p;
|
|
|
|
total_bytes = 0;
|
|
|
|
points = g_unix_mount_points_get (NULL);
|
|
for (p = points; p != NULL; p = p->next)
|
|
{
|
|
GUnixMountEntry *mount = p->data;
|
|
const char *mount_path;
|
|
struct statfs buf;
|
|
|
|
mount_path = g_unix_mount_get_mount_path (mount);
|
|
|
|
if (g_str_has_prefix (mount_path, "/media/")
|
|
|| g_str_has_prefix (mount_path, g_get_home_dir ()))
|
|
{
|
|
g_free (mount);
|
|
continue;
|
|
}
|
|
|
|
if (statfs (mount_path, &buf) < 0)
|
|
{
|
|
g_warning ("Unable to stat / filesystem: %s", g_strerror (errno));
|
|
g_free (mount);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
total_bytes += (guint64) buf.f_blocks * buf.f_bsize;
|
|
}
|
|
|
|
g_free (mount);
|
|
}
|
|
g_list_free (points);
|
|
|
|
return format_size_for_display (total_bytes);
|
|
}
|
|
|
|
static char *
|
|
remove_duplicate_whitespace (const char *old)
|
|
{
|
|
char *new;
|
|
GRegex *re;
|
|
GError *error;
|
|
|
|
error = NULL;
|
|
re = g_regex_new ("[ \t\n\r]+", G_REGEX_MULTILINE, 0, &error);
|
|
if (re == NULL)
|
|
{
|
|
g_warning ("Error building regex: %s", error->message);
|
|
g_error_free (error);
|
|
return g_strdup (old);
|
|
}
|
|
new = g_regex_replace (re,
|
|
old,
|
|
-1,
|
|
0,
|
|
" ",
|
|
0,
|
|
&error);
|
|
g_regex_unref (re);
|
|
if (new == NULL)
|
|
{
|
|
g_warning ("Error replacing string: %s", error->message);
|
|
g_error_free (error);
|
|
return g_strdup (old);
|
|
}
|
|
|
|
return new;
|
|
}
|
|
|
|
|
|
static char *
|
|
get_cpu_info (const glibtop_sysinfo *info)
|
|
{
|
|
GHashTable *counts;
|
|
GString *cpu;
|
|
char *ret;
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
int i;
|
|
int j;
|
|
|
|
counts = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
/* count duplicates */
|
|
for (i = 0; i != info->ncpu; ++i)
|
|
{
|
|
const char * const keys[] = { "model name", "cpu" };
|
|
char *model;
|
|
int *count;
|
|
|
|
model = NULL;
|
|
|
|
for (j = 0; model == NULL && j != G_N_ELEMENTS (keys); ++j)
|
|
{
|
|
model = g_hash_table_lookup (info->cpuinfo[i].values,
|
|
keys[j]);
|
|
}
|
|
|
|
if (model == NULL)
|
|
model = _("Unknown model");
|
|
|
|
count = g_hash_table_lookup (counts, model);
|
|
if (count == NULL)
|
|
g_hash_table_insert (counts, model, GINT_TO_POINTER (1));
|
|
else
|
|
g_hash_table_replace (counts, model, GINT_TO_POINTER (GPOINTER_TO_INT (count) + 1));
|
|
}
|
|
|
|
cpu = g_string_new (NULL);
|
|
g_hash_table_iter_init (&iter, counts);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
{
|
|
char *stripped;
|
|
int count;
|
|
|
|
count = GPOINTER_TO_INT (value);
|
|
stripped = remove_duplicate_whitespace ((const char *)key);
|
|
if (count > 1)
|
|
g_string_append_printf (cpu, "%s \303\227 %d ", stripped, count);
|
|
else
|
|
g_string_append_printf (cpu, "%s ", stripped);
|
|
g_free (stripped);
|
|
}
|
|
|
|
g_hash_table_destroy (counts);
|
|
|
|
ret = prettify_info (cpu->str);
|
|
g_string_free (cpu, TRUE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
on_section_changed (GtkTreeSelection *selection,
|
|
gpointer data)
|
|
{
|
|
CcInfoPanel *self = CC_INFO_PANEL (data);
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
gint *indices;
|
|
int index;
|
|
|
|
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
return;
|
|
|
|
path = gtk_tree_model_get_path (model, &iter);
|
|
|
|
indices = gtk_tree_path_get_indices (path);
|
|
index = indices[0];
|
|
|
|
if (index >= 0)
|
|
{
|
|
g_object_set (G_OBJECT (WID (self->priv->builder, "notebook")),
|
|
"page", index, NULL);
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static gboolean
|
|
switch_fallback_get_mapping (GValue *value,
|
|
GVariant *variant,
|
|
gpointer data)
|
|
{
|
|
const char *setting;
|
|
|
|
setting = g_variant_get_string (variant, NULL);
|
|
g_value_set_boolean (value, strcmp (setting, "gnome") != 0);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
toggle_fallback_warning_label (CcInfoPanel *self,
|
|
gboolean visible)
|
|
{
|
|
GtkWidget *widget;
|
|
const char *text;
|
|
|
|
widget = WID (self->priv->builder, "graphics_logout_warning_label");
|
|
|
|
if (self->priv->is_fallback)
|
|
text = _("The next login will attempt to use the standard experience.");
|
|
else
|
|
text = _("The next login will use the fallback mode intended for unsupported graphics hardware.");
|
|
|
|
gtk_label_set_text (GTK_LABEL (widget), text);
|
|
|
|
if (visible)
|
|
gtk_widget_show (widget);
|
|
else
|
|
gtk_widget_hide (widget);
|
|
}
|
|
|
|
static GVariant *
|
|
switch_fallback_set_mapping (const GValue *value,
|
|
const GVariantType *expected_type,
|
|
gpointer data)
|
|
{
|
|
CcInfoPanel *self = data;
|
|
gboolean is_set;
|
|
|
|
is_set = g_value_get_boolean (value);
|
|
if (is_set != self->priv->is_fallback)
|
|
toggle_fallback_warning_label (self, TRUE);
|
|
else
|
|
toggle_fallback_warning_label (self, FALSE);
|
|
|
|
return g_variant_new_string (is_set ? "gnome-fallback" : "gnome");
|
|
}
|
|
|
|
static void
|
|
info_panel_setup_graphics (CcInfoPanel *self)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkSwitch *sw;
|
|
char *text;
|
|
|
|
widget = WID (self->priv->builder, "graphics_driver_label");
|
|
gtk_label_set_markup (GTK_LABEL (widget), self->priv->graphics_data->hardware_string);
|
|
|
|
self->priv->is_fallback = get_current_is_fallback (self);
|
|
if (self->priv->is_fallback)
|
|
{
|
|
/* translators: The hardware is not able to run GNOME 3's
|
|
* shell, so we use the GNOME "Fallback" session */
|
|
text = g_strdup (C_("Experience", "Fallback"));
|
|
}
|
|
else
|
|
{
|
|
/* translators: The hardware is able to run GNOME 3's
|
|
* shell, also called "Standard" experience */
|
|
text = g_strdup (C_("Experience", "Standard"));
|
|
}
|
|
widget = WID (self->priv->builder, "graphics_experience_label");
|
|
gtk_label_set_markup (GTK_LABEL (widget), text ? text : "");
|
|
g_free (text);
|
|
|
|
widget = WID (self->priv->builder, "graphics_fallback_switch_box");
|
|
sw = GTK_SWITCH (gtk_switch_new ());
|
|
g_settings_bind_with_mapping (self->priv->session_settings, KEY_SESSION_NAME,
|
|
sw, "active", 0,
|
|
switch_fallback_get_mapping,
|
|
switch_fallback_set_mapping, self, NULL);
|
|
gtk_box_pack_start (GTK_BOX (widget), GTK_WIDGET (sw), FALSE, FALSE, 0);
|
|
gtk_widget_show_all (GTK_WIDGET (sw));
|
|
}
|
|
|
|
static void
|
|
default_app_changed (GtkAppChooserButton *button,
|
|
CcInfoPanel *self)
|
|
{
|
|
GAppInfo *info;
|
|
char *content_type;
|
|
GError *error = NULL;
|
|
|
|
info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (button));
|
|
content_type = gtk_app_chooser_get_content_type (GTK_APP_CHOOSER (button));
|
|
if (g_app_info_set_as_default_for_type (info, content_type, &error) == FALSE)
|
|
{
|
|
g_warning ("Failed to set '%s' as the default application for '%s': %s",
|
|
g_app_info_get_name (info), content_type, error->message);
|
|
g_error_free (error);
|
|
error = NULL;
|
|
}
|
|
|
|
/* Set https support for the browser as well */
|
|
if (g_str_equal (content_type, "x-scheme-handler/https"))
|
|
{
|
|
if (g_app_info_set_as_default_for_type (info, "x-scheme-handler/https", &error) == FALSE)
|
|
{
|
|
g_warning ("Failed to set '%s' as the default application for '%s': %s",
|
|
g_app_info_get_name (info), "x-scheme-handler/https", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
g_free (content_type);
|
|
g_object_unref (info);
|
|
}
|
|
|
|
static void
|
|
info_panel_setup_default_app (CcInfoPanel *self,
|
|
const char *content_type,
|
|
guint left_attach,
|
|
guint right_attach,
|
|
guint top_attach,
|
|
guint bottom_attach)
|
|
{
|
|
GtkWidget *button;
|
|
GtkWidget *table;
|
|
GAppInfo *info;
|
|
GError *error = NULL;
|
|
|
|
table = WID (self->priv->builder, "default_apps_table");
|
|
|
|
/* FIXME: We need to do this because GtkAppChooser doesn't
|
|
* give us the opportunity to select what app should be selected
|
|
* by default.
|
|
* https://bugzilla.gnome.org/show_bug.cgi?id=642706 */
|
|
info = g_app_info_get_default_for_type (content_type, FALSE);
|
|
if (info != NULL && g_app_info_set_as_last_used_for_type (info, content_type, &error) == FALSE)
|
|
{
|
|
g_warning ("Failed to set '%s' as the default application for '%s': %s",
|
|
g_app_info_get_name (info), content_type, error->message);
|
|
g_error_free (error);
|
|
}
|
|
if (info != NULL)
|
|
g_object_unref (info);
|
|
|
|
button = gtk_app_chooser_button_new (content_type);
|
|
gtk_table_attach (GTK_TABLE (table), button,
|
|
left_attach, right_attach,
|
|
top_attach, bottom_attach, GTK_FILL, 0, 0, 0);
|
|
g_signal_connect (G_OBJECT (button), "changed",
|
|
G_CALLBACK (default_app_changed), self);
|
|
gtk_widget_show (button);
|
|
}
|
|
|
|
static void
|
|
info_panel_setup_default_apps (CcInfoPanel *self)
|
|
{
|
|
info_panel_setup_default_app (self, "x-scheme-handler/http",
|
|
1, 2, 0, 1);
|
|
|
|
info_panel_setup_default_app (self, "x-scheme-handler/mailto",
|
|
1, 2, 1, 2);
|
|
|
|
info_panel_setup_default_app (self, "text/calendar",
|
|
1, 2, 2, 3);
|
|
|
|
info_panel_setup_default_app (self, "audio/x-vorbis+ogg",
|
|
1, 2, 3, 4);
|
|
|
|
info_panel_setup_default_app (self, "video/x-ogm+ogg",
|
|
1, 2, 4, 5);
|
|
|
|
info_panel_setup_default_app (self, "image/jpeg",
|
|
1, 2, 5, 6);
|
|
}
|
|
|
|
static void
|
|
info_panel_setup_selector (CcInfoPanel *self)
|
|
{
|
|
GtkTreeView *view;
|
|
GtkListStore *model;
|
|
GtkTreeSelection *selection;
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeIter iter;
|
|
int section_name_column = 0;
|
|
|
|
view = GTK_TREE_VIEW (WID (self->priv->builder, "overview_treeview"));
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
|
|
model = gtk_list_store_new (1, G_TYPE_STRING);
|
|
gtk_tree_view_set_model (view, GTK_TREE_MODEL (model));
|
|
g_object_unref (model);
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
g_object_set (renderer,
|
|
"width-chars", 20,
|
|
"ellipsize", PANGO_ELLIPSIZE_END,
|
|
NULL);
|
|
column = gtk_tree_view_column_new_with_attributes (_("Section"),
|
|
renderer,
|
|
"text", section_name_column,
|
|
NULL);
|
|
gtk_tree_view_append_column (view, column);
|
|
|
|
|
|
gtk_list_store_append (model, &iter);
|
|
gtk_list_store_set (model, &iter, section_name_column,
|
|
_("Overview"),
|
|
-1);
|
|
gtk_tree_selection_select_iter (selection, &iter);
|
|
|
|
gtk_list_store_append (model, &iter);
|
|
gtk_list_store_set (model, &iter, section_name_column,
|
|
_("Default Applications"),
|
|
-1);
|
|
|
|
gtk_list_store_append (model, &iter);
|
|
gtk_list_store_set (model, &iter, section_name_column,
|
|
_("Graphics"),
|
|
-1);
|
|
|
|
g_signal_connect (selection, "changed",
|
|
G_CALLBACK (on_section_changed), self);
|
|
on_section_changed (selection, self);
|
|
|
|
gtk_widget_show_all (GTK_WIDGET (view));
|
|
}
|
|
|
|
static void
|
|
info_panel_setup_overview (CcInfoPanel *self)
|
|
{
|
|
GtkWidget *widget;
|
|
gboolean res;
|
|
glibtop_mem mem;
|
|
const glibtop_sysinfo *info;
|
|
char *text;
|
|
|
|
res = load_gnome_version (&self->priv->gnome_version,
|
|
&self->priv->gnome_distributor,
|
|
&self->priv->gnome_date);
|
|
if (res)
|
|
{
|
|
widget = WID (self->priv->builder, "version_label");
|
|
text = g_strdup_printf (_("Version %s"), self->priv->gnome_version);
|
|
gtk_label_set_text (GTK_LABEL (widget), text);
|
|
g_free (text);
|
|
}
|
|
|
|
glibtop_get_mem (&mem);
|
|
text = g_format_size_for_display (mem.total);
|
|
widget = WID (self->priv->builder, "memory_label");
|
|
gtk_label_set_text (GTK_LABEL (widget), text ? text : "");
|
|
g_free (text);
|
|
|
|
info = glibtop_get_sysinfo ();
|
|
|
|
widget = WID (self->priv->builder, "processor_label");
|
|
text = get_cpu_info (info);
|
|
gtk_label_set_markup (GTK_LABEL (widget), text ? text : "");
|
|
g_free (text);
|
|
|
|
widget = WID (self->priv->builder, "os_type_label");
|
|
text = get_os_type ();
|
|
gtk_label_set_text (GTK_LABEL (widget), text ? text : "");
|
|
g_free (text);
|
|
|
|
widget = WID (self->priv->builder, "disk_label");
|
|
text = get_primary_disc_info ();
|
|
gtk_label_set_text (GTK_LABEL (widget), text ? text : "");
|
|
g_free (text);
|
|
|
|
widget = WID (self->priv->builder, "graphics_label");
|
|
gtk_label_set_markup (GTK_LABEL (widget), self->priv->graphics_data->hardware_string);
|
|
|
|
widget = WID (self->priv->builder, "info_vbox");
|
|
gtk_widget_reparent (widget, (GtkWidget *) self);
|
|
}
|
|
|
|
static void
|
|
refresh_update_button (CcInfoPanel *self)
|
|
{
|
|
GtkWidget *widget;
|
|
|
|
widget = WID (self->priv->builder, "updates_button");
|
|
if (self->priv->updates_available)
|
|
gtk_widget_show (widget);
|
|
else
|
|
gtk_widget_hide (widget);
|
|
}
|
|
|
|
static void
|
|
on_pk_transaction_signal (GDBusProxy *proxy,
|
|
char *sender_name,
|
|
char *signal_name,
|
|
GVariant *parameters,
|
|
CcInfoPanel *self)
|
|
{
|
|
if (g_strcmp0 (signal_name, "Package") == 0)
|
|
{
|
|
self->priv->updates_available = TRUE;
|
|
}
|
|
else if (g_strcmp0 (signal_name, "Finished") == 0)
|
|
{
|
|
refresh_update_button (self);
|
|
}
|
|
else if (g_strcmp0 (signal_name, "Destroy") == 0)
|
|
{
|
|
g_object_unref (self->priv->pk_transaction_proxy);
|
|
self->priv->pk_transaction_proxy = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_pk_get_updates_ready (GObject *source,
|
|
GAsyncResult *res,
|
|
CcInfoPanel *self)
|
|
{
|
|
GError *error;
|
|
GVariant *result;
|
|
|
|
error = NULL;
|
|
result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
|
|
if (result == NULL)
|
|
{
|
|
g_warning ("Error getting PackageKit updates list: %s", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_pk_get_tid_ready (GObject *source,
|
|
GAsyncResult *res,
|
|
CcInfoPanel *self)
|
|
{
|
|
GError *error;
|
|
GVariant *result;
|
|
char *tid;
|
|
|
|
error = NULL;
|
|
result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
|
|
if (result == NULL)
|
|
{
|
|
g_warning ("Error getting PackageKit transaction ID: %s", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
g_variant_get (result, "(s)", &tid);
|
|
|
|
self->priv->pk_transaction_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
NULL,
|
|
"org.freedesktop.PackageKit",
|
|
tid,
|
|
"org.freedesktop.PackageKit.Transaction",
|
|
NULL,
|
|
NULL);
|
|
g_free (tid);
|
|
g_variant_unref (result);
|
|
|
|
if (self->priv->pk_transaction_proxy == NULL)
|
|
{
|
|
g_warning ("Unable to get PackageKit transaction proxy object");
|
|
return;
|
|
}
|
|
|
|
g_signal_connect (self->priv->pk_transaction_proxy,
|
|
"g-signal",
|
|
G_CALLBACK (on_pk_transaction_signal),
|
|
self);
|
|
|
|
g_dbus_proxy_call (self->priv->pk_transaction_proxy,
|
|
"GetUpdates",
|
|
g_variant_new ("(s)", "none"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
(GAsyncReadyCallback) on_pk_get_updates_ready,
|
|
self);
|
|
}
|
|
|
|
static void
|
|
refresh_updates (CcInfoPanel *self)
|
|
{
|
|
self->priv->updates_available = FALSE;
|
|
|
|
g_assert (self->priv->pk_proxy != NULL);
|
|
g_dbus_proxy_call (self->priv->pk_proxy,
|
|
"GetTid",
|
|
NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
(GAsyncReadyCallback) on_pk_get_tid_ready,
|
|
self);
|
|
}
|
|
|
|
static void
|
|
on_pk_signal (GDBusProxy *proxy,
|
|
char *sender_name,
|
|
char *signal_name,
|
|
GVariant *parameters,
|
|
CcInfoPanel *self)
|
|
{
|
|
if (g_strcmp0 (signal_name, "UpdatesChanged") == 0)
|
|
{
|
|
refresh_updates (self);
|
|
}
|
|
}
|
|
|
|
static void
|
|
on_updates_button_clicked (GtkWidget *widget,
|
|
CcInfoPanel *self)
|
|
{
|
|
GError *error;
|
|
error = NULL;
|
|
g_spawn_command_line_async ("gpk-update-viewer", &error);
|
|
if (error != NULL)
|
|
{
|
|
g_warning ("unable to launch Software Updates: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cc_info_panel_init (CcInfoPanel *self)
|
|
{
|
|
GError *error = NULL;
|
|
GtkWidget *widget;
|
|
|
|
self->priv = INFO_PANEL_PRIVATE (self);
|
|
|
|
self->priv->builder = gtk_builder_new ();
|
|
|
|
self->priv->session_settings = g_settings_new (GNOME_SESSION_MANAGER_SCHEMA);
|
|
|
|
self->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
|
|
|
g_assert (self->priv->session_bus);
|
|
|
|
self->priv->pk_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
NULL,
|
|
"org.freedesktop.PackageKit",
|
|
"/org/freedesktop/PackageKit",
|
|
"org.freedesktop.PackageKit",
|
|
NULL,
|
|
NULL);
|
|
if (self->priv->pk_proxy == NULL)
|
|
g_warning ("Unable to get PackageKit proxy object");
|
|
else
|
|
{
|
|
g_signal_connect (self->priv->pk_proxy,
|
|
"g-signal",
|
|
G_CALLBACK (on_pk_signal),
|
|
self);
|
|
refresh_updates (self);
|
|
}
|
|
|
|
gtk_builder_add_from_file (self->priv->builder,
|
|
GNOMECC_UI_DIR "/info.ui",
|
|
&error);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_warning ("Could not load interface file: %s", error->message);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
self->priv->graphics_data = get_graphics_data ();
|
|
|
|
widget = WID (self->priv->builder, "updates_button");
|
|
g_signal_connect (widget, "clicked", G_CALLBACK (on_updates_button_clicked), self);
|
|
|
|
info_panel_setup_selector (self);
|
|
info_panel_setup_overview (self);
|
|
info_panel_setup_default_apps (self);
|
|
info_panel_setup_graphics (self);
|
|
}
|
|
|
|
void
|
|
cc_info_panel_register (GIOModule *module)
|
|
{
|
|
cc_info_panel_register_type (G_TYPE_MODULE (module));
|
|
g_io_extension_point_implement (CC_SHELL_PANEL_EXTENSION_POINT,
|
|
CC_TYPE_INFO_PANEL,
|
|
"info", 0);
|
|
}
|
|
|