./0000755000004100000410000000000012735467745011267 5ustar www-datawww-data./autogen.sh0000755000004100000410000000115112735467744013265 0ustar www-datawww-data#!/bin/sh # Run this to generate all the initial makefiles, etc. REQUIRED_AUTOMAKE_VERSION=1.5 USE_GNOME2_MACROS=1 srcdir=`dirname $0` test -z "$srcdir" && srcdir=. PKG_NAME="unity-settings-daemon" (test -f $srcdir/configure.ac \ && test -d $srcdir/gnome-settings-daemon \ && test -f $srcdir/gnome-settings-daemon/gnome-settings-manager.h) || { echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" echo " top-level unity-settings-daemon directory" exit 1 } which gnome-autogen.sh || { echo "You need to install gnome-common from the GNOME SVN" exit 1 } . gnome-autogen.sh ./plugins/0000755000004100000410000000000012735467744012747 5ustar www-datawww-data./plugins/updates/0000755000004100000410000000000012735467763014415 5ustar www-datawww-data./plugins/updates/updates.gnome-settings-plugin.in0000644000004100000410000000024112735467744022644 0ustar www-datawww-data[GNOME Settings Plugin] Module=updates IAge=0 Priority=300 _Name=Updates _Description=Updates plugin Authors=Richard Hughes Copyright=Copyright © 2011 Website= ./plugins/updates/gsd-updates-refresh.h0000644000004100000410000000361212735467744020443 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2007-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GSD_UPDATES_REFRESH_H #define __GSD_UPDATES_REFRESH_H #include G_BEGIN_DECLS #define GSD_TYPE_UPDATES_REFRESH (gsd_updates_refresh_get_type ()) #define GSD_UPDATES_REFRESH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_UPDATES_REFRESH, GsdUpdatesRefresh)) #define GSD_UPDATES_REFRESH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_UPDATES_REFRESH, GsdUpdatesRefreshClass)) #define GSD_IS_UPDATES_REFRESH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_UPDATES_REFRESH)) typedef struct GsdUpdatesRefreshPrivate GsdUpdatesRefreshPrivate; typedef struct { GObject parent; GsdUpdatesRefreshPrivate *priv; } GsdUpdatesRefresh; typedef struct { GObjectClass parent_class; } GsdUpdatesRefreshClass; GType gsd_updates_refresh_get_type (void); GsdUpdatesRefresh *gsd_updates_refresh_new (void); G_END_DECLS #endif /* __GSD_UPDATES_REFRESH_H */ ./plugins/updates/gsd-updates-plugin.c0000644000004100000410000000202412735467744020272 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2011 Richard Hughes * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-updates-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdUpdates, gsd_updates) ./plugins/updates/Makefile.am0000644000004100000410000000220512735467744016447 0ustar www-datawww-dataplugin_name = updates plugin_LTLIBRARIES = \ libupdates.la libupdates_la_SOURCES = \ gsd-updates-common.h \ gsd-updates-plugin.c \ gsd-updates-refresh.h \ gsd-updates-refresh.c \ gsd-updates-firmware.h \ gsd-updates-firmware.c \ gsd-updates-manager.h \ gsd-updates-manager.c libupdates_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libupdates_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(GUDEV_CFLAGS) \ -DG_UDEV_API_IS_SUBJECT_TO_CHANGE \ $(PACKAGEKIT_CFLAGS) \ -DI_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE \ -DDATADIR=\"$(datadir)\" \ -DBINDIR=\"$(bindir)\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ -I$(top_srcdir)/data \ $(AM_CFLAGS) libupdates_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libupdates_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(PACKAGEKIT_LIBS) plugin_in_files = \ updates.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/updates/gsd-updates-firmware.h0000644000004100000410000000367012735467744020625 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GSD_UPDATES_FIRMWARE_H #define __GSD_UPDATES_FIRMWARE_H #include G_BEGIN_DECLS #define GSD_UPDATES_TYPE_FIRMWARE (gsd_updates_firmware_get_type ()) #define GSD_UPDATES_FIRMWARE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmware)) #define GSD_UPDATES_FIRMWARE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmwareClass)) #define GSD_UPDATES_IS_FIRMWARE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_UPDATES_TYPE_FIRMWARE)) typedef struct GsdUpdatesFirmwarePrivate GsdUpdatesFirmwarePrivate; typedef struct { GObject parent; GsdUpdatesFirmwarePrivate *priv; } GsdUpdatesFirmware; typedef struct { GObjectClass parent_class; } GsdUpdatesFirmwareClass; GType gsd_updates_firmware_get_type (void); GsdUpdatesFirmware *gsd_updates_firmware_new (void); G_END_DECLS #endif /* __GSD_UPDATES_FIRMWARE_H */ ./plugins/updates/gsd-updates-manager.c0000644000004100000410000016151512735467763020422 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2011 Richard Hughes * * 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 "config.h" #include #include #include #include #include #include #include #include "gsd-enums.h" #include "gsd-updates-manager.h" #include "gsd-updates-firmware.h" #include "gsd-updates-refresh.h" #include "gsd-updates-common.h" #include "gnome-settings-profile.h" #include "gnome-settings-bus.h" #define GSD_UPDATES_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManagerPrivate)) #define MAX_FAILED_GET_UPDATES 10 /* the maximum number of tries */ #define GSD_UPDATES_ICON_NORMAL "software-update-available-symbolic" #define GSD_UPDATES_ICON_URGENT "software-update-urgent-symbolic" #define GSD_UPDATES_CHECK_OFFLINE_TIMEOUT 30 /* time in seconds */ struct GsdUpdatesManagerPrivate { GCancellable *cancellable; GsdUpdatesRefresh *refresh; GsdUpdatesFirmware *firmware; GSettings *settings_proxy; GSettings *settings_ftp; GSettings *settings_gsd; GSettings *settings_http; guint number_updates_critical_last_shown; guint offline_update_id; PkError *offline_update_error; NotifyNotification *notification_updates; PkControl *control; PkTask *task; guint inhibit_cookie; GsdSessionManager *proxy_session; guint update_viewer_watcher_id; GVolumeMonitor *volume_monitor; guint failed_get_updates_count; GPtrArray *update_packages; }; static void gsd_updates_manager_class_init (GsdUpdatesManagerClass *klass); static void gsd_updates_manager_init (GsdUpdatesManager *updates_manager); G_DEFINE_TYPE (GsdUpdatesManager, gsd_updates_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static void child_exit_cb (GPid pid, gint status, gpointer user_data) { g_spawn_close_pid (pid); } static void clear_offline_updates_message (void) { gboolean ret; GError *error = NULL; gchar *argv[3]; GPid pid; argv[0] = "pkexec"; argv[1] = LIBEXECDIR "/pk-clear-offline-update"; argv[2] = NULL; ret = g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, &error); if (!ret) { g_warning ("Failure clearing offline update message: %s", error->message); g_error_free (error); return; } g_child_watch_add (pid, child_exit_cb, NULL); } static void show_offline_updates_error (GsdUpdatesManager *manager) { const gchar *title; gboolean show_geeky = FALSE; GString *msg; GtkWidget *dialog; /* TRANSLATORS: this is when the offline update failed */ title = _("Failed To Update"); msg = g_string_new (""); switch (pk_error_get_code (manager->priv->offline_update_error)) { case PK_ERROR_ENUM_UNFINISHED_TRANSACTION: /* TRANSLATORS: the transaction could not be completed * as a previous transaction was unfinished */ g_string_append (msg, _("A previous update was unfinished.")); show_geeky = TRUE; break; case PK_ERROR_ENUM_PACKAGE_DOWNLOAD_FAILED: case PK_ERROR_ENUM_NO_CACHE: case PK_ERROR_ENUM_NO_NETWORK: case PK_ERROR_ENUM_NO_MORE_MIRRORS_TO_TRY: case PK_ERROR_ENUM_CANNOT_FETCH_SOURCES: /* TRANSLATORS: the package manager needed to download * something with no network available */ g_string_append (msg, _("Network access was required but not available.")); break; case PK_ERROR_ENUM_BAD_GPG_SIGNATURE: case PK_ERROR_ENUM_CANNOT_UPDATE_REPO_UNSIGNED: case PK_ERROR_ENUM_GPG_FAILURE: case PK_ERROR_ENUM_MISSING_GPG_SIGNATURE: case PK_ERROR_ENUM_PACKAGE_CORRUPT: /* TRANSLATORS: if the package is not signed correctly * */ g_string_append (msg, _("An update was not signed in the correct way.")); show_geeky = TRUE; break; case PK_ERROR_ENUM_DEP_RESOLUTION_FAILED: case PK_ERROR_ENUM_FILE_CONFLICTS: case PK_ERROR_ENUM_INCOMPATIBLE_ARCHITECTURE: case PK_ERROR_ENUM_PACKAGE_CONFLICTS: /* TRANSLATORS: the transaction failed in a way the user * probably cannot comprehend. Package management systems * really are teh suck.*/ g_string_append (msg, _("The update could not be completed.")); show_geeky = TRUE; break; case PK_ERROR_ENUM_TRANSACTION_CANCELLED: /* TRANSLATORS: the user aborted the update manually */ g_string_append (msg, _("The update was cancelled.")); break; case PK_ERROR_ENUM_NO_PACKAGES_TO_UPDATE: case PK_ERROR_ENUM_UPDATE_NOT_FOUND: /* TRANSLATORS: the user must have updated manually after * the updates were prepared */ g_string_append (msg, _("An offline update was requested but no packages required updating.")); break; case PK_ERROR_ENUM_NO_SPACE_ON_DEVICE: /* TRANSLATORS: we ran out of disk space */ g_string_append (msg, _("No space was left on the drive.")); break; case PK_ERROR_ENUM_PACKAGE_FAILED_TO_BUILD: case PK_ERROR_ENUM_PACKAGE_FAILED_TO_INSTALL: case PK_ERROR_ENUM_PACKAGE_FAILED_TO_REMOVE: /* TRANSLATORS: the update process failed in a general * way, usually this message will come from source distros * like gentoo */ g_string_append (msg, _("An update failed to install correctly.")); show_geeky = TRUE; break; default: /* TRANSLATORS: We didn't handle the error type */ g_string_append (msg, _("The offline update failed in an unexpected way.")); show_geeky = TRUE; break; } if (show_geeky) { g_string_append_printf (msg, "\n%s\n\n%s", /* TRANSLATORS: these are geeky messages from the * package manager no mortal is supposed to understand, * but google might know what they mean */ _("Detailed errors from the package manager follow:"), pk_error_get_details (manager->priv->offline_update_error)); } dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", title); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", msg->str); g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); gtk_widget_show (dialog); clear_offline_updates_message (); g_string_free (msg, TRUE); } static void libnotify_action_cb (NotifyNotification *notification, gchar *action, gpointer user_data) { gboolean ret; GError *error = NULL; GsdUpdatesManager *manager = GSD_UPDATES_MANAGER (user_data); notify_notification_close (notification, NULL); if (g_strcmp0 (action, "distro-upgrade-info") == 0) { ret = g_spawn_command_line_async (DATADIR "/PackageKit/pk-upgrade-distro.sh", &error); if (!ret) { g_warning ("Failure launching pk-upgrade-distro.sh: %s", error->message); g_error_free (error); } goto out; } if (g_strcmp0 (action, "show-update-viewer") == 0) { ret = g_spawn_command_line_async (BINDIR "/gpk-update-viewer", &error); if (!ret) { g_warning ("Failure launching update viewer: %s", error->message); g_error_free (error); } goto out; } if (g_strcmp0 (action, "clear-offline-updates") == 0) { clear_offline_updates_message (); goto out; } if (g_strcmp0 (action, "error-offline-updates") == 0) { show_offline_updates_error (manager); goto out; } if (g_strcmp0 (action, "cancel") == 0) { /* try to cancel */ g_cancellable_cancel (manager->priv->cancellable); goto out; } g_warning ("unknown action id: %s", action); out: return; } static void on_notification_closed (NotifyNotification *notification, gpointer data) { g_object_unref (notification); } static void get_distro_upgrades_finished_cb (GObject *object, GAsyncResult *res, GsdUpdatesManager *manager) { const gchar *title; gboolean ret; gchar *name = NULL; GError *error = NULL; GPtrArray *array = NULL; GString *string = NULL; guint i; NotifyNotification *notification; PkClient *client = PK_CLIENT(object); PkDistroUpgrade *item; PkError *error_code = NULL; PkResults *results; PkUpdateStateEnum state; /* get the results */ results = pk_client_generic_finish (PK_CLIENT(client), res, &error); if (results == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } if (error->domain != PK_CLIENT_ERROR || error->code != PK_CLIENT_ERROR_NOT_SUPPORTED) { g_warning ("failed to get upgrades: %s", error->message); } g_error_free (error); goto out; } /* check error code */ error_code = pk_results_get_error_code (results); if (error_code != NULL) { g_warning ("failed to get upgrades: %s, %s", pk_error_enum_to_string (pk_error_get_code (error_code)), pk_error_get_details (error_code)); goto out; } /* process results */ array = pk_results_get_distro_upgrade_array (results); /* any updates? */ if (array->len == 0) { g_debug ("no upgrades"); goto out; } /* do we do the notification? */ ret = g_settings_get_boolean (manager->priv->settings_gsd, GSD_SETTINGS_NOTIFY_DISTRO_UPGRADES); if (!ret) { g_debug ("ignoring due to GSettings"); goto out; } /* find the upgrade string */ string = g_string_new (""); for (i=0; i < array->len; i++) { item = (PkDistroUpgrade *) g_ptr_array_index (array, i); g_object_get (item, "name", &name, "state", &state, NULL); g_string_append_printf (string, "%s (%s)\n", name, pk_distro_upgrade_enum_to_string (state)); g_free (name); } if (string->len != 0) g_string_set_size (string, string->len-1); /* TRANSLATORS: a distro update is available, e.g. Fedora 8 to Fedora 9 */ title = _("Distribution upgrades available"); notification = notify_notification_new (title, string->str, GSD_UPDATES_ICON_NORMAL); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER); notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL); notify_notification_add_action (notification, "distro-upgrade-info", /* TRANSLATORS: provides more information about the upgrade */ _("More information"), libnotify_action_cb, manager, NULL); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } out: if (error_code != NULL) g_object_unref (error_code); if (array != NULL) g_ptr_array_unref (array); if (string != NULL) g_string_free (string, TRUE); if (results != NULL) g_object_unref (results); } static void due_get_upgrades_cb (GsdUpdatesRefresh *refresh, GsdUpdatesManager *manager) { /* optimize the amount of downloaded data by setting the cache age */ pk_client_set_cache_age (PK_CLIENT(manager->priv->task), g_settings_get_int (manager->priv->settings_gsd, GSD_SETTINGS_FREQUENCY_GET_UPGRADES)); /* get new distro upgrades list */ pk_client_get_distro_upgrades_async (PK_CLIENT(manager->priv->task), NULL, NULL, NULL, (GAsyncReadyCallback) get_distro_upgrades_finished_cb, manager); } static void refresh_cache_finished_cb (GObject *object, GAsyncResult *res, GsdUpdatesManager *manager) { PkClient *client = PK_CLIENT(object); PkResults *results; GError *error = NULL; PkError *error_code = NULL; /* get the results */ results = pk_client_generic_finish (PK_CLIENT(client), res, &error); if (results == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } g_warning ("failed to refresh the cache: %s", error->message); g_error_free (error); return; } /* check error code */ error_code = pk_results_get_error_code (results); if (error_code != NULL) { g_warning ("failed to refresh the cache: %s, %s", pk_error_enum_to_string (pk_error_get_code (error_code)), pk_error_get_details (error_code)); } if (error_code != NULL) g_object_unref (error_code); if (results != NULL) g_object_unref (results); } static void due_refresh_cache_cb (GsdUpdatesRefresh *refresh, GsdUpdatesManager *manager) { /* optimize the amount of downloaded data by setting the cache age */ pk_client_set_cache_age (PK_CLIENT(manager->priv->task), g_settings_get_int (manager->priv->settings_gsd, GSD_SETTINGS_FREQUENCY_REFRESH_CACHE)); pk_client_refresh_cache_async (PK_CLIENT(manager->priv->task), TRUE, NULL, NULL, NULL, (GAsyncReadyCallback) refresh_cache_finished_cb, manager); } static void notify_critical_updates (GsdUpdatesManager *manager, GPtrArray *array) { const gchar *message; const gchar *title; gboolean ret; GError *error = NULL; NotifyNotification *notification; /* if the number of critical updates is the same as the last notification, * then skip the notifcation as we don't want to bombard the user every hour */ if (array->len == manager->priv->number_updates_critical_last_shown) { g_debug ("ignoring as user ignored last warning"); return; } /* save for comparison later */ manager->priv->number_updates_critical_last_shown = array->len; /* TRANSLATORS: title in the libnotify popup */ title = ngettext ("Update", "Updates", array->len); /* TRANSLATORS: message when there are security updates */ message = ngettext ("An important software update is available", "Important software updates are available", array->len); /* close any existing notification */ if (manager->priv->notification_updates != NULL) { notify_notification_close (manager->priv->notification_updates, NULL); manager->priv->notification_updates = NULL; } /* do the bubble */ g_debug ("title=%s, message=%s", title, message); notification = notify_notification_new (title, message, GSD_UPDATES_ICON_URGENT); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, 15000); notify_notification_set_urgency (notification, NOTIFY_URGENCY_CRITICAL); notify_notification_add_action (notification, "show-update-viewer", /* TRANSLATORS: button: open the update viewer to install updates*/ _("Install updates"), libnotify_action_cb, manager, NULL); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } /* track so we can prevent doubled notifications */ manager->priv->notification_updates = notification; g_object_add_weak_pointer (G_OBJECT (manager->priv->notification_updates), (void **) &manager->priv->notification_updates); } static void notify_normal_updates_maybe (GsdUpdatesManager *manager, GPtrArray *array) { const gchar *message; const gchar *title; gboolean ret; GError *error = NULL; guint64 time_last_notify; guint64 time_now; guint freq_updates_notify; NotifyNotification *notification; /* find out if enough time has passed since the last notification */ time_now = g_get_real_time () / 1000000; freq_updates_notify = g_settings_get_int (manager->priv->settings_gsd, GSD_SETTINGS_FREQUENCY_UPDATES_NOTIFICATION); g_settings_get (manager->priv->settings_gsd, GSD_SETTINGS_LAST_UPDATES_NOTIFICATION, "t", &time_last_notify); if (time_last_notify > 0 && (guint64) freq_updates_notify > time_now - time_last_notify) { g_debug ("not showing non-critical notification as already shown %i hours ago", (guint) (time_now - time_last_notify) / (60 * 60)); return; } /* TRANSLATORS: title in the libnotify popup */ title = ngettext ("Update", "Updates", array->len); /* TRANSLATORS: message when there are non-security updates */ message = ngettext ("A software update is available.", "Software updates are available.", array->len); /* close any existing notification */ if (manager->priv->notification_updates != NULL) { notify_notification_close (manager->priv->notification_updates, NULL); manager->priv->notification_updates = NULL; } /* do the bubble */ g_debug ("title=%s, message=%s", title, message); notification = notify_notification_new (title, message, GSD_UPDATES_ICON_NORMAL); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, 15000); notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL); notify_notification_add_action (notification, "show-update-viewer", /* TRANSLATORS: button: open the update viewer to install updates*/ _("Install updates"), libnotify_action_cb, manager, NULL); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } /* reset notification time */ g_settings_set (manager->priv->settings_gsd, GSD_SETTINGS_LAST_UPDATES_NOTIFICATION, "t", time_now); /* track so we can prevent doubled notifications */ manager->priv->notification_updates = notification; g_object_add_weak_pointer (G_OBJECT (manager->priv->notification_updates), (void **) &manager->priv->notification_updates); } static void notify_failed_get_updates_maybe (GsdUpdatesManager *manager) { const gchar *button; const gchar *message; const gchar *title; gboolean ret; GError *error = NULL; NotifyNotification *notification; /* give the user a break */ if (manager->priv->failed_get_updates_count++ < MAX_FAILED_GET_UPDATES) { g_debug ("failed GetUpdates, but will retry %i more times before notification", MAX_FAILED_GET_UPDATES - manager->priv->failed_get_updates_count); goto out; } /* TRANSLATORS: the updates mechanism */ title = _("Updates"); /* TRANSLATORS: we failed to get the updates multiple times, * and now we need to inform the user that something might be wrong */ message = _("Unable to access software updates"); /* TRANSLATORS: try again, this time launching the update viewer */ button = _("Try again"); notification = notify_notification_new (title, message, GSD_UPDATES_ICON_NORMAL); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, 120*1000); notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL); notify_notification_add_action (notification, "show-update-viewer", button, libnotify_action_cb, manager, NULL); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("failed to show notification: %s", error->message); g_error_free (error); } out: /* reset, even if the message failed */ manager->priv->failed_get_updates_count = 0; } static void check_updates_for_importance (GsdUpdatesManager *manager) { guint i; PkPackage *pkg; GPtrArray *important_array; /* check each package */ important_array = g_ptr_array_new (); for (i = 0; i < manager->priv->update_packages->len; i++) { pkg = g_ptr_array_index (manager->priv->update_packages, i); if (pk_package_get_info (pkg) == PK_INFO_ENUM_SECURITY || pk_package_get_info (pkg) == PK_INFO_ENUM_IMPORTANT) g_ptr_array_add (important_array, pkg); } if (important_array->len > 0) { notify_critical_updates (manager, important_array); } else { notify_normal_updates_maybe (manager, manager->priv->update_packages); } g_ptr_array_unref (important_array); } static void package_download_finished_cb (GObject *object, GAsyncResult *res, GsdUpdatesManager *manager) { PkClient *client = PK_CLIENT(object); PkResults *results; GError *error = NULL; PkError *error_code = NULL; /* get the results */ results = pk_client_generic_finish (PK_CLIENT(client), res, &error); if (results == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } g_warning ("failed to download: %s", error->message); g_error_free (error); notify_failed_get_updates_maybe (manager); return; } /* check error code */ error_code = pk_results_get_error_code (results); if (error_code != NULL) { g_warning ("failed to download: %s, %s", pk_error_enum_to_string (pk_error_get_code (error_code)), pk_error_get_details (error_code)); switch (pk_error_get_code (error_code)) { case PK_ERROR_ENUM_CANCELLED_PRIORITY: case PK_ERROR_ENUM_TRANSACTION_CANCELLED: g_debug ("ignoring error"); break; default: notify_failed_get_updates_maybe (manager); break; } goto out; } /* check to see if should notify */ check_updates_for_importance (manager); out: if (error_code != NULL) g_object_unref (error_code); if (results != NULL) g_object_unref (results); } static void auto_download_updates (GsdUpdatesManager *manager) { gchar **package_ids; guint i; PkPackage *pkg; /* download each package */ package_ids = g_new0 (gchar *, manager->priv->update_packages->len + 1); for (i = 0; i < manager->priv->update_packages->len; i++) { pkg = g_ptr_array_index (manager->priv->update_packages, i); package_ids[i] = g_strdup (pk_package_get_id (pkg)); } #if PK_CHECK_VERSION(0,8,1) /* we've set only-download in PkTask */ pk_task_update_packages_async (manager->priv->task, package_ids, manager->priv->cancellable, NULL, NULL, (GAsyncReadyCallback) package_download_finished_cb, manager); #else /* download them all */ pk_client_download_packages_async (PK_CLIENT(manager->priv->task), package_ids, NULL, /* this means system cache */ manager->priv->cancellable, NULL, NULL, (GAsyncReadyCallback) package_download_finished_cb, manager); #endif g_strfreev (package_ids); } static void get_updates_finished_cb (GObject *object, GAsyncResult *res, GsdUpdatesManager *manager) { PkClient *client = PK_CLIENT(object); PkResults *results; GError *error = NULL; gboolean ret; PkError *error_code = NULL; /* get the results */ results = pk_client_generic_finish (PK_CLIENT(client), res, &error); if (results == NULL) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } g_warning ("failed to get updates: %s", error->message); g_error_free (error); notify_failed_get_updates_maybe (manager); goto out; } /* check error code */ error_code = pk_results_get_error_code (results); if (error_code != NULL) { g_warning ("failed to get updates: %s, %s", pk_error_enum_to_string (pk_error_get_code (error_code)), pk_error_get_details (error_code)); switch (pk_error_get_code (error_code)) { case PK_ERROR_ENUM_CANCELLED_PRIORITY: case PK_ERROR_ENUM_TRANSACTION_CANCELLED: g_debug ("ignoring error"); break; default: notify_failed_get_updates_maybe (manager); break; } goto out; } /* we succeeded, so clear the count */ manager->priv->failed_get_updates_count = 0; /* so we can download or check for important & security updates */ if (manager->priv->update_packages != NULL) g_ptr_array_unref (manager->priv->update_packages); manager->priv->update_packages = pk_results_get_package_array (results); /* we have no updates */ if (manager->priv->update_packages->len == 0) { g_debug ("no updates"); goto out; } /* should we auto-download the updates? */ ret = g_settings_get_boolean (manager->priv->settings_gsd, GSD_SETTINGS_AUTO_DOWNLOAD_UPDATES); if (ret) { auto_download_updates (manager); goto out; } /* just check to see if should notify */ check_updates_for_importance (manager); out: if (error_code != NULL) g_object_unref (error_code); if (results != NULL) g_object_unref (results); } static void query_updates (GsdUpdatesManager *manager) { /* optimize the amount of downloaded data by setting the cache age */ pk_client_set_cache_age (PK_CLIENT(manager->priv->task), g_settings_get_int (manager->priv->settings_gsd, GSD_SETTINGS_FREQUENCY_GET_UPDATES)); /* get new update list */ pk_client_get_updates_async (PK_CLIENT(manager->priv->task), pk_bitfield_value (PK_FILTER_ENUM_NONE), manager->priv->cancellable, NULL, NULL, (GAsyncReadyCallback) get_updates_finished_cb, manager); } static void due_get_updates_cb (GsdUpdatesRefresh *refresh, GsdUpdatesManager *manager) { query_updates (manager); } static gchar * get_proxy_http (GsdUpdatesManager *manager) { gboolean ret; gchar *host = NULL; gchar *password = NULL; gchar *proxy = NULL; gchar *username = NULL; GString *string = NULL; guint port; GDesktopProxyMode proxy_mode; proxy_mode = g_settings_get_enum (manager->priv->settings_proxy, "mode"); if (proxy_mode != G_DESKTOP_PROXY_MODE_MANUAL) goto out; host = g_settings_get_string (manager->priv->settings_http, "host"); if (host == NULL) goto out; port = g_settings_get_int (manager->priv->settings_http, "port"); /* use an HTTP auth string? */ ret = g_settings_get_boolean (manager->priv->settings_http, "use-authentication"); if (ret) { username = g_settings_get_string (manager->priv->settings_http, "authentication-user"); password = g_settings_get_string (manager->priv->settings_http, "authentication-password"); } /* make PackageKit proxy string */ string = g_string_new (host); if (port > 0) g_string_append_printf (string, ":%i", port); if (username != NULL && password != NULL) g_string_append_printf (string, "@%s:%s", username, password); else if (username != NULL) g_string_append_printf (string, "@%s", username); else if (password != NULL) g_string_append_printf (string, "@:%s", password); proxy = g_string_free (string, FALSE); out: g_free (host); g_free (username); g_free (password); return proxy; } static gchar * get_proxy_ftp (GsdUpdatesManager *manager) { gchar *host = NULL; gchar *proxy = NULL; GString *string = NULL; guint port; GDesktopProxyMode proxy_mode; proxy_mode = g_settings_get_enum (manager->priv->settings_proxy, "mode"); if (proxy_mode != G_DESKTOP_PROXY_MODE_MANUAL) goto out; host = g_settings_get_string (manager->priv->settings_ftp, "host"); if (host == NULL) goto out; port = g_settings_get_int (manager->priv->settings_ftp, "port"); if (port == 0) goto out; /* make PackageKit proxy string */ string = g_string_new (host); if (port > 0) g_string_append_printf (string, ":%i", port); proxy = g_string_free (string, FALSE); out: g_free (host); return proxy; } static void set_proxy_cb (GObject *object, GAsyncResult *res, gpointer user_data) { gboolean ret; GError *error = NULL; PkControl *control = PK_CONTROL (object); /* get the result */ ret = pk_control_set_proxy_finish (control, res, &error); if (!ret) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("failed to set proxies: %s", error->message); g_error_free (error); } } static void reload_proxy_settings (GsdUpdatesManager *manager) { gchar *proxy_http; gchar *proxy_ftp; proxy_http = get_proxy_http (manager); proxy_ftp = get_proxy_ftp (manager); /* send to daemon */ pk_control_set_proxy_async (manager->priv->control, proxy_http, proxy_ftp, NULL, set_proxy_cb, manager); g_free (proxy_http); g_free (proxy_ftp); } static void settings_changed_cb (GSettings *settings, const char *key, GsdUpdatesManager *manager) { reload_proxy_settings (manager); } static void settings_gsd_changed_cb (GSettings *settings, const char *key, GsdUpdatesManager *manager) { } static void session_inhibit (GsdUpdatesManager *manager) { const gchar *reason; GError *error = NULL; GVariant *retval = NULL; /* state invalid somehow */ if (manager->priv->inhibit_cookie != 0) { g_warning ("already locked"); goto out; } /* TRANSLATORS: the reason why we've inhibited it */ reason = _("A transaction that cannot be interrupted is running"); retval = g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->proxy_session), "Inhibit", g_variant_new ("(susu)", "gnome-settings-daemon", /* app-id */ 0, /* xid */ reason, /* reason */ 4 /* flags */), G_DBUS_CALL_FLAGS_NONE, -1, manager->priv->cancellable, &error); if (retval == NULL) { g_warning ("failed to inhibit gnome-session: %s", error->message); g_error_free (error); goto out; } /* get cookie */ g_variant_get (retval, "(u)", &manager->priv->inhibit_cookie); out: if (retval != NULL) g_variant_unref (retval); } static void session_uninhibit (GsdUpdatesManager *manager) { GError *error = NULL; GVariant *retval = NULL; /* state invalid somehow */ if (manager->priv->inhibit_cookie == 0) { g_warning ("not locked"); goto out; } retval = g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->proxy_session), "Uninhibit", g_variant_new ("(u)", manager->priv->inhibit_cookie), G_DBUS_CALL_FLAGS_NONE, -1, manager->priv->cancellable, &error); if (retval == NULL) { g_warning ("failed to uninhibit gnome-session: %s", error->message); g_error_free (error); goto out; } out: manager->priv->inhibit_cookie = 0; if (retval != NULL) g_variant_unref (retval); } static void notify_locked_cb (PkControl *control, GParamSpec *pspec, GsdUpdatesManager *manager) { gboolean locked; g_object_get (control, "locked", &locked, NULL); /* TODO: locked is a bit harsh, we can probably still allow * reboot when packages are downloading or the transaction is * depsolving */ if (locked) { session_inhibit (manager); } else { session_uninhibit (manager); } } static void update_viewer_appeared_cb (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GsdUpdatesManager *manager = GSD_UPDATES_MANAGER (user_data); /* close any existing notification */ if (manager->priv->notification_updates != NULL) { g_debug ("update viewer on the bus, clearing bubble"); notify_notification_close (manager->priv->notification_updates, NULL); manager->priv->notification_updates = NULL; } } static gboolean file_exists_in_root (const gchar *root, const gchar *filename) { gboolean ret = FALSE; GFile *source; gchar *source_path; source_path = g_build_filename (root, filename, NULL); source = g_file_new_for_path (source_path); /* ignore virtual mountpoints */ if (!g_file_is_native (source)) goto out; /* an interesting file exists */ ret = g_file_query_exists (source, NULL); g_debug ("checking for %s: %s", source_path, ret ? "yes" : "no"); if (!ret) goto out; out: g_free (source_path); g_object_unref (source); return ret; } static void mount_added_cb (GVolumeMonitor *volume_monitor, GMount *mount, GsdUpdatesManager *manager) { gboolean ret = FALSE; gchar **filenames = NULL; gchar *media_repo_filenames; gchar *root_path; GFile *root; guint i; /* check if any installed media is an install disk */ root = g_mount_get_root (mount); root_path = g_file_get_path (root); /* use settings */ media_repo_filenames = g_settings_get_string (manager->priv->settings_gsd, GSD_SETTINGS_MEDIA_REPO_FILENAMES); if (media_repo_filenames == NULL) { g_warning ("failed to get media repo filenames"); goto out; } /* search each possible filename */ filenames = g_strsplit (media_repo_filenames, ",", -1); for (i=0; filenames[i] != NULL; i++) { ret = file_exists_in_root (root_path, filenames[i]); if (ret) break; } /* do an updates check with the new media */ if (ret) query_updates (manager); out: g_strfreev (filenames); g_free (media_repo_filenames); g_free (root_path); g_object_unref (root); } #define PK_OFFLINE_UPDATE_RESULTS_GROUP "PackageKit Offline Update Results" #define PK_OFFLINE_UPDATE_RESULTS_FILENAME "/var/lib/PackageKit/offline-update-competed" static gboolean check_offline_update_cb (gpointer user_data) { const gchar *message; const gchar *title; gboolean ret; gboolean success; gchar *error_code = NULL; gchar *error_details = NULL; gchar *packages = NULL; GError *error = NULL; GKeyFile *key_file = NULL; GsdUpdatesManager *manager = (GsdUpdatesManager *) user_data; guint i; guint num_packages = 1; NotifyNotification *notification; PkErrorEnum error_enum = PK_ERROR_ENUM_UNKNOWN; /* was any offline update attempted */ if (!g_file_test (PK_OFFLINE_UPDATE_RESULTS_FILENAME, G_FILE_TEST_EXISTS)) goto out; /* open the file and see what happened */ key_file = g_key_file_new (); ret = g_key_file_load_from_file (key_file, PK_OFFLINE_UPDATE_RESULTS_FILENAME, G_KEY_FILE_NONE, &error); if (!ret) { g_warning ("failed to open %s: %s", PK_OFFLINE_UPDATE_RESULTS_FILENAME, error->message); g_error_free (error); goto out; } success = g_key_file_get_boolean (key_file, PK_OFFLINE_UPDATE_RESULTS_GROUP, "Success", NULL); if (success) { packages = g_key_file_get_string (key_file, PK_OFFLINE_UPDATE_RESULTS_GROUP, "Packages", NULL); if (packages == NULL) { g_warning ("No 'Packages' in %s", PK_OFFLINE_UPDATE_RESULTS_FILENAME); goto out; } /* count the packages for translators */ for (i = 0; packages[i] != '\0'; i++) { if (packages[i] == ',') num_packages++; } /* TRANSLATORS: title in the libnotify popup */ title = ngettext ("Software Update Installed", "Software Updates Installed", num_packages); /* TRANSLATORS: message when we've done offline updates */ message = ngettext ("An important OS update has been installed.", "Important OS updates have been installed.", num_packages); /* no need to keep the file around anymore */ clear_offline_updates_message (); } else { /* get error details */ manager->priv->offline_update_error = pk_error_new (); error_code = g_key_file_get_string (key_file, PK_OFFLINE_UPDATE_RESULTS_GROUP, "ErrorCode", NULL); if (error_code != NULL) error_enum = pk_error_enum_from_string (error_code); error_details = g_key_file_get_string (key_file, PK_OFFLINE_UPDATE_RESULTS_GROUP, "ErrorDetails", NULL); g_object_set (manager->priv->offline_update_error, "code", error_enum, "details", error_details, NULL); /* TRANSLATORS: title in the libnotify popup */ title = _("Software Updates Failed"); /* TRANSLATORS: message when we've not done offline updates */ message = _("An important OS update failed to be installed."); } /* do the bubble */ g_debug ("title=%s, message=%s", title, message); notification = notify_notification_new (title, message, GSD_UPDATES_ICON_URGENT); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, -1); notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL); if (success) { #if 0 notify_notification_add_action (notification, "review-offline-updates", /* TRANSLATORS: button: review the offline update changes */ _("Review"), libnotify_action_cb, manager, NULL); #endif } else { notify_notification_add_action (notification, "error-offline-updates", /* TRANSLATORS: button: review the offline update changes */ _("Show details"), libnotify_action_cb, manager, NULL); } notify_notification_add_action (notification, "clear-offline-updates", /* TRANSLATORS: button: clear notification */ _("OK"), libnotify_action_cb, manager, NULL); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } out: g_free (packages); g_free (error_code); g_free (error_details); if (key_file != NULL) g_key_file_free (key_file); manager->priv->offline_update_id = 0; return FALSE; } gboolean gsd_updates_manager_start (GsdUpdatesManager *manager, GError **error) { gboolean ret = FALSE; g_debug ("Starting updates manager"); /* use PackageKit */ manager->priv->cancellable = g_cancellable_new (); manager->priv->control = pk_control_new (); g_signal_connect (manager->priv->control, "notify::locked", G_CALLBACK (notify_locked_cb), manager); manager->priv->task = pk_task_new (); g_object_set (manager->priv->task, "background", TRUE, "interactive", FALSE, #if PK_CHECK_VERSION(0,8,1) "only-download", TRUE, #endif NULL); /* watch UDev for missing firmware */ manager->priv->firmware = gsd_updates_firmware_new (); /* get automatic callbacks about when we should check for * updates, refresh-caches and upgrades */ manager->priv->refresh = gsd_updates_refresh_new (); g_signal_connect (manager->priv->refresh, "get-upgrades", G_CALLBACK (due_get_upgrades_cb), manager); g_signal_connect (manager->priv->refresh, "refresh-cache", G_CALLBACK (due_refresh_cache_cb), manager); g_signal_connect (manager->priv->refresh, "get-updates", G_CALLBACK (due_get_updates_cb), manager); /* get proxy settings */ manager->priv->settings_proxy = g_settings_new ("org.gnome.system.proxy"); g_signal_connect (manager->priv->settings_proxy, "changed", G_CALLBACK (settings_changed_cb), manager); /* get http settings */ manager->priv->settings_http = g_settings_new ("org.gnome.system.proxy.http"); g_signal_connect (manager->priv->settings_http, "changed", G_CALLBACK (settings_changed_cb), manager); /* get ftp settings */ manager->priv->settings_ftp = g_settings_new ("org.gnome.system.proxy.ftp"); g_signal_connect (manager->priv->settings_ftp, "changed", G_CALLBACK (settings_changed_cb), manager); /* get ftp settings */ manager->priv->settings_gsd = g_settings_new ("org.gnome.settings-daemon.plugins.updates"); g_signal_connect (manager->priv->settings_gsd, "changed", G_CALLBACK (settings_gsd_changed_cb), manager); /* use gnome-session for the idle detection */ manager->priv->proxy_session = gnome_settings_bus_get_session_proxy (); if (manager->priv->proxy_session == NULL) goto out; /* if the update viewer is started, then hide the notification */ manager->priv->update_viewer_watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION, "org.freedesktop.PackageKit.UpdateViewer", G_BUS_NAME_WATCHER_FLAGS_NONE, update_viewer_appeared_cb, NULL, manager, NULL); /* get a volume monitor so we can watch media */ manager->priv->volume_monitor = g_volume_monitor_get (); g_signal_connect (manager->priv->volume_monitor, "mount-added", G_CALLBACK (mount_added_cb), manager); /* coldplug */ reload_proxy_settings (manager); /* check for offline update */ manager->priv->offline_update_id = g_timeout_add_seconds (GSD_UPDATES_CHECK_OFFLINE_TIMEOUT, check_offline_update_cb, manager); /* success */ ret = TRUE; g_debug ("Started updates manager"); out: return ret; } void gsd_updates_manager_stop (GsdUpdatesManager *manager) { g_debug ("Stopping updates manager"); g_clear_object (&manager->priv->settings_proxy); g_clear_object (&manager->priv->settings_http); g_clear_object (&manager->priv->settings_ftp); g_clear_object (&manager->priv->settings_gsd); g_clear_object (&manager->priv->control); g_clear_object (&manager->priv->task); g_clear_object (&manager->priv->refresh); g_clear_object (&manager->priv->firmware); g_clear_object (&manager->priv->proxy_session); g_clear_object (&manager->priv->volume_monitor); if (manager->priv->cancellable) { g_cancellable_cancel (manager->priv->cancellable); g_clear_object (&manager->priv->cancellable); } if (manager->priv->update_viewer_watcher_id != 0) { g_bus_unwatch_name (manager->priv->update_viewer_watcher_id); manager->priv->update_viewer_watcher_id = 0; } if (manager->priv->offline_update_id) { g_source_remove (manager->priv->offline_update_id); manager->priv->offline_update_id = 0; } if (manager->priv->update_packages != NULL) { g_ptr_array_unref (manager->priv->update_packages); manager->priv->update_packages = NULL; } g_clear_object (&manager->priv->offline_update_error); } static GObject * gsd_updates_manager_constructor ( GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdUpdatesManager *m; m = GSD_UPDATES_MANAGER (G_OBJECT_CLASS (gsd_updates_manager_parent_class)->constructor ( type, n_construct_properties, construct_properties)); return G_OBJECT (m); } static void gsd_updates_manager_dispose (GObject *object) { GsdUpdatesManager *manager; manager = GSD_UPDATES_MANAGER (object); gsd_updates_manager_stop (manager); G_OBJECT_CLASS (gsd_updates_manager_parent_class)->dispose (object); } static void gsd_updates_manager_class_init (GsdUpdatesManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_updates_manager_constructor; object_class->dispose = gsd_updates_manager_dispose; g_type_class_add_private (klass, sizeof (GsdUpdatesManagerPrivate)); } static void gsd_updates_manager_init (GsdUpdatesManager *manager) { manager->priv = GSD_UPDATES_MANAGER_GET_PRIVATE (manager); } GsdUpdatesManager * gsd_updates_manager_new (void) { if (manager_object) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_UPDATES_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_UPDATES_MANAGER (manager_object); } ./plugins/updates/gsd-updates-manager.h0000644000004100000410000000420512735467744020416 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2011 Richard Hughes * * 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. * */ #ifndef __GSD_UPDATES_MANAGER_H #define __GSD_UPDATES_MANAGER_H #include #include G_BEGIN_DECLS #define GSD_TYPE_UPDATES_MANAGER (gsd_updates_manager_get_type ()) #define GSD_UPDATES_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManager)) #define GSD_UPDATES_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManagerClass)) #define GSD_IS_UPDATES_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_UPDATES_MANAGER)) #define GSD_IS_UPDATES_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_UPDATES_MANAGER)) #define GSD_UPDATES_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_UPDATES_MANAGER, GsdUpdatesManagerClass)) typedef struct GsdUpdatesManagerPrivate GsdUpdatesManagerPrivate; typedef struct { GObject parent; GsdUpdatesManagerPrivate *priv; } GsdUpdatesManager; typedef struct { GObjectClass parent_class; } GsdUpdatesManagerClass; GType gsd_updates_manager_get_type (void) G_GNUC_CONST; GsdUpdatesManager *gsd_updates_manager_new (void); gboolean gsd_updates_manager_start (GsdUpdatesManager *manager, GError **error); void gsd_updates_manager_stop (GsdUpdatesManager *manager); G_END_DECLS #endif /* __GSD_UPDATES_MANAGER_H */ ./plugins/updates/gsd-updates-refresh.c0000644000004100000410000004757712735467763020461 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2007-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include "gnome-settings-bus.h" #include "gsd-updates-common.h" #include "gsd-updates-refresh.h" static void gsd_updates_refresh_finalize (GObject *object); #define GSD_UPDATES_REFRESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_UPDATES_REFRESH, GsdUpdatesRefreshPrivate)) #define PERIODIC_CHECK_TIME 60*60 /* poke PackageKit every hour */ #define LOGIN_TIMEOUT 3 /* seconds */ #define SESSION_STARTUP_TIMEOUT 10 /* seconds */ enum { PRESENCE_STATUS_AVAILABLE = 0, PRESENCE_STATUS_INVISIBLE, PRESENCE_STATUS_BUSY, PRESENCE_STATUS_IDLE, PRESENCE_STATUS_UNKNOWN }; /* * at startup, after a small delay, force a GetUpdates call * every hour (or any event) check: - if we are online, idle and on AC power, it's been more than a day since we refreshed then RefreshCache - if we are online and it's been longer than the timeout since getting the updates period then GetUpdates */ struct GsdUpdatesRefreshPrivate { gboolean session_idle; gboolean on_battery; gboolean network_active; guint timeout_id; guint periodic_id; UpClient *client; GSettings *settings; GsdSessionManager *proxy_session; PkControl *control; }; enum { REFRESH_CACHE, GET_UPDATES, GET_UPGRADES, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (GsdUpdatesRefresh, gsd_updates_refresh, G_TYPE_OBJECT) static void gsd_updates_refresh_class_init (GsdUpdatesRefreshClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_updates_refresh_finalize; g_type_class_add_private (klass, sizeof (GsdUpdatesRefreshPrivate)); signals [REFRESH_CACHE] = g_signal_new ("refresh-cache", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals [GET_UPDATES] = g_signal_new ("get-updates", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals [GET_UPGRADES] = g_signal_new ("get-upgrades", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void get_time_refresh_cache_cb (GObject *object, GAsyncResult *res, GsdUpdatesRefresh *refresh) { PkControl *control = PK_CONTROL (object); GError *error = NULL; guint seconds; guint thresh; /* get the result */ seconds = pk_control_get_time_since_action_finish (control, res, &error); if (seconds == 0) { g_warning ("failed to get time: %s", error->message); g_error_free (error); return; } /* have we passed the timout? */ thresh = g_settings_get_int (refresh->priv->settings, GSD_SETTINGS_FREQUENCY_GET_UPDATES); if (seconds < thresh) { g_debug ("not before timeout, thresh=%u, now=%u", thresh, seconds); return; } /* send signal */ g_debug ("emitting refresh-cache"); g_signal_emit (refresh, signals [REFRESH_CACHE], 0); } static void maybe_refresh_cache (GsdUpdatesRefresh *refresh) { guint thresh; g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh)); /* if we don't want to auto check for updates, don't do this either */ thresh = g_settings_get_int (refresh->priv->settings, GSD_SETTINGS_FREQUENCY_GET_UPDATES); if (thresh == 0) { g_debug ("not when policy is set to never"); return; } /* only do the refresh cache when the user is idle */ if (!refresh->priv->session_idle) { g_debug ("not when session active"); return; } /* get this each time, as it may have changed behind out back */ thresh = g_settings_get_int (refresh->priv->settings, GSD_SETTINGS_FREQUENCY_REFRESH_CACHE); if (thresh == 0) { g_debug ("not when policy is set to never"); return; } /* get the time since the last refresh */ pk_control_get_time_since_action_async (refresh->priv->control, PK_ROLE_ENUM_REFRESH_CACHE, NULL, (GAsyncReadyCallback) get_time_refresh_cache_cb, refresh); } static void get_time_get_updates_cb (GObject *object, GAsyncResult *res, GsdUpdatesRefresh *refresh) { PkControl *control = PK_CONTROL (object); GError *error = NULL; guint seconds; guint thresh; /* get the result */ seconds = pk_control_get_time_since_action_finish (control, res, &error); if (seconds == 0) { g_warning ("failed to get time: %s", error->message); g_error_free (error); return; } /* have we passed the timout? */ thresh = g_settings_get_int (refresh->priv->settings, GSD_SETTINGS_FREQUENCY_GET_UPDATES); if (seconds < thresh) { g_debug ("not before timeout, thresh=%u, now=%u", thresh, seconds); return; } /* send signal */ g_debug ("emitting get-updates"); g_signal_emit (refresh, signals [GET_UPDATES], 0); } static void maybe_get_updates (GsdUpdatesRefresh *refresh) { guint thresh; g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh)); /* if we don't want to auto check for updates, don't do this either */ thresh = g_settings_get_int (refresh->priv->settings, GSD_SETTINGS_FREQUENCY_GET_UPDATES); if (thresh == 0) { g_debug ("not when policy is set to never"); return; } /* get the time since the last refresh */ pk_control_get_time_since_action_async (refresh->priv->control, PK_ROLE_ENUM_GET_UPDATES, NULL, (GAsyncReadyCallback) get_time_get_updates_cb, refresh); } static void get_time_get_upgrades_cb (GObject *object, GAsyncResult *res, GsdUpdatesRefresh *refresh) { PkControl *control = PK_CONTROL (object); GError *error = NULL; guint seconds; guint thresh; /* get the result */ seconds = pk_control_get_time_since_action_finish (control, res, &error); if (seconds == 0) { g_warning ("failed to get time: %s", error->message); g_error_free (error); return; } /* have we passed the timout? */ thresh = g_settings_get_int (refresh->priv->settings, GSD_SETTINGS_FREQUENCY_GET_UPDATES); if (seconds < thresh) { g_debug ("not before timeout, thresh=%u, now=%u", thresh, seconds); return; } /* send signal */ g_debug ("emitting get-upgrades"); g_signal_emit (refresh, signals [GET_UPGRADES], 0); } static void maybe_get_upgrades (GsdUpdatesRefresh *refresh) { guint thresh; g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh)); /* get this each time, as it may have changed behind out back */ thresh = g_settings_get_int (refresh->priv->settings, GSD_SETTINGS_FREQUENCY_GET_UPGRADES); if (thresh == 0) { g_debug ("not when policy is set to never"); return; } /* get the time since the last refresh */ pk_control_get_time_since_action_async (refresh->priv->control, PK_ROLE_ENUM_GET_DISTRO_UPGRADES, NULL, (GAsyncReadyCallback) get_time_get_upgrades_cb, refresh); } static gboolean change_state_cb (GsdUpdatesRefresh *refresh) { /* check all actions */ maybe_refresh_cache (refresh); maybe_get_updates (refresh); maybe_get_upgrades (refresh); return FALSE; } static gboolean change_state (GsdUpdatesRefresh *refresh) { gboolean ret; g_return_val_if_fail (GSD_IS_UPDATES_REFRESH (refresh), FALSE); /* no point continuing if we have no network */ if (!refresh->priv->network_active) { g_debug ("not when no network"); return FALSE; } /* not on battery unless overridden */ ret = g_settings_get_boolean (refresh->priv->settings, GSD_SETTINGS_UPDATE_BATTERY); if (!ret && refresh->priv->on_battery) { g_debug ("not when on battery"); return FALSE; } /* wait a little time for things to settle down */ if (refresh->priv->timeout_id != 0) g_source_remove (refresh->priv->timeout_id); g_debug ("defering action for %i seconds", SESSION_STARTUP_TIMEOUT); refresh->priv->timeout_id = g_timeout_add_seconds (SESSION_STARTUP_TIMEOUT, (GSourceFunc) change_state_cb, refresh); g_source_set_name_by_id (refresh->priv->timeout_id, "[GsdUpdatesRefresh] change-state"); return TRUE; } static void settings_key_changed_cb (GSettings *client, const gchar *key, GsdUpdatesRefresh *refresh) { g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh)); if (g_strcmp0 (key, GSD_SETTINGS_FREQUENCY_GET_UPDATES) == 0 || g_strcmp0 (key, GSD_SETTINGS_FREQUENCY_GET_UPGRADES) == 0 || g_strcmp0 (key, GSD_SETTINGS_FREQUENCY_REFRESH_CACHE) == 0 || g_strcmp0 (key, GSD_SETTINGS_UPDATE_BATTERY) == 0) change_state (refresh); } static gboolean convert_network_state (GsdUpdatesRefresh *refresh, PkNetworkEnum state) { /* offline */ if (state == PK_NETWORK_ENUM_OFFLINE) return FALSE; /* online */ if (state == PK_NETWORK_ENUM_ONLINE || state == PK_NETWORK_ENUM_WIFI || state == PK_NETWORK_ENUM_WIRED) return TRUE; /* check policy */ if (state == PK_NETWORK_ENUM_MOBILE) return g_settings_get_boolean (refresh->priv->settings, GSD_SETTINGS_CONNECTION_USE_MOBILE); /* not recognised */ g_warning ("state unknown: %i", state); return TRUE; } static void notify_network_state_cb (PkControl *control, GParamSpec *pspec, GsdUpdatesRefresh *refresh) { PkNetworkEnum state; g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh)); g_object_get (control, "network-state", &state, NULL); refresh->priv->network_active = convert_network_state (refresh, state); g_debug ("setting online %i", refresh->priv->network_active); if (refresh->priv->network_active) change_state (refresh); } static gboolean periodic_timeout_cb (gpointer user_data) { GsdUpdatesRefresh *refresh = GSD_UPDATES_REFRESH (user_data); g_return_val_if_fail (GSD_IS_UPDATES_REFRESH (refresh), FALSE); /* debug so we can catch polling */ g_debug ("polling check"); /* triggered once an hour */ change_state (refresh); /* always return */ return TRUE; } static void gsd_updates_refresh_client_changed_cb (UpClient *client, GsdUpdatesRefresh *refresh) { gboolean on_battery; g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh)); /* get the on-battery state */ on_battery = up_client_get_on_battery (refresh->priv->client); if (on_battery == refresh->priv->on_battery) { g_debug ("same state as before, ignoring"); return; } /* save in local cache */ g_debug ("setting on_battery %i", on_battery); refresh->priv->on_battery = on_battery; if (!on_battery) change_state (refresh); } static void get_properties_cb (GObject *object, GAsyncResult *res, GsdUpdatesRefresh *refresh) { PkNetworkEnum state; GError *error = NULL; PkControl *control = PK_CONTROL(object); gboolean ret; /* get the result */ ret = pk_control_get_properties_finish (control, res, &error); if (!ret) { /* TRANSLATORS: backend is broken, and won't tell us what it supports */ g_warning ("could not get properties"); g_error_free (error); goto out; } /* get values */ g_object_get (control, "network-state", &state, NULL); refresh->priv->network_active = convert_network_state (refresh, state); out: return; } static void session_presence_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, GsdUpdatesRefresh *refresh) { guint status; g_return_if_fail (GSD_IS_UPDATES_REFRESH (refresh)); if (g_strcmp0 (signal_name, "StatusChanged") != 0) return; /* map status code into boolean */ g_variant_get (parameters, "(u)", &status); refresh->priv->session_idle = (status == PRESENCE_STATUS_IDLE); g_debug ("setting is_idle %i", refresh->priv->session_idle); if (refresh->priv->session_idle) change_state (refresh); } static void gsd_updates_refresh_init (GsdUpdatesRefresh *refresh) { GVariant *status; guint status_code; refresh->priv = GSD_UPDATES_REFRESH_GET_PRIVATE (refresh); refresh->priv->on_battery = FALSE; refresh->priv->network_active = FALSE; refresh->priv->timeout_id = 0; refresh->priv->periodic_id = 0; /* we need to know the updates frequency */ refresh->priv->settings = g_settings_new (GSD_SETTINGS_SCHEMA); g_signal_connect (refresh->priv->settings, "changed", G_CALLBACK (settings_key_changed_cb), refresh); /* we need to query the last cache refresh time */ refresh->priv->control = pk_control_new (); g_signal_connect (refresh->priv->control, "notify::network-state", G_CALLBACK (notify_network_state_cb), refresh); /* get network state */ pk_control_get_properties_async (refresh->priv->control, NULL, (GAsyncReadyCallback) get_properties_cb, refresh); /* use a UpClient */ refresh->priv->client = up_client_new (); g_signal_connect (refresh->priv->client, "changed", G_CALLBACK (gsd_updates_refresh_client_changed_cb), refresh); /* get the battery state */ refresh->priv->on_battery = up_client_get_on_battery (refresh->priv->client); g_debug ("setting on battery %i", refresh->priv->on_battery); /* use gnome-session for the idle detection */ refresh->priv->proxy_session = gnome_settings_bus_get_session_proxy (); if (refresh->priv->proxy_session != NULL) { g_signal_connect (G_DBUS_PROXY (refresh->priv->proxy_session), "g-signal", G_CALLBACK (session_presence_signal_cb), refresh); status = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (refresh->priv->proxy_session), "status"); if (status) { g_variant_get (status, "u", &status_code); refresh->priv->session_idle = (status_code == PRESENCE_STATUS_IDLE); g_variant_unref (status); } else { refresh->priv->session_idle = FALSE; } } /* we check this in case we miss one of the async signals */ refresh->priv->periodic_id = g_timeout_add_seconds (PERIODIC_CHECK_TIME, periodic_timeout_cb, refresh); g_source_set_name_by_id (refresh->priv->periodic_id, "[GsdUpdatesRefresh] periodic check"); /* check system state */ change_state (refresh); } static void gsd_updates_refresh_finalize (GObject *object) { GsdUpdatesRefresh *refresh; g_return_if_fail (GSD_IS_UPDATES_REFRESH (object)); refresh = GSD_UPDATES_REFRESH (object); g_return_if_fail (refresh->priv != NULL); if (refresh->priv->timeout_id != 0) g_source_remove (refresh->priv->timeout_id); if (refresh->priv->periodic_id != 0) g_source_remove (refresh->priv->periodic_id); g_signal_handlers_disconnect_by_data (refresh->priv->client, refresh); g_signal_handlers_disconnect_by_data (refresh->priv->proxy_session, refresh); g_object_unref (refresh->priv->control); g_object_unref (refresh->priv->settings); g_object_unref (refresh->priv->client); if (refresh->priv->proxy_session != NULL) g_object_unref (refresh->priv->proxy_session); G_OBJECT_CLASS (gsd_updates_refresh_parent_class)->finalize (object); } GsdUpdatesRefresh * gsd_updates_refresh_new (void) { GsdUpdatesRefresh *refresh; refresh = g_object_new (GSD_TYPE_UPDATES_REFRESH, NULL); return GSD_UPDATES_REFRESH (refresh); } ./plugins/updates/gsd-updates-firmware.c0000644000004100000410000010750412735467744020621 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007-2012 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #include #include #include #include #ifdef HAVE_GUDEV #include #endif #include "gsd-updates-common.h" #include "gsd-updates-firmware.h" static void gsd_updates_firmware_finalize (GObject *object); #define GSD_UPDATES_FIRMWARE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_UPDATES_TYPE_FIRMWARE, GsdUpdatesFirmwarePrivate)) #define GSD_UPDATES_FIRMWARE_MISSING_DIR "/run/udev/firmware-missing" #define GSD_UPDATES_FIRMWARE_LOADING_DIR "/lib/firmware" #define GSD_UPDATES_FIRMWARE_LOGIN_DELAY 10 /* seconds */ #define GSD_UPDATES_FIRMWARE_PROCESS_DELAY 2 /* seconds */ #define GSD_UPDATES_FIRMWARE_INSERT_DELAY 2 /* seconds */ #define GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM "/usr/sbin/pk-device-rebind" struct GsdUpdatesFirmwarePrivate { GSettings *settings; GFileMonitor *monitor; GPtrArray *array_requested; PkTask *task; GPtrArray *packages_found; guint timeout_id; }; typedef enum { FIRMWARE_SUBSYSTEM_USB, FIRMWARE_SUBSYSTEM_PCI, FIRMWARE_SUBSYSTEM_UNKNOWN } FirmwareSubsystem; typedef struct { gchar *filename; gchar *sysfs_path; gchar *model; gchar *id; FirmwareSubsystem subsystem; } GsdUpdatesFirmwareRequest; G_DEFINE_TYPE (GsdUpdatesFirmware, gsd_updates_firmware, G_TYPE_OBJECT) static void install_package_ids (GsdUpdatesFirmware *firmware); static void ignore_devices (GsdUpdatesFirmware *firmware); static gboolean subsystem_can_replug (FirmwareSubsystem subsystem) { if (subsystem == FIRMWARE_SUBSYSTEM_USB) return TRUE; return FALSE; } static GsdUpdatesFirmwareRequest * request_new (const gchar *filename, const gchar *sysfs_path) { GsdUpdatesFirmwareRequest *req; #ifdef HAVE_GUDEV GUdevDevice *device; GUdevClient *client; const gchar *subsystem; const gchar *model; const gchar *id_vendor; const gchar *id_product; #endif req = g_new0 (GsdUpdatesFirmwareRequest, 1); req->filename = g_strdup (filename); req->sysfs_path = g_strdup (sysfs_path); req->subsystem = FIRMWARE_SUBSYSTEM_UNKNOWN; #ifdef HAVE_GUDEV /* get all subsystems */ client = g_udev_client_new (NULL); device = g_udev_client_query_by_sysfs_path (client, sysfs_path); if (device == NULL) goto out; /* find subsystem, which will affect if we have to replug, or reboot */ subsystem = g_udev_device_get_subsystem (device); if (g_strcmp0 (subsystem, "usb") == 0) { req->subsystem = FIRMWARE_SUBSYSTEM_USB; } else if (g_strcmp0 (subsystem, "pci") == 0) { req->subsystem = FIRMWARE_SUBSYSTEM_PCI; } else { g_warning ("subsystem unrecognised: %s", subsystem); } /* get model, so we can show something sensible */ model = g_udev_device_get_property (device, "ID_MODEL"); if (model != NULL && model[0] != '\0') { req->model = g_strdup (model); /* replace invalid chars */ g_strdelimit (req->model, "_", ' '); } /* create ID so we can ignore the specific device */ id_vendor = g_udev_device_get_property (device, "ID_VENDOR"); id_product = g_udev_device_get_property (device, "ID_MODEL_ID"); req->id = g_strdup_printf ("%s_%s", id_vendor, id_product); out: if (device != NULL) g_object_unref (device); g_object_unref (client); #endif return req; } static void request_free (GsdUpdatesFirmwareRequest *req) { g_free (req->filename); g_free (req->model); g_free (req->sysfs_path); g_free (req->id); g_free (req); } static gboolean device_rebind (GsdUpdatesFirmware *firmware) { gboolean ret; gchar *argv[4]; gchar *rebind_stderr = NULL; gchar *rebind_stdout = NULL; GError *error = NULL; gint exit_status = 0; guint i; GPtrArray *array; const GsdUpdatesFirmwareRequest *req; GString *string; string = g_string_new (""); /* make a string array of all the devices to replug */ array = firmware->priv->array_requested; for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); g_string_append_printf (string, "%s ", req->sysfs_path); } /* remove trailing space */ if (string->len > 0) g_string_set_size (string, string->len-1); /* use PolicyKit to do this as root */ argv[0] = "pkexec"; argv[1] = GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM; argv[2] = string->str; argv[3] = NULL; ret = g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &rebind_stdout, &rebind_stderr, &exit_status, &error); if (!ret) { g_warning ("failed to spawn '%s': %s", argv[1], error->message); g_error_free (error); goto out; } /* if we failed to rebind the device */ if (exit_status != 0) { g_warning ("failed to rebind: %s, %s", rebind_stdout, rebind_stderr); ret = FALSE; goto out; } out: g_free (rebind_stdout); g_free (rebind_stderr); g_string_free (string, TRUE); return ret; } static void libnotify_cb (NotifyNotification *notification, gchar *action, gpointer data) { GsdUpdatesFirmware *firmware = GSD_UPDATES_FIRMWARE (data); if (g_strcmp0 (action, "install-firmware") == 0) { install_package_ids (firmware); } else if (g_strcmp0 (action, "ignore-devices") == 0) { ignore_devices (firmware); } else { g_warning ("unknown action id: %s", action); } notify_notification_close (notification, NULL); } static void on_notification_closed (NotifyNotification *notification, gpointer data) { g_object_unref (notification); } static void require_restart (GsdUpdatesFirmware *firmware) { const gchar *message; gboolean ret; GError *error = NULL; NotifyNotification *notification; /* TRANSLATORS: we need to restart so the new hardware can re-request the firmware */ message = _("You will need to restart this computer before the hardware will work correctly."); /* TRANSLATORS: title of libnotify bubble */ notification = notify_notification_new (_("Additional software was installed"), message, NULL); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER); notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); /* show the bubble */ ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } } static void require_replug (GsdUpdatesFirmware *firmware) { const gchar *message; gboolean ret; GError *error = NULL; NotifyNotification *notification; /* TRANSLATORS: we need to remove an replug so the new hardware can re-request the firmware */ message = _("You will need to remove and then reinsert the hardware before it will work correctly."); /* TRANSLATORS: title of libnotify bubble */ notification = notify_notification_new (_("Additional software was installed"), message, NULL); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER); notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); /* show the bubble */ ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } } static void require_nothing (GsdUpdatesFirmware *firmware) { const gchar *message; gboolean ret; GError *error = NULL; NotifyNotification *notification; /* TRANSLATORS: we need to remove an replug so the new hardware can re-request the firmware */ message = _("Your hardware has been set up and is now ready to use."); /* TRANSLATORS: title of libnotify bubble */ notification = notify_notification_new (_("Additional software was installed"), message, NULL); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER); notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); /* show the bubble */ ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } } static void install_packages_cb (GObject *object, GAsyncResult *res, GsdUpdatesFirmware *firmware) { PkClient *client = PK_CLIENT (object); GError *error = NULL; PkResults *results = NULL; GPtrArray *array = NULL; gboolean restart = FALSE; const GsdUpdatesFirmwareRequest *req; gboolean ret; guint i; PkError *error_code = NULL; /* get the results */ results = pk_client_generic_finish (client, res, &error); if (results == NULL) { g_warning ("failed to install file: %s", error->message); g_error_free (error); goto out; } /* check error code */ error_code = pk_results_get_error_code (results); if (error_code != NULL) { g_warning ("failed to install file: %s, %s", pk_error_enum_to_string (pk_error_get_code (error_code)), pk_error_get_details (error_code)); goto out; } /* go through all the requests, and find the worst type */ array = firmware->priv->array_requested; for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); ret = subsystem_can_replug (req->subsystem); if (!ret) { restart = TRUE; break; } } /* can we just rebind the device */ ret = g_file_test (GSD_UPDATES_FIRMWARE_DEVICE_REBIND_PROGRAM, G_FILE_TEST_EXISTS); if (ret) { ret = device_rebind (firmware); if (ret) { require_nothing (firmware); goto out; } } else { /* give the user the correct message */ if (restart) require_restart (firmware); else require_replug (firmware); } /* clear array */ g_ptr_array_set_size (firmware->priv->array_requested, 0); out: if (error_code != NULL) g_object_unref (error_code); if (array != NULL) g_ptr_array_unref (array); if (results != NULL) g_object_unref (results); } static gchar ** package_array_to_strv (GPtrArray *array) { PkPackage *item; gchar **results; guint i; results = g_new0 (gchar *, array->len+1); for (i=0; ilen; i++) { item = g_ptr_array_index (array, i); results[i] = g_strdup (pk_package_get_id (item)); } return results; } static void install_package_ids (GsdUpdatesFirmware *firmware) { gchar **package_ids; /* install all of the firmware files */ package_ids = package_array_to_strv (firmware->priv->packages_found); pk_client_install_packages_async (PK_CLIENT(firmware->priv->task), TRUE, package_ids, NULL, NULL, NULL, (GAsyncReadyCallback) install_packages_cb, firmware); g_strfreev (package_ids); } static void ignore_devices (GsdUpdatesFirmware *firmware) { gchar *existing = NULL; GsdUpdatesFirmwareRequest *req; GPtrArray *array; GString *string; guint i; /* get from settings */ existing = g_settings_get_string (firmware->priv->settings, GSD_SETTINGS_IGNORED_DEVICES); /* get existing string */ string = g_string_new (existing); if (string->len > 0) g_string_append (string, ","); /* add all listed devices */ array = firmware->priv->array_requested; for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); g_string_append_printf (string, "%s,", req->id); } /* remove final ',' */ if (string->len > 2) g_string_set_size (string, string->len - 1); /* set new string */ g_settings_set_string (firmware->priv->settings, GSD_SETTINGS_IGNORED_DEVICES, string->str); g_free (existing); g_string_free (string, TRUE); } static PkPackage * check_available (GsdUpdatesFirmware *firmware, const gchar *filename) { guint length = 0; GPtrArray *array = NULL; GError *error = NULL; PkPackage *item = NULL; PkBitfield filter; PkResults *results; gchar **values = NULL; PkError *error_code = NULL; /* search for newest not installed package */ filter = pk_bitfield_from_enums (PK_FILTER_ENUM_NOT_INSTALLED, PK_FILTER_ENUM_NEWEST, -1); values = g_strsplit (filename, "&", -1); results = pk_client_search_files (PK_CLIENT(firmware->priv->task), filter, values, NULL, NULL, NULL, &error); if (results == NULL) { g_warning ("failed to search file %s: %s", filename, error->message); g_error_free (error); goto out; } /* check error code */ error_code = pk_results_get_error_code (results); if (error_code != NULL) { g_warning ("failed to search file: %s, %s", pk_error_enum_to_string (pk_error_get_code (error_code)), pk_error_get_details (error_code)); goto out; } /* make sure we have one package */ array = pk_results_get_package_array (results); if (array->len == 0) g_debug ("no package providing %s found", filename); else if (array->len != 1) g_warning ("not one package providing %s found (%i)", filename, length); else item = g_object_ref (g_ptr_array_index (array, 0)); out: g_strfreev (values); if (error_code != NULL) g_object_unref (error_code); if (array != NULL) g_ptr_array_unref (array); if (results != NULL) g_object_unref (results); return item; } static void remove_duplicate (GPtrArray *array) { guint i, j; const gchar *val; const gchar *val_tmp; /* remove each duplicate entry */ for (i=0; ilen; i++) { val = g_ptr_array_index (array, i); for (j=i+1; jlen; j++) { val_tmp = g_ptr_array_index (array, j); if (g_strcmp0 (val_tmp, val) == 0) g_ptr_array_remove_index_fast (array, j); } } } static gboolean delay_timeout_cb (gpointer data) { guint i; gboolean ret; GString *string; GsdUpdatesFirmware *firmware = GSD_UPDATES_FIRMWARE (data); NotifyNotification *notification; GPtrArray *array; GError *error = NULL; PkPackage *item = NULL; const GsdUpdatesFirmwareRequest *req; gboolean has_data = FALSE; /* message string */ string = g_string_new (""); /* try to find each firmware file in an available package */ array = firmware->priv->array_requested; for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); /* save to new array if we found one package for this file */ item = check_available (firmware, req->filename); if (item != NULL) { g_ptr_array_add (firmware->priv->packages_found, item); g_object_unref (item); } } /* nothing to do */ if (firmware->priv->packages_found->len == 0) { g_debug ("no packages providing any of the missing firmware"); goto out; } /* check we don't want the same package more than once */ remove_duplicate (firmware->priv->packages_found); /* have we got any models to array */ for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); if (req->model != NULL) { has_data = TRUE; break; } } /* TRANSLATORS: we need another package to keep udev quiet */ g_string_append (string, _("Additional firmware is required to make hardware in this computer function correctly.")); /* sdd what information we have */ if (has_data) { g_string_append (string, "\n"); for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); if (req->model != NULL) g_string_append_printf (string, "\n• %s", req->model); } g_string_append (string, "\n"); } /* TRANSLATORS: title of libnotify bubble */ notification = notify_notification_new (_("Additional firmware required"), string->str, NULL); notify_notification_set_hint_string (notification, "desktop-entry", "gpk-update-viewer"); notify_notification_set_app_name (notification, _("Software Updates")); notify_notification_set_timeout (notification, NOTIFY_EXPIRES_NEVER); notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW); notify_notification_add_action (notification, "install-firmware", /* TRANSLATORS: button label */ _("Install firmware"), libnotify_cb, firmware, NULL); notify_notification_add_action (notification, "ignore-devices", /* TRANSLATORS: we should ignore this device and not ask anymore */ _("Ignore devices"), libnotify_cb, firmware, NULL); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("error: %s", error->message); g_error_free (error); } out: g_string_free (string, TRUE); /* never repeat */ return FALSE; } static void remove_banned (GsdUpdatesFirmware *firmware, GPtrArray *array) { gboolean ret; gchar **banned = NULL; gchar *banned_str; GsdUpdatesFirmwareRequest *req; guint i, j; /* get from settings */ banned_str = g_settings_get_string (firmware->priv->settings, GSD_SETTINGS_BANNED_FIRMWARE); if (banned_str == NULL) { g_warning ("could not read banned list"); goto out; } /* nothing in list, common case */ if (banned_str[0] == '\0') { g_debug ("nothing in banned list"); goto out; } /* split using "," */ banned = g_strsplit (banned_str, ",", 0); /* remove any banned pattern matches */ i = 0; while (i < array->len) { ret = FALSE; req = g_ptr_array_index (array, i); for (j=0; banned[j] != NULL; j++) { ret = g_pattern_match_simple (banned[j], req->filename); if (ret) { g_debug ("match %s for %s, removing", banned[j], req->filename); g_ptr_array_remove_index_fast (array, i); break; } } if (!ret) i++; } out: g_free (banned_str); g_strfreev (banned); } static void remove_ignored (GsdUpdatesFirmware *firmware, GPtrArray *array) { gboolean ret; gchar **ignored = NULL; gchar *ignored_str; GsdUpdatesFirmwareRequest *req; guint i, j; /* get from settings */ ignored_str = g_settings_get_string (firmware->priv->settings, GSD_SETTINGS_IGNORED_DEVICES); if (ignored_str == NULL) { g_warning ("could not read ignored list"); goto out; } /* nothing in list, common case */ if (ignored_str[0] == '\0') { g_debug ("nothing in ignored list"); goto out; } /* split using "," */ ignored = g_strsplit (ignored_str, ",", 0); /* remove any ignored pattern matches */ i = 0; while (i < array->len) { ret = FALSE; req = g_ptr_array_index (array, i); if (req->id == NULL) continue; for (j=0; ignored[j] != NULL; j++) { ret = g_pattern_match_simple (ignored[j], req->id); if (ret) { g_debug ("match %s for %s, removing", ignored[j], req->id); g_ptr_array_remove_index_fast (array, i); break; } } if (!ret) i++; } out: g_free (ignored_str); g_strfreev (ignored); } static gchar * udev_text_decode (const gchar *data) { guint i; guint j; gchar *decode; decode = g_strdup (data); for (i = 0, j = 0; data[i] != '\0'; j++) { if (memcmp (&data[i], "\\x2f", 4) == 0) { decode[j] = '/'; i += 4; } else if (memcmp (&data[i], "\\x5c", 4) == 0) { decode[j] = '\\'; i += 4; } else { decode[j] = data[i]; i++; } } decode[j] = '\0'; return decode; } static gchar * get_device (GsdUpdatesFirmware *firmware, const gchar *filename) { GFile *file; GFileInfo *info; const gchar *symlink_path; gchar *syspath = NULL; GError *error = NULL; gchar *target = NULL; gchar *tmp; /* get the file data */ file = g_file_new_for_path (filename); info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, G_FILE_QUERY_INFO_NONE, NULL, &error); if (info == NULL) { g_warning ("Failed to get symlink: %s", error->message); g_error_free (error); goto out; } /* /devices/pci0000:00/0000:00:1d.0/usb5/5-2/firmware/5-2 */ symlink_path = g_file_info_get_symlink_target (info); if (symlink_path == NULL) { g_warning ("failed to get symlink target"); goto out; } /* prepend sys to make '/sys/devices/pci0000:00/0000:00:1d.0/usb5/5-2/firmware/5-2' */ syspath = g_strconcat ("/sys", symlink_path, NULL); /* start with the longest, and try to find a sub-path that exists */ tmp = &syspath[strlen (syspath)]; while (tmp != NULL) { *tmp = '\0'; g_debug ("testing %s", target); if (g_file_test (syspath, G_FILE_TEST_EXISTS)) { target = g_strdup (syspath); goto out; } tmp = g_strrstr (syspath, "/"); } out: if (info != NULL) g_object_unref (info); g_object_unref (file); g_free (syspath); return target; } static void add_filename (GsdUpdatesFirmware *firmware, const gchar *filename_no_path) { gboolean ret; gchar *filename_path = NULL; gchar *missing_path = NULL; gchar *sysfs_path = NULL; GsdUpdatesFirmwareRequest *req; GPtrArray *array; guint i; /* this is the file we want to load */ filename_path = g_build_filename (GSD_UPDATES_FIRMWARE_LOADING_DIR, filename_no_path, NULL); /* file already exists */ ret = g_file_test (filename_path, G_FILE_TEST_EXISTS); if (ret) goto out; /* this is the file that udev created for us */ missing_path = g_build_filename (GSD_UPDATES_FIRMWARE_MISSING_DIR, filename_no_path, NULL); g_debug ("filename=%s -> %s", missing_path, filename_path); /* get symlink target */ sysfs_path = get_device (firmware, missing_path); if (sysfs_path == NULL) goto out; /* find any previous requests with this path or firmware */ array = firmware->priv->array_requested; for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); if (g_strcmp0 (sysfs_path, req->sysfs_path) == 0) { g_debug ("ignoring previous sysfs request for %s", sysfs_path); goto out; } if (g_strcmp0 (filename_path, req->filename) == 0) { g_debug ("ignoring previous filename request for %s", filename_path); goto out; } } /* create new request object */ req = request_new (filename_path, sysfs_path); g_ptr_array_add (firmware->priv->array_requested, req); out: g_free (missing_path); g_free (filename_path); g_free (sysfs_path); } static void scan_directory (GsdUpdatesFirmware *firmware) { gboolean ret; GError *error = NULL; GDir *dir; const gchar *filename; gchar *filename_decoded; guint i; GPtrArray *array; const GsdUpdatesFirmwareRequest *req; guint scan_id = 0; /* should we check and show the user */ ret = g_settings_get_boolean (firmware->priv->settings, GSD_SETTINGS_ENABLE_CHECK_FIRMWARE); if (!ret) { g_debug ("not showing thanks to GSettings"); return; } /* open the directory of requests */ dir = g_dir_open (GSD_UPDATES_FIRMWARE_MISSING_DIR, 0, &error); if (dir == NULL) { if (error->code != G_FILE_ERROR_NOENT) { g_warning ("failed to open directory: %s", error->message); } g_error_free (error); return; } /* find all the firmware requests */ filename = g_dir_read_name (dir); while (filename != NULL) { filename_decoded = udev_text_decode (filename); add_filename (firmware, filename_decoded); g_free (filename_decoded); /* next file */ filename = g_dir_read_name (dir); } g_dir_close (dir); /* debugging */ array = firmware->priv->array_requested; for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); g_debug ("requested: %s", req->filename); } /* remove banned files */ remove_banned (firmware, array); /* remove ignored devices */ remove_ignored (firmware, array); /* debugging */ array = firmware->priv->array_requested; for (i=0; ilen; i++) { req = g_ptr_array_index (array, i); g_debug ("searching for: %s", req->filename); } /* don't spam the user at startup, so wait a little delay */ if (array->len > 0) { scan_id = g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_PROCESS_DELAY, delay_timeout_cb, firmware); g_source_set_name_by_id (scan_id, "[GsdUpdatesFirmware] process"); } } static gboolean scan_directory_cb (GsdUpdatesFirmware *firmware) { scan_directory (firmware); firmware->priv->timeout_id = 0; return FALSE; } static void monitor_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, GsdUpdatesFirmware *firmware) { if (firmware->priv->timeout_id > 0) { g_debug ("clearing timeout as device changed"); g_source_remove (firmware->priv->timeout_id); } /* wait for the device to settle */ firmware->priv->timeout_id = g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_INSERT_DELAY, (GSourceFunc) scan_directory_cb, firmware); g_source_set_name_by_id (firmware->priv->timeout_id, "[GsdUpdatesFirmware] changed"); } static void gsd_updates_firmware_class_init (GsdUpdatesFirmwareClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_updates_firmware_finalize; g_type_class_add_private (klass, sizeof (GsdUpdatesFirmwarePrivate)); } static void gsd_updates_firmware_init (GsdUpdatesFirmware *firmware) { GFile *file; GError *error = NULL; firmware->priv = GSD_UPDATES_FIRMWARE_GET_PRIVATE (firmware); firmware->priv->timeout_id = 0; firmware->priv->packages_found = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); firmware->priv->array_requested = g_ptr_array_new_with_free_func ((GDestroyNotify) request_free); firmware->priv->settings = g_settings_new (GSD_SETTINGS_SCHEMA); firmware->priv->task = pk_task_new (); g_object_set (firmware->priv->task, "background", TRUE, NULL); /* setup watch for new hardware */ file = g_file_new_for_path (GSD_UPDATES_FIRMWARE_MISSING_DIR); firmware->priv->monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, &error); if (firmware->priv->monitor == NULL) { g_warning ("failed to setup monitor: %s", error->message); g_error_free (error); goto out; } /* limit to one per second */ g_file_monitor_set_rate_limit (firmware->priv->monitor, 1000); /* get notified of changes */ g_signal_connect (firmware->priv->monitor, "changed", G_CALLBACK (monitor_changed_cb), firmware); out: g_object_unref (file); firmware->priv->timeout_id = g_timeout_add_seconds (GSD_UPDATES_FIRMWARE_LOGIN_DELAY, (GSourceFunc) scan_directory_cb, firmware); g_source_set_name_by_id (firmware->priv->timeout_id, "[GsdUpdatesFirmware] login coldplug"); } static void gsd_updates_firmware_finalize (GObject *object) { GsdUpdatesFirmware *firmware; g_return_if_fail (GSD_UPDATES_IS_FIRMWARE (object)); firmware = GSD_UPDATES_FIRMWARE (object); g_return_if_fail (firmware->priv != NULL); g_ptr_array_unref (firmware->priv->array_requested); g_ptr_array_unref (firmware->priv->packages_found); g_object_unref (PK_CLIENT(firmware->priv->task)); g_object_unref (firmware->priv->settings); if (firmware->priv->monitor != NULL) g_object_unref (firmware->priv->monitor); if (firmware->priv->timeout_id > 0) g_source_remove (firmware->priv->timeout_id); G_OBJECT_CLASS (gsd_updates_firmware_parent_class)->finalize (object); } GsdUpdatesFirmware * gsd_updates_firmware_new (void) { GsdUpdatesFirmware *firmware; firmware = g_object_new (GSD_UPDATES_TYPE_FIRMWARE, NULL); return GSD_UPDATES_FIRMWARE (firmware); } ./plugins/updates/gsd-updates-common.h0000644000004100000410000000421512735467744020275 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GSD_UPDATES_COMMON_H #define __GSD_UPDATES_COMMON_H G_BEGIN_DECLS #define GSD_SETTINGS_BANNED_FIRMWARE "banned-firmware" #define GSD_SETTINGS_CONNECTION_USE_MOBILE "connection-use-mobile" #define GSD_SETTINGS_ENABLE_CHECK_FIRMWARE "enable-check-firmware" #define GSD_SETTINGS_FREQUENCY_GET_UPDATES "frequency-get-updates" #define GSD_SETTINGS_FREQUENCY_GET_UPGRADES "frequency-get-upgrades" #define GSD_SETTINGS_FREQUENCY_REFRESH_CACHE "frequency-refresh-cache" #define GSD_SETTINGS_FREQUENCY_UPDATES_NOTIFICATION "frequency-updates-notification" #define GSD_SETTINGS_IGNORED_DEVICES "ignored-devices" #define GSD_SETTINGS_LAST_UPDATES_NOTIFICATION "last-updates-notification" #define GSD_SETTINGS_MEDIA_REPO_FILENAMES "media-repo-filenames" #define GSD_SETTINGS_NOTIFY_DISTRO_UPGRADES "notify-distro-upgrades" #define GSD_SETTINGS_SCHEMA "org.gnome.settings-daemon.plugins.updates" #define GSD_SETTINGS_UPDATE_BATTERY "update-battery" #define GSD_SETTINGS_AUTO_DOWNLOAD_UPDATES "auto-download-updates" G_END_DECLS #endif /* __GSD_UPDATES_COMMON_H */ ./plugins/updates/updates-design.svg0000644000004100000410000010124012735467744020047 0ustar www-datawww-data image/svg+xml Wait for refresh dueGetUpdates (typ. 1 day) GetUpdates() 'Auto download'checkbox set? Y N UpdatePackages(only-download) Any security orcritical updates? Y N Y N > notify threshold?typ. 1 week Y N 'Use Mobile'checkbox set? Y N On GPRS orCDMA? Notify the user aboutimportant updates Notify the user aboutregular updates ./plugins/screensaver-proxy/0000755000004100000410000000000012735467763016447 5ustar www-datawww-data./plugins/screensaver-proxy/gsd-screensaver-proxy-plugin.c0000644000004100000410000000205712735467744024364 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 Bastien Nocera * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-screensaver-proxy-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdScreensaverProxy, gsd_screensaver_proxy) ./plugins/screensaver-proxy/Makefile.am0000644000004100000410000000310612735467744020502 0ustar www-datawww-dataplugin_name = screensaver-proxy plugin_LTLIBRARIES = libscreensaver-proxy.la libscreensaver_proxy_la_SOURCES = \ gsd-screensaver-proxy-manager.c \ gsd-screensaver-proxy-manager.h \ gsd-screensaver-proxy-plugin.c libscreensaver_proxy_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libscreensaver_proxy_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) libscreensaver_proxy_la_LDFLAGS = $(GSD_PLUGIN_LDFLAGS) libscreensaver_proxy_la_LIBADD = $(SETTINGS_PLUGIN_LIBS) libexec_PROGRAMS = usd-test-screensaver-proxy usd_test_screensaver_proxy_SOURCES = \ test-screensaver-proxy.c \ gsd-screensaver-proxy-manager.c \ gsd-screensaver-proxy-manager.h usd_test_screensaver_proxy_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) usd_test_screensaver_proxy_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_screensaver_proxy_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = screensaver-proxy.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = $(plugin_in_files) CLEANFILES = $(plugin_DATA) DISTCLEANFILES = $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/screensaver-proxy/test-screensaver-proxy.c0000644000004100000410000000040012735467744023260 0ustar www-datawww-data#define NEW gsd_screensaver_proxy_manager_new #define START gsd_screensaver_proxy_manager_start #define STOP gsd_screensaver_proxy_manager_stop #define MANAGER GsdScreensaverProxyManager #include "gsd-screensaver-proxy-manager.h" #include "test-plugin.h" ./plugins/screensaver-proxy/screensaver-proxy.gnome-settings-plugin.in0000644000004100000410000000035512735467744026736 0ustar www-datawww-data[GNOME Settings Plugin] Module=screensaver-proxy IAge=0 Priority=8 _Name=Screensaver Proxy _Description=Proxy FreeDesktop screensaver inhibition to gnome-session Authors=Bastien Nocera Copyright=Copyright © 2012 Bastien Nocera Website= ./plugins/screensaver-proxy/gsd-screensaver-proxy-manager.h0000644000004100000410000000516712735467744024512 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 Bastien Nocera * * 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. * */ #ifndef __GSD_SCREENSAVER_PROXY_MANAGER_H #define __GSD_SCREENSAVER_PROXY_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_SCREENSAVER_PROXY_MANAGER (gsd_screensaver_proxy_manager_get_type ()) #define GSD_SCREENSAVER_PROXY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_SCREENSAVER_PROXY_MANAGER, GsdScreensaverProxyManager)) #define GSD_SCREENSAVER_PROXY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_SCREENSAVER_PROXY_MANAGER, GsdScreensaverProxyManagerClass)) #define GSD_IS_SCREENSAVER_PROXY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_SCREENSAVER_PROXY_MANAGER)) #define GSD_IS_SCREENSAVER_PROXY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_SCREENSAVER_PROXY_MANAGER)) #define GSD_SCREENSAVER_PROXY_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_SCREENSAVER_PROXY_MANAGER, GsdScreensaverProxyManagerClass)) typedef struct GsdScreensaverProxyManagerPrivate GsdScreensaverProxyManagerPrivate; typedef struct { GObject parent; GsdScreensaverProxyManagerPrivate *priv; } GsdScreensaverProxyManager; typedef struct { GObjectClass parent_class; } GsdScreensaverProxyManagerClass; GType gsd_screensaver_proxy_manager_get_type (void); GsdScreensaverProxyManager *gsd_screensaver_proxy_manager_new (void); gboolean gsd_screensaver_proxy_manager_start (GsdScreensaverProxyManager *manager, GError **error); void gsd_screensaver_proxy_manager_stop (GsdScreensaverProxyManager *manager); G_END_DECLS #endif /* __GSD_SCREENSAVER_PROXY_MANAGER_H */ ./plugins/screensaver-proxy/gsd-screensaver-proxy-manager.c0000644000004100000410000004532712735467763024510 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 Bastien Nocera * * 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 "config.h" #include #include #include #include #include #include #include #include #include "gnome-settings-bus.h" #include "gnome-settings-profile.h" #include "gsd-screensaver-proxy-manager.h" #include "gsd-idle-monitor.h" #define GSD_SCREENSAVER_PROXY_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_SCREENSAVER_PROXY_MANAGER, GsdScreensaverProxyManagerPrivate)) /* As available in: * https://projects.kde.org/projects/kde/kde-workspace/repository/revisions/master/entry/ksmserver/screenlocker/dbus/org.freedesktop.ScreenSaver.xml * and documented in: * https://projects.kde.org/projects/kde/kde-workspace/repository/revisions/master/entry/ksmserver/screenlocker/interface.h */ static const gchar introspection_xml[] = "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""; static const gchar introspection_xml2[] = "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""; #define GSD_SCREENSAVER_PROXY_DBUS_SERVICE "org.freedesktop.ScreenSaver" #define GSD_SCREENSAVER_PROXY_DBUS_PATH "/org/freedesktop/ScreenSaver" #define GSD_SCREENSAVER_PROXY_DBUS_PATH2 "/ScreenSaver" #define GSD_SCREENSAVER_PROXY_DBUS_INTERFACE "org.freedesktop.ScreenSaver" #define GSM_INHIBITOR_FLAG_IDLE 1 << 3 struct GsdScreensaverProxyManagerPrivate { GsdSessionManager *session; GsdScreenSaver *screensaver; GDBusConnection *connection; GCancellable *bus_cancellable; GDBusNodeInfo *introspection_data; GDBusNodeInfo *introspection_data2; guint name_id; GHashTable *watch_ht; /* key = sender, value = name watch id */ GHashTable *cookie_ht; /* key = cookie, value = sender */ }; static void gsd_screensaver_proxy_manager_class_init (GsdScreensaverProxyManagerClass *klass); static void gsd_screensaver_proxy_manager_init (GsdScreensaverProxyManager *screensaver_proxy_manager); static void gsd_screensaver_proxy_manager_finalize (GObject *object); G_DEFINE_TYPE (GsdScreensaverProxyManager, gsd_screensaver_proxy_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static void name_vanished_cb (GDBusConnection *connection, const gchar *name, GsdScreensaverProxyManager *manager) { GHashTableIter iter; gpointer cookie_ptr; const char *sender; /* Look for all the cookies under that name, * and call uninhibit for them */ g_hash_table_iter_init (&iter, manager->priv->cookie_ht); while (g_hash_table_iter_next (&iter, &cookie_ptr, (gpointer *) &sender)) { if (g_strcmp0 (sender, name) == 0) { guint cookie = GPOINTER_TO_UINT (cookie_ptr); g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->session), "Uninhibit", g_variant_new ("(u)", cookie), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); g_debug ("Removing cookie %u for sender %s", cookie, sender); g_hash_table_iter_remove (&iter); } } g_hash_table_remove (manager->priv->watch_ht, name); } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GsdScreensaverProxyManager *manager = GSD_SCREENSAVER_PROXY_MANAGER (user_data); GError *error = NULL; GVariant *ret = NULL; /* Check session pointer as a proxy for whether the manager is in the start or stop state */ if (manager->priv->session == NULL || manager->priv->screensaver == NULL) { goto unimplemented; } g_debug ("Calling method '%s.%s' for ScreenSaver Proxy", interface_name, method_name); if (g_strcmp0 (method_name, "Inhibit") == 0) { const char *app_id; const char *reason; guint cookie; g_variant_get (parameters, "(ss)", &app_id, &reason); ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->session), "Inhibit", g_variant_new ("(susu)", app_id, 0, reason, GSM_INHIBITOR_FLAG_IDLE), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); g_variant_get (ret, "(u)", &cookie); g_hash_table_insert (manager->priv->cookie_ht, GUINT_TO_POINTER (cookie), g_strdup (sender)); if (g_hash_table_lookup (manager->priv->watch_ht, sender) == NULL) { guint watch_id; watch_id = g_bus_watch_name_on_connection (manager->priv->connection, sender, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, (GBusNameVanishedCallback) name_vanished_cb, manager, NULL); g_hash_table_insert (manager->priv->watch_ht, g_strdup (sender), GUINT_TO_POINTER (watch_id)); } } else if (g_strcmp0 (method_name, "UnInhibit") == 0) { guint cookie; g_variant_get (parameters, "(u)", &cookie); g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->session), "Uninhibit", parameters, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); g_debug ("Removing cookie %u from the list for %s", cookie, sender); g_hash_table_remove (manager->priv->cookie_ht, GUINT_TO_POINTER (cookie)); } else if (g_strcmp0 (method_name, "Lock") == 0 || g_strcmp0 (method_name, "SimulateUserActivity") == 0 || g_strcmp0 (method_name, "GetActive") == 0 || g_strcmp0 (method_name, "GetActiveTime") == 0 || g_strcmp0 (method_name, "SetActive") == 0) { ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->screensaver), method_name, parameters, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error == NULL && g_strcmp0 (method_name, "SetActive") == 0) { g_variant_unref (ret); /* Returning the actual Activate state here is not possible, * as calling GetActive at this point might return an invalid * value if the activation process is still ongoing. */ ret = g_variant_ref (parameters); } } else if (g_strcmp0 (method_name, "GetSessionIdleTime") == 0) { GsdIdleMonitor *idle_monitor = gsd_idle_monitor_get_core (); gint64 idle_time_ms = gsd_idle_monitor_get_idletime (idle_monitor); ret = g_variant_new ("(u)", idle_time_ms / 1000); } else { goto unimplemented; } if (error != NULL) { g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); return; } g_dbus_method_invocation_return_value (invocation, ret); return; unimplemented: g_dbus_method_invocation_return_dbus_error (invocation, "org.freedesktop.DBus.Error.NotSupported", "This method is not implemented"); } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, /* GetProperty */ NULL, /* SetProperty */ }; static void on_bus_gotten (GObject *source_object, GAsyncResult *res, GsdScreensaverProxyManager *manager) { GDBusConnection *connection; GDBusInterfaceInfo **infos; GError *error = NULL; if (manager->priv->bus_cancellable == NULL || g_cancellable_is_cancelled (manager->priv->bus_cancellable)) { g_warning ("Operation has been cancelled, so not retrieving session bus"); return; } connection = g_bus_get_finish (res, &error); if (connection == NULL) { g_warning ("Could not get session bus: %s", error->message); g_error_free (error); return; } manager->priv->connection = connection; infos = manager->priv->introspection_data->interfaces; g_dbus_connection_register_object (connection, GSD_SCREENSAVER_PROXY_DBUS_PATH, infos[0], &interface_vtable, manager, NULL, NULL); infos = manager->priv->introspection_data2->interfaces; g_dbus_connection_register_object (connection, GSD_SCREENSAVER_PROXY_DBUS_PATH2, infos[0], &interface_vtable, manager, NULL, NULL); manager->priv->name_id = g_bus_own_name_on_connection (manager->priv->connection, GSD_SCREENSAVER_PROXY_DBUS_SERVICE, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, NULL, NULL, NULL); } static void register_manager_dbus (GsdScreensaverProxyManager *manager) { manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); manager->priv->introspection_data2 = g_dbus_node_info_new_for_xml (introspection_xml2, NULL); manager->priv->bus_cancellable = g_cancellable_new (); g_assert (manager->priv->introspection_data != NULL); g_assert (manager->priv->introspection_data2 != NULL); g_bus_get (G_BUS_TYPE_SESSION, manager->priv->bus_cancellable, (GAsyncReadyCallback) on_bus_gotten, manager); } gboolean gsd_screensaver_proxy_manager_start (GsdScreensaverProxyManager *manager, GError **error) { g_debug ("Starting screensaver-proxy manager"); gnome_settings_profile_start (NULL); manager->priv->session = gnome_settings_bus_get_session_proxy (); manager->priv->screensaver = gnome_settings_bus_get_screen_saver_proxy (); manager->priv->watch_ht = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_bus_unwatch_name); manager->priv->cookie_ht = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_free); gnome_settings_profile_end (NULL); return TRUE; } void gsd_screensaver_proxy_manager_stop (GsdScreensaverProxyManager *manager) { g_debug ("Stopping screensaver_proxy manager"); g_clear_object (&manager->priv->session); g_clear_object (&manager->priv->screensaver); g_clear_pointer (&manager->priv->watch_ht, g_hash_table_destroy); g_clear_pointer (&manager->priv->cookie_ht, g_hash_table_destroy); } static void gsd_screensaver_proxy_manager_class_init (GsdScreensaverProxyManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_screensaver_proxy_manager_finalize; g_type_class_add_private (klass, sizeof (GsdScreensaverProxyManagerPrivate)); } static void gsd_screensaver_proxy_manager_init (GsdScreensaverProxyManager *manager) { manager->priv = GSD_SCREENSAVER_PROXY_MANAGER_GET_PRIVATE (manager); } static void gsd_screensaver_proxy_manager_finalize (GObject *object) { GsdScreensaverProxyManager *manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_SCREENSAVER_PROXY_MANAGER (object)); manager = GSD_SCREENSAVER_PROXY_MANAGER (object); g_return_if_fail (manager->priv != NULL); gsd_screensaver_proxy_manager_stop (manager); if (manager->priv->name_id != 0) { g_bus_unown_name (manager->priv->name_id); manager->priv->name_id = 0; } g_clear_object (&manager->priv->connection); g_clear_object (&manager->priv->bus_cancellable); g_clear_pointer (&manager->priv->introspection_data, g_dbus_node_info_unref); g_clear_pointer (&manager->priv->introspection_data2, g_dbus_node_info_unref); G_OBJECT_CLASS (gsd_screensaver_proxy_manager_parent_class)->finalize (object); } GsdScreensaverProxyManager * gsd_screensaver_proxy_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_SCREENSAVER_PROXY_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); register_manager_dbus (manager_object); } return GSD_SCREENSAVER_PROXY_MANAGER (manager_object); } ./plugins/background/0000755000004100000410000000000012735467763015067 5ustar www-datawww-data./plugins/background/Makefile.am0000644000004100000410000000310512735467744017121 0ustar www-datawww-dataNULL = plugin_name = background libexec_PROGRAMS = usd-test-background usd_test_background_SOURCES = \ test-background.c \ gsd-background-manager.h \ gsd-background-manager.c \ $(NULL) usd_test_background_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) usd_test_background_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(BACKGROUND_CFLAGS) \ $(AM_CFLAGS) usd_test_background_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(SETTINGS_PLUGIN_LIBS) \ $(BACKGROUND_LIBS) \ $(NULL) plugin_LTLIBRARIES = \ libbackground.la \ $(NULL) libbackground_la_SOURCES = \ gsd-background-plugin.c \ gsd-background-manager.h \ gsd-background-manager.c \ $(NULL) libbackground_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/background/libbackground \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libbackground_la_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(BACKGROUND_CFLAGS) \ $(AM_CFLAGS) libbackground_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) \ $(NULL) libbackground_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(BACKGROUND_LIBS) \ $(NULL) plugin_in_files = \ background.gnome-settings-plugin.in \ $(NULL) plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) \ $(NULL) CLEANFILES = \ $(plugin_DATA) \ $(NULL) DISTCLEANFILES = \ $(plugin_DATA) \ $(NULL) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/background/gsd-background-manager.h0000644000004100000410000000462312735467744021546 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_BACKGROUND_MANAGER_H #define __GSD_BACKGROUND_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_BACKGROUND_MANAGER (gsd_background_manager_get_type ()) #define GSD_BACKGROUND_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_BACKGROUND_MANAGER, GsdBackgroundManager)) #define GSD_BACKGROUND_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_BACKGROUND_MANAGER, GsdBackgroundManagerClass)) #define GSD_IS_BACKGROUND_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_BACKGROUND_MANAGER)) #define GSD_IS_BACKGROUND_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_BACKGROUND_MANAGER)) #define GSD_BACKGROUND_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_BACKGROUND_MANAGER, GsdBackgroundManagerClass)) typedef struct GsdBackgroundManagerPrivate GsdBackgroundManagerPrivate; typedef struct { GObject parent; GsdBackgroundManagerPrivate *priv; } GsdBackgroundManager; typedef struct { GObjectClass parent_class; } GsdBackgroundManagerClass; GType gsd_background_manager_get_type (void); GsdBackgroundManager * gsd_background_manager_new (void); gboolean gsd_background_manager_start (GsdBackgroundManager *manager, GError **error); void gsd_background_manager_stop (GsdBackgroundManager *manager); G_END_DECLS #endif /* __GSD_BACKGROUND_MANAGER_H */ ./plugins/background/gsd-background-plugin.c0000644000004100000410000000203412735467744021417 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-background-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdBackground, gsd_background) ./plugins/background/background.gnome-settings-plugin.in0000644000004100000410000000021712735467744023773 0ustar www-datawww-data[GNOME Settings Plugin] Module=background IAge=0 _Name=Background _Description=Background plugin Authors= Copyright=Copyright © 2007 Website= ./plugins/background/gsd-background-manager.c0000644000004100000410000004744612735467763021554 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright © 2001 Ximian, Inc. * Copyright (C) 2007 William Jon McCann * Copyright 2007 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 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define GNOME_DESKTOP_USE_UNSTABLE_API #include #include #include "gnome-settings-bus.h" #include "gnome-settings-profile.h" #include "gsd-background-manager.h" #define GSD_BACKGROUND_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_BACKGROUND_MANAGER, GsdBackgroundManagerPrivate)) struct GsdBackgroundManagerPrivate { GSettings *settings; GnomeBG *bg; GnomeBGCrossfade *fade; GDBusProxy *proxy; guint proxy_signal_id; }; static void gsd_background_manager_class_init (GsdBackgroundManagerClass *klass); static void gsd_background_manager_init (GsdBackgroundManager *background_manager); static void gsd_background_manager_finalize (GObject *object); static void setup_bg (GsdBackgroundManager *manager); static void connect_screen_signals (GsdBackgroundManager *manager); G_DEFINE_TYPE (GsdBackgroundManager, gsd_background_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static gboolean dont_draw_background (GsdBackgroundManager *manager) { return !g_settings_get_boolean (manager->priv->settings, "draw-background"); } static gboolean nautilus_is_drawing_background (GsdBackgroundManager *manager) { Atom window_id_atom; Window nautilus_xid; Atom actual_type; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char *data; Atom wmclass_atom; gboolean running; gint error; gboolean show_desktop_icons; show_desktop_icons = g_settings_get_boolean (manager->priv->settings, "show-desktop-icons"); if (! show_desktop_icons) { return FALSE; } window_id_atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "NAUTILUS_DESKTOP_WINDOW_ID", True); if (window_id_atom == None) { return FALSE; } XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_ROOT_WINDOW (), window_id_atom, 0, 1, False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (data != NULL) { nautilus_xid = *(Window *) data; XFree (data); } else { return FALSE; } if (actual_type != XA_WINDOW) { return FALSE; } if (actual_format != 32) { return FALSE; } wmclass_atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "WM_CLASS", False); gdk_error_trap_push (); XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), nautilus_xid, wmclass_atom, 0, 24, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data); error = gdk_error_trap_pop (); if (error == BadWindow) { return FALSE; } if (actual_type == XA_STRING && nitems == 24 && bytes_after == 0 && actual_format == 8 && data != NULL && !strcmp ((char *)data, "desktop_window") && !strcmp ((char *)data + strlen ((char *)data) + 1, "Nautilus")) { running = TRUE; } else { running = FALSE; } if (data != NULL) { XFree (data); } return running; } static void on_crossfade_finished (GsdBackgroundManager *manager) { g_object_unref (manager->priv->fade); manager->priv->fade = NULL; } static void draw_background (GsdBackgroundManager *manager, gboolean use_crossfade) { GdkDisplay *display; int n_screens; int i; if (nautilus_is_drawing_background (manager) || dont_draw_background (manager)) { return; } gnome_settings_profile_start (NULL); display = gdk_display_get_default (); n_screens = gdk_display_get_n_screens (display); for (i = 0; i < n_screens; ++i) { GdkScreen *screen; GdkWindow *root_window; cairo_surface_t *surface; screen = gdk_display_get_screen (display, i); root_window = gdk_screen_get_root_window (screen); surface = gnome_bg_create_surface (manager->priv->bg, root_window, gdk_screen_get_width (screen), gdk_screen_get_height (screen), TRUE); if (use_crossfade) { if (manager->priv->fade != NULL) { g_object_unref (manager->priv->fade); } manager->priv->fade = gnome_bg_set_surface_as_root_with_crossfade (screen, surface); g_signal_connect_swapped (manager->priv->fade, "finished", G_CALLBACK (on_crossfade_finished), manager); } else { gnome_bg_set_surface_as_root (screen, surface); } cairo_surface_destroy (surface); } gnome_settings_profile_end (NULL); } static void on_bg_transitioned (GnomeBG *bg, GsdBackgroundManager *manager) { draw_background (manager, FALSE); } static gboolean settings_change_event_cb (GSettings *settings, gpointer keys, gint n_keys, GsdBackgroundManager *manager) { gnome_bg_load_from_preferences (manager->priv->bg, manager->priv->settings); return FALSE; } static void on_screen_size_changed (GdkScreen *screen, GsdBackgroundManager *manager) { draw_background (manager, FALSE); } static void watch_bg_preferences (GsdBackgroundManager *manager) { g_signal_connect (manager->priv->settings, "change-event", G_CALLBACK (settings_change_event_cb), manager); } static void on_bg_changed (GnomeBG *bg, GsdBackgroundManager *manager) { draw_background (manager, TRUE); } static void setup_bg (GsdBackgroundManager *manager) { g_return_if_fail (manager->priv->bg == NULL); manager->priv->bg = gnome_bg_new (); g_signal_connect (manager->priv->bg, "changed", G_CALLBACK (on_bg_changed), manager); g_signal_connect (manager->priv->bg, "transitioned", G_CALLBACK (on_bg_transitioned), manager); connect_screen_signals (manager); watch_bg_preferences (manager); gnome_bg_load_from_preferences (manager->priv->bg, manager->priv->settings); } static void setup_bg_and_draw_background (GsdBackgroundManager *manager) { setup_bg (manager); draw_background (manager, FALSE); } static void disconnect_session_manager_listener (GsdBackgroundManager *manager) { if (manager->priv->proxy && manager->priv->proxy_signal_id) { g_signal_handler_disconnect (manager->priv->proxy, manager->priv->proxy_signal_id); manager->priv->proxy_signal_id = 0; } } static void on_session_manager_signal (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { GsdBackgroundManager *manager = GSD_BACKGROUND_MANAGER (user_data); if (g_strcmp0 (signal_name, "SessionRunning") == 0) { setup_bg_and_draw_background (manager); disconnect_session_manager_listener (manager); } } static void draw_background_after_session_loads (GsdBackgroundManager *manager) { manager->priv->proxy = G_DBUS_PROXY (gnome_settings_bus_get_session_proxy ()); manager->priv->proxy_signal_id = g_signal_connect (manager->priv->proxy, "g-signal", G_CALLBACK (on_session_manager_signal), manager); } static void disconnect_screen_signals (GsdBackgroundManager *manager) { GdkDisplay *display; int i; int n_screens; display = gdk_display_get_default (); n_screens = gdk_display_get_n_screens (display); for (i = 0; i < n_screens; ++i) { GdkScreen *screen; screen = gdk_display_get_screen (display, i); g_signal_handlers_disconnect_by_func (screen, G_CALLBACK (on_screen_size_changed), manager); } } static void connect_screen_signals (GsdBackgroundManager *manager) { GdkDisplay *display; int i; int n_screens; display = gdk_display_get_default (); n_screens = gdk_display_get_n_screens (display); for (i = 0; i < n_screens; ++i) { GdkScreen *screen; screen = gdk_display_get_screen (display, i); g_signal_connect (screen, "monitors-changed", G_CALLBACK (on_screen_size_changed), manager); g_signal_connect (screen, "size-changed", G_CALLBACK (on_screen_size_changed), manager); } } static void draw_background_changed (GSettings *settings, const char *key, GsdBackgroundManager *manager) { if (dont_draw_background (manager) == FALSE) setup_bg_and_draw_background (manager); } static void set_accountsservice_background (const gchar *background) { GDBusProxy *proxy = NULL; GDBusProxy *user = NULL; GVariant *variant = NULL; GError *error = NULL; gchar *object_path = NULL; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts", NULL, &error); if (proxy == NULL) { g_warning ("Failed to contact accounts service: %s", error->message); g_error_free (error); return; } variant = g_dbus_proxy_call_sync (proxy, "FindUserByName", g_variant_new ("(s)", g_get_user_name ()), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (variant == NULL) { g_warning ("Could not contact accounts service to look up '%s': %s", g_get_user_name (), error->message); g_error_free (error); goto bail; } g_variant_get (variant, "(o)", &object_path); user = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.Accounts", object_path, "org.freedesktop.Accounts.User", NULL, &error); g_free (object_path); if (user == NULL) { g_warning ("Could not create proxy for user '%s': %s", g_variant_get_string (variant, NULL), error->message); g_error_free (error); goto bail; } g_variant_unref (variant); variant = g_dbus_proxy_call_sync (user, "SetBackgroundFile", g_variant_new ("(s)", background ? background : ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (variant == NULL) { g_warning ("Failed to set the background '%s': %s", background, error->message); g_error_free (error); goto bail; } bail: if (proxy != NULL) g_object_unref (proxy); if (variant != NULL) g_variant_unref (variant); } static void picture_uri_changed (GSettings *settings, const char *key, GsdBackgroundManager *manager) { const char *picture_uri = g_settings_get_string (settings, key); GFile *picture_file = g_file_new_for_uri (picture_uri); char *picture_path = g_file_get_path (picture_file); set_accountsservice_background (picture_path); g_free (picture_path); g_object_unref (picture_file); } gboolean gsd_background_manager_start (GsdBackgroundManager *manager, GError **error) { gboolean show_desktop_icons; g_debug ("Starting background manager"); gnome_settings_profile_start (NULL); manager->priv->settings = g_settings_new ("org.gnome.desktop.background"); g_signal_connect (manager->priv->settings, "changed::draw-background", G_CALLBACK (draw_background_changed), manager); g_signal_connect (manager->priv->settings, "changed::picture-uri", G_CALLBACK (picture_uri_changed), manager); /* If this is set, nautilus will draw the background and is * almost definitely in our session. however, it may not be * running yet (so is_nautilus_running() will fail). so, on * startup, just don't do anything if this key is set so we * don't waste time setting the background only to have * nautilus overwrite it. */ show_desktop_icons = g_settings_get_boolean (manager->priv->settings, "show-desktop-icons"); if (!show_desktop_icons) { setup_bg (manager); } else { draw_background_after_session_loads (manager); } gnome_settings_profile_end (NULL); return TRUE; } void gsd_background_manager_stop (GsdBackgroundManager *manager) { GsdBackgroundManagerPrivate *p = manager->priv; g_debug ("Stopping background manager"); disconnect_screen_signals (manager); if (manager->priv->proxy) { disconnect_session_manager_listener (manager); g_clear_object (&manager->priv->proxy); } g_signal_handlers_disconnect_by_func (manager->priv->settings, settings_change_event_cb, manager); if (p->settings != NULL) { g_object_unref (p->settings); p->settings = NULL; } if (p->bg != NULL) { g_object_unref (p->bg); p->bg = NULL; } } static GObject * gsd_background_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdBackgroundManager *background_manager; background_manager = GSD_BACKGROUND_MANAGER (G_OBJECT_CLASS (gsd_background_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (background_manager); } static void gsd_background_manager_class_init (GsdBackgroundManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_background_manager_constructor; object_class->finalize = gsd_background_manager_finalize; g_type_class_add_private (klass, sizeof (GsdBackgroundManagerPrivate)); } static void gsd_background_manager_init (GsdBackgroundManager *manager) { manager->priv = GSD_BACKGROUND_MANAGER_GET_PRIVATE (manager); } static void gsd_background_manager_finalize (GObject *object) { GsdBackgroundManager *background_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_BACKGROUND_MANAGER (object)); background_manager = GSD_BACKGROUND_MANAGER (object); g_return_if_fail (background_manager->priv != NULL); G_OBJECT_CLASS (gsd_background_manager_parent_class)->finalize (object); } GsdBackgroundManager * gsd_background_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_BACKGROUND_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_BACKGROUND_MANAGER (manager_object); } ./plugins/background/test-background.c0000644000004100000410000000033612735467744020330 0ustar www-datawww-data#define NEW gsd_background_manager_new #define START gsd_background_manager_start #define STOP gsd_background_manager_stop #define MANAGER GsdBackgroundManager #include "gsd-background-manager.h" #include "test-plugin.h" ./plugins/xrandr/0000755000004100000410000000000012735467763014246 5ustar www-datawww-data./plugins/xrandr/gsd-xrandr-manager.c0000644000004100000410000027135112735467763020104 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2007, 2008 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 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_WACOM #include #endif /* HAVE_WACOM */ #include "gsd-enums.h" #include "gsd-input-helper.h" #include "gnome-settings-plugin.h" #include "gnome-settings-profile.h" #include "gnome-settings-bus.h" #include "gsd-xrandr-manager.h" #include "gsd-rr-config.h" #include "gsd-rr.h" #include "gsd-pnp-ids.h" #define GSD_XRANDR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_XRANDR_MANAGER, GsdXrandrManagerPrivate)) #define CONF_SCHEMA "org.gnome.settings-daemon.plugins.xrandr" #define CONF_KEY_DEFAULT_MONITORS_SETUP "default-monitors-setup" #define CONF_KEY_DEFAULT_CONFIGURATION_FILE "default-configuration-file" /* Number of seconds that the confirmation dialog will last before it resets the * RANDR configuration to its old state. */ #define CONFIRMATION_DIALOG_SECONDS 30 /* name of the icon files (usd-xrandr.svg, etc.) */ #define GSD_XRANDR_ICON_NAME "usd-xrandr" #define GSD_XRANDR_DBUS_NAME GSD_DBUS_NAME ".XRANDR" #define GSD_XRANDR_DBUS_PATH GSD_DBUS_PATH "/XRANDR" static const gchar introspection_xml[] = "" " " " " " " " " " " "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; struct GsdXrandrManagerPrivate { GsdRRScreen *rw_screen; gboolean running; UpClient *upower_client; gboolean laptop_lid_is_closed; GSettings *settings; GDBusNodeInfo *introspection_data; guint name_id; GDBusConnection *connection; GCancellable *bus_cancellable; /* fn-F7 status */ int current_fn_f7_config; /* -1 if no configs */ GsdRRConfig **fn_f7_configs; /* NULL terminated, NULL if there are no configs */ /* Last time at which we got a "screen got reconfigured" event; see on_randr_event() */ guint32 last_config_timestamp; #ifdef HAVE_WACOM WacomDeviceDatabase *wacom_db; #endif /* HAVE_WACOM */ int main_touchscreen_id; gchar *main_touchscreen_name; }; typedef struct { int mapping_pid; guint mapping_kill_id; guint mapping_retry_id; gboolean mapping_killed; } TouchMappingPrivate; static const GsdRRRotation possible_rotations[] = { GSD_RR_ROTATION_0, GSD_RR_ROTATION_90, GSD_RR_ROTATION_180, GSD_RR_ROTATION_270 /* We don't allow REFLECT_X or REFLECT_Y for now, as gnome-display-properties doesn't allow them, either */ }; static void gsd_xrandr_manager_class_init (GsdXrandrManagerClass *klass); static void gsd_xrandr_manager_init (GsdXrandrManager *xrandr_manager); static void gsd_xrandr_manager_finalize (GObject *object); static void error_message (GsdXrandrManager *mgr, const char *primary_text, GError *error_to_display, const char *secondary_text); static void get_allowed_rotations_for_output (GsdRRConfig *config, GsdRRScreen *rr_screen, GsdRROutputInfo *output, int *out_num_rotations, GsdRRRotation *out_rotations); static void handle_fn_f7 (GsdXrandrManager *mgr, guint32 timestamp); static void handle_rotate_windows (GsdXrandrManager *mgr, GsdRRRotation rotation, guint32 timestamp); static void register_manager_dbus (GsdXrandrManager *manager); G_DEFINE_TYPE (GsdXrandrManager, gsd_xrandr_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static FILE *log_file; /* Wait before retrying */ static const int RETRY_TIMEOUT = 5; /* Wait before timing out */ static const int MAPPING_TIMEOUT = 3; static GsdRROutput * input_info_find_size_match (GsdXrandrManager *manager, GsdRRScreen *rr_screen); static int map_touch_to_output (GsdXrandrManager *manager, GsdRROutputInfo *output); static gboolean do_touchscreen_mapping (GsdXrandrManager *manager); static void log_open (void) { char *toggle_filename; char *log_filename; struct stat st; if (log_file) return; toggle_filename = g_build_filename (g_get_home_dir (), "gsd-debug-randr", NULL); log_filename = g_build_filename (g_get_home_dir (), "gsd-debug-randr.log", NULL); if (stat (toggle_filename, &st) != 0) goto out; log_file = fopen (log_filename, "a"); if (log_file && ftell (log_file) == 0) fprintf (log_file, "To keep this log from being created, please rm ~/gsd-debug-randr\n"); out: g_free (toggle_filename); g_free (log_filename); } static void log_close (void) { if (log_file) { fclose (log_file); log_file = NULL; } } static void log_msg (const char *format, ...) { if (log_file) { va_list args; va_start (args, format); vfprintf (log_file, format, args); va_end (args); } } static void log_output (GsdRROutputInfo *output) { gchar *name = gsd_rr_output_info_get_name (output); gchar *display_name = gsd_rr_output_info_get_display_name (output); log_msg (" %s: ", name ? name : "unknown"); if (gsd_rr_output_info_is_connected (output)) { if (gsd_rr_output_info_is_active (output)) { int x, y, width, height; gsd_rr_output_info_get_geometry (output, &x, &y, &width, &height); log_msg ("%dx%d@%d +%d+%d", width, height, gsd_rr_output_info_get_refresh_rate (output), x, y); } else log_msg ("off"); } else log_msg ("disconnected"); if (display_name) log_msg (" (%s)", display_name); if (gsd_rr_output_info_get_primary (output)) log_msg (" (primary output)"); log_msg ("\n"); } static void log_configuration (GsdRRConfig *config) { int i; GsdRROutputInfo **outputs = gsd_rr_config_get_outputs (config); log_msg (" cloned: %s\n", gsd_rr_config_get_clone (config) ? "yes" : "no"); for (i = 0; outputs[i] != NULL; i++) log_output (outputs[i]); if (i == 0) log_msg (" no outputs!\n"); } static char timestamp_relationship (guint32 a, guint32 b) { if (a < b) return '<'; else if (a > b) return '>'; else return '='; } static void log_screen (GsdRRScreen *screen) { GsdRRConfig *config; int min_w, min_h, max_w, max_h; guint32 change_timestamp, config_timestamp; if (!log_file) return; config = gsd_rr_config_new_current (screen, NULL); gsd_rr_screen_get_ranges (screen, &min_w, &max_w, &min_h, &max_h); gsd_rr_screen_get_timestamps (screen, &change_timestamp, &config_timestamp); log_msg (" Screen min(%d, %d), max(%d, %d), change=%u %c config=%u\n", min_w, min_h, max_w, max_h, change_timestamp, timestamp_relationship (change_timestamp, config_timestamp), config_timestamp); log_configuration (config); g_object_unref (config); } static void log_configurations (GsdRRConfig **configs) { int i; if (!configs) { log_msg (" No configurations\n"); return; } for (i = 0; configs[i]; i++) { log_msg (" Configuration %d\n", i); log_configuration (configs[i]); } } static void show_timestamps_dialog (GsdXrandrManager *manager, const char *msg) { #if 1 return; #else struct GsdXrandrManagerPrivate *priv = manager->priv; GtkWidget *dialog; guint32 change_timestamp, config_timestamp; static int serial; gsd_rr_screen_get_timestamps (priv->rw_screen, &change_timestamp, &config_timestamp); dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "RANDR timestamps (%d):\n%s\nchange: %u\nconfig: %u", serial++, msg, change_timestamp, config_timestamp); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_widget_show (dialog); #endif } static void print_output (GsdRROutputInfo *info) { int x, y, width, height; g_debug (" Output: %s attached to %s", gsd_rr_output_info_get_display_name (info), gsd_rr_output_info_get_name (info)); g_debug (" status: %s", gsd_rr_output_info_is_active (info) ? "on" : "off"); gsd_rr_output_info_get_geometry (info, &x, &y, &width, &height); g_debug (" width: %d", width); g_debug (" height: %d", height); g_debug (" rate: %d", gsd_rr_output_info_get_refresh_rate (info)); g_debug (" primary: %s", gsd_rr_output_info_get_primary (info) ? "true" : "false"); g_debug (" position: %d %d", x, y); } static void print_configuration (GsdRRConfig *config, const char *header) { int i; GsdRROutputInfo **outputs; g_debug ("=== %s Configuration ===", header); if (!config) { g_debug (" none"); return; } g_debug (" Clone: %s", gsd_rr_config_get_clone (config) ? "true" : "false"); outputs = gsd_rr_config_get_outputs (config); for (i = 0; outputs[i] != NULL; ++i) print_output (outputs[i]); } static gboolean is_laptop (GsdRRScreen *screen, GsdRROutputInfo *output) { GsdRROutput *rr_output; rr_output = gsd_rr_screen_get_output_by_name (screen, gsd_rr_output_info_get_name (output)); return gsd_rr_output_is_laptop (rr_output); } static GsdRROutputInfo * get_laptop_output_info (GsdRRScreen *screen, GsdRRConfig *config) { int i; GsdRROutputInfo **outputs = gsd_rr_config_get_outputs (config); for (i = 0; outputs[i] != NULL; i++) { if (is_laptop (screen, outputs[i])) return outputs[i]; } return NULL; } static gboolean non_laptop_outputs_are_active (GsdRRConfig *config, GsdRROutputInfo *laptop_info) { GsdRROutputInfo **outputs; int i; outputs = gsd_rr_config_get_outputs (config); for (i = 0; outputs[i] != NULL; i++) { if (outputs[i] == laptop_info) continue; if (gsd_rr_output_info_is_active (outputs[i])) return TRUE; } return FALSE; } static void turn_off_laptop_display_in_configuration (GsdRRScreen *screen, GsdRRConfig *config) { GsdRROutputInfo *laptop_info; laptop_info = get_laptop_output_info (screen, config); if (laptop_info) { /* Turn off the laptop's screen only if other displays are on. This is to avoid an all-black-screens scenario. */ if (non_laptop_outputs_are_active (config, laptop_info)) gsd_rr_output_info_set_active (laptop_info, FALSE); } /* Adjust the offsets of outputs so they start at (0, 0) */ gsd_rr_config_sanitize (config); } /* This function effectively centralizes the use of gsd_rr_config_apply_from_filename_with_time(). * * Optionally filters out GSD_RR_ERROR_NO_MATCHING_CONFIG from the matching * process(), since that is not usually an error. */ static gboolean apply_configuration_from_filename (GsdXrandrManager *manager, const char *filename, gboolean no_matching_config_is_an_error, guint32 timestamp, GError **error) { struct GsdXrandrManagerPrivate *priv = manager->priv; GsdRRConfig *config; GError *my_error; gboolean success; char *str; str = g_strdup_printf ("Applying %s with timestamp %d", filename, timestamp); show_timestamps_dialog (manager, str); g_free (str); my_error = NULL; config = g_object_new (GSD_TYPE_RR_CONFIG, "screen", priv->rw_screen, NULL); if (!gsd_rr_config_load_filename (config, filename, &my_error)) { g_object_unref (config); if (g_error_matches (my_error, GSD_RR_ERROR, GSD_RR_ERROR_NO_MATCHING_CONFIG)) { if (no_matching_config_is_an_error) { g_propagate_error (error, my_error); return FALSE; } else { /* This is not an error; the user probably changed his monitors * and so they don't match any of the stored configurations. */ g_error_free (my_error); return TRUE; } } else { g_propagate_error (error, my_error); return FALSE; } } if (up_client_get_lid_is_closed (priv->upower_client)) turn_off_laptop_display_in_configuration (priv->rw_screen, config); gsd_rr_config_ensure_primary (config); success = gsd_rr_config_apply_with_time (config, priv->rw_screen, timestamp, error); g_object_unref (config); return success; } /* This function centralizes the use of gsd_rr_config_apply_with_time(). * * Applies a configuration and displays an error message if an error happens. * We just return whether setting the configuration succeeded. */ static gboolean apply_configuration (GsdXrandrManager *manager, GsdRRConfig *config, guint32 timestamp, gboolean show_error, gboolean save_configuration) { GsdXrandrManagerPrivate *priv = manager->priv; GError *error; gboolean success; gsd_rr_config_ensure_primary (config); print_configuration (config, "Applying Configuration"); error = NULL; success = gsd_rr_config_apply_with_time (config, priv->rw_screen, timestamp, &error); if (success) { if (save_configuration) gsd_rr_config_save (config, NULL); /* NULL-GError - there's not much we can do if this fails */ } else { log_msg ("Could not switch to the following configuration (timestamp %u): %s\n", timestamp, error->message); log_configuration (config); if (show_error) error_message (manager, _("Could not switch the monitor configuration"), error, NULL); g_error_free (error); } return success; } static void restore_backup_configuration_without_messages (const char *backup_filename, const char *intended_filename) { backup_filename = gsd_rr_config_get_backup_filename (); rename (backup_filename, intended_filename); } static void restore_backup_configuration (GsdXrandrManager *manager, const char *backup_filename, const char *intended_filename, guint32 timestamp) { int saved_errno; if (rename (backup_filename, intended_filename) == 0) { GError *error; error = NULL; if (!apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, &error)) { error_message (manager, _("Could not restore the display's configuration"), error, NULL); if (error) g_error_free (error); } return; } saved_errno = errno; /* ENOENT means the original file didn't exist. That is *not* an error; * the backup was not created because there wasn't even an original * monitors.xml (such as on a first-time login). Note that *here* there * is a "didn't work" monitors.xml, so we must delete that one. */ if (saved_errno == ENOENT) unlink (intended_filename); else { char *msg; msg = g_strdup_printf ("Could not rename %s to %s: %s", backup_filename, intended_filename, g_strerror (saved_errno)); error_message (manager, _("Could not restore the display's configuration from a backup"), NULL, msg); g_free (msg); } unlink (backup_filename); } typedef struct { GsdXrandrManager *manager; GtkWidget *dialog; int countdown; int response_id; } TimeoutDialog; static void print_countdown_text (TimeoutDialog *timeout) { gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (timeout->dialog), ngettext ("The display will be reset to its previous configuration in %d second", "The display will be reset to its previous configuration in %d seconds", timeout->countdown), timeout->countdown); } static gboolean timeout_cb (gpointer data) { TimeoutDialog *timeout = data; timeout->countdown--; if (timeout->countdown == 0) { timeout->response_id = GTK_RESPONSE_CANCEL; gtk_main_quit (); } else { print_countdown_text (timeout); } return TRUE; } static void timeout_response_cb (GtkDialog *dialog, int response_id, gpointer data) { TimeoutDialog *timeout = data; if (response_id == GTK_RESPONSE_DELETE_EVENT) { /* The user closed the dialog or pressed ESC, revert */ timeout->response_id = GTK_RESPONSE_CANCEL; } else timeout->response_id = response_id; gtk_main_quit (); } static gboolean user_says_things_are_ok (GsdXrandrManager *manager, GdkWindow *parent_window) { TimeoutDialog timeout; guint timeout_id; timeout.manager = manager; timeout.dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Does the display look OK?")); timeout.countdown = CONFIRMATION_DIALOG_SECONDS; print_countdown_text (&timeout); gtk_window_set_icon_name (GTK_WINDOW (timeout.dialog), "preferences-desktop-display"); gtk_dialog_add_button (GTK_DIALOG (timeout.dialog), _("_Restore Previous Configuration"), GTK_RESPONSE_CANCEL); gtk_dialog_add_button (GTK_DIALOG (timeout.dialog), _("_Keep This Configuration"), GTK_RESPONSE_ACCEPT); gtk_dialog_set_default_response (GTK_DIALOG (timeout.dialog), GTK_RESPONSE_ACCEPT); /* ah, the optimism */ g_signal_connect (timeout.dialog, "response", G_CALLBACK (timeout_response_cb), &timeout); gtk_widget_realize (timeout.dialog); if (parent_window) gdk_window_set_transient_for (gtk_widget_get_window (timeout.dialog), parent_window); gtk_widget_show_all (timeout.dialog); /* We don't use g_timeout_add_seconds() since we actually care that the user sees "real" second ticks in the dialog */ timeout_id = g_timeout_add (1000, timeout_cb, &timeout); gtk_main (); gtk_widget_destroy (timeout.dialog); g_source_remove (timeout_id); if (timeout.response_id == GTK_RESPONSE_ACCEPT) return TRUE; else return FALSE; } struct confirmation { GsdXrandrManager *manager; GdkWindow *parent_window; guint32 timestamp; }; static gboolean confirm_with_user_idle_cb (gpointer data) { struct confirmation *confirmation = data; char *backup_filename; char *intended_filename; backup_filename = gsd_rr_config_get_backup_filename (); intended_filename = gsd_rr_config_get_intended_filename (); if (user_says_things_are_ok (confirmation->manager, confirmation->parent_window)) unlink (backup_filename); else restore_backup_configuration (confirmation->manager, backup_filename, intended_filename, confirmation->timestamp); g_free (confirmation); return FALSE; } static void queue_confirmation_by_user (GsdXrandrManager *manager, GdkWindow *parent_window, guint32 timestamp) { struct confirmation *confirmation; confirmation = g_new (struct confirmation, 1); confirmation->manager = manager; confirmation->parent_window = parent_window; confirmation->timestamp = timestamp; g_idle_add (confirm_with_user_idle_cb, confirmation); } static gboolean try_to_apply_intended_configuration (GsdXrandrManager *manager, GdkWindow *parent_window, guint32 timestamp, GError **error) { char *backup_filename; char *intended_filename; gboolean result; /* Try to apply the intended configuration */ backup_filename = gsd_rr_config_get_backup_filename (); intended_filename = gsd_rr_config_get_intended_filename (); result = apply_configuration_from_filename (manager, intended_filename, FALSE, timestamp, error); if (!result) { error_message (manager, _("The selected configuration for displays could not be applied"), error ? *error : NULL, NULL); restore_backup_configuration_without_messages (backup_filename, intended_filename); goto out; } else { /* We need to return as quickly as possible, so instead of * confirming with the user right here, we do it in an idle * handler. The caller only expects a status for "could you * change the RANDR configuration?", not "is the user OK with it * as well?". */ queue_confirmation_by_user (manager, parent_window, timestamp); } out: g_free (backup_filename); g_free (intended_filename); return result; } /* DBus method for org.gnome.SettingsDaemon.XRANDR_2 ApplyConfiguration; see gsd-xrandr-manager.xml for the interface definition */ static gboolean gsd_xrandr_manager_2_apply_configuration (GsdXrandrManager *manager, gint64 parent_window_id, gint64 timestamp, GError **error) { GdkWindow *parent_window; gboolean result; if (parent_window_id != 0) parent_window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), (Window) parent_window_id); else parent_window = NULL; result = try_to_apply_intended_configuration (manager, parent_window, (guint32) timestamp, error); if (parent_window) g_object_unref (parent_window); return result; } /* DBus method for org.gnome.SettingsDaemon.XRANDR_2 VideoModeSwitch; see gsd-xrandr-manager.xml for the interface definition */ static gboolean gsd_xrandr_manager_2_video_mode_switch (GsdXrandrManager *manager, guint32 timestamp, GError **error) { handle_fn_f7 (manager, timestamp); return TRUE; } /* DBus method for org.gnome.SettingsDaemon.XRANDR_2 Rotate; see gsd-xrandr-manager.xml for the interface definition */ static gboolean gsd_xrandr_manager_2_rotate (GsdXrandrManager *manager, guint32 timestamp, GError **error) { handle_rotate_windows (manager, GSD_RR_ROTATION_NEXT, timestamp); return TRUE; } /* DBus method for org.gnome.SettingsDaemon.XRANDR_2 RotateTo; see gsd-xrandr-manager.xml for the interface definition */ static gboolean gsd_xrandr_manager_2_rotate_to (GsdXrandrManager *manager, GsdRRRotation rotation, guint32 timestamp, GError **error) { guint i; gboolean found; found = FALSE; for (i = 0; i < G_N_ELEMENTS (possible_rotations); i++) { if (rotation == possible_rotations[i]) { found = TRUE; break; } } if (found == FALSE) { g_debug ("Not setting out of bounds rotation '%d'", rotation); return FALSE; } handle_rotate_windows (manager, rotation, timestamp); return TRUE; } static gboolean get_clone_size (GsdRRScreen *screen, int *width, int *height) { GsdRRMode **modes = gsd_rr_screen_list_clone_modes (screen); int best_w, best_h; int i; best_w = 0; best_h = 0; for (i = 0; modes[i] != NULL; ++i) { GsdRRMode *mode = modes[i]; int w, h; w = gsd_rr_mode_get_width (mode); h = gsd_rr_mode_get_height (mode); if (w * h > best_w * best_h) { best_w = w; best_h = h; } } if (best_w > 0 && best_h > 0) { if (width) *width = best_w; if (height) *height = best_h; return TRUE; } return FALSE; } static gboolean config_is_all_off (GsdRRConfig *config) { int j; GsdRROutputInfo **outputs; outputs = gsd_rr_config_get_outputs (config); for (j = 0; outputs[j] != NULL; ++j) { if (gsd_rr_output_info_is_active (outputs[j])) { return FALSE; } } return TRUE; } static gboolean laptop_lid_is_closed (GsdXrandrManager *manager) { return up_client_get_lid_is_closed (manager->priv->upower_client); } static gboolean is_laptop_with_closed_lid (GsdXrandrManager *manager, GsdRRScreen *screen, GsdRROutputInfo *info) { return is_laptop (screen, info) && laptop_lid_is_closed (manager); } static GsdRRConfig * make_clone_setup (GsdXrandrManager *manager, GsdRRScreen *screen) { GsdRRConfig *result; GsdRROutputInfo **outputs; int width, height; int i; if (!get_clone_size (screen, &width, &height)) return NULL; result = gsd_rr_config_new_current (screen, NULL); gsd_rr_config_set_clone (result, TRUE); outputs = gsd_rr_config_get_outputs (result); for (i = 0; outputs[i] != NULL; ++i) { GsdRROutputInfo *info = outputs[i]; gsd_rr_output_info_set_active (info, FALSE); if (!is_laptop_with_closed_lid (manager, screen, info) && gsd_rr_output_info_is_connected (info)) { GsdRROutput *output = gsd_rr_screen_get_output_by_name (screen, gsd_rr_output_info_get_name (info)); GsdRRMode **modes = gsd_rr_output_list_modes (output); int j; int best_rate = 0; for (j = 0; modes[j] != NULL; ++j) { GsdRRMode *mode = modes[j]; int w, h; w = gsd_rr_mode_get_width (mode); h = gsd_rr_mode_get_height (mode); if (w == width && h == height) { int r = gsd_rr_mode_get_freq (mode); if (r > best_rate) best_rate = r; } } if (best_rate > 0) { gsd_rr_output_info_set_active (info, TRUE); gsd_rr_output_info_set_rotation (info, GSD_RR_ROTATION_0); gsd_rr_output_info_set_refresh_rate (info, best_rate); gsd_rr_output_info_set_geometry (info, 0, 0, width, height); } } } if (config_is_all_off (result)) { g_object_unref (G_OBJECT (result)); result = NULL; } print_configuration (result, "clone setup"); return result; } static GsdRRMode * find_best_mode (GsdRROutput *output) { GsdRRMode *preferred; GsdRRMode **modes; int best_size; int best_width, best_height, best_rate; int i; GsdRRMode *best_mode; preferred = gsd_rr_output_get_preferred_mode (output); if (preferred) return preferred; modes = gsd_rr_output_list_modes (output); if (!modes) return NULL; best_size = best_width = best_height = best_rate = 0; best_mode = NULL; for (i = 0; modes[i] != NULL; i++) { int w, h, r; int size; w = gsd_rr_mode_get_width (modes[i]); h = gsd_rr_mode_get_height (modes[i]); r = gsd_rr_mode_get_freq (modes[i]); size = w * h; if (size > best_size) { best_size = size; best_width = w; best_height = h; best_rate = r; best_mode = modes[i]; } else if (size == best_size) { if (r > best_rate) { best_rate = r; best_mode = modes[i]; } } } return best_mode; } static gboolean turn_on (GsdRRScreen *screen, GsdRROutputInfo *info, int x, int y) { GsdRROutput *output = gsd_rr_screen_get_output_by_name (screen, gsd_rr_output_info_get_name (info)); GsdRRMode *mode = find_best_mode (output); if (mode) { gsd_rr_output_info_set_active (info, TRUE); gsd_rr_output_info_set_geometry (info, x, y, gsd_rr_mode_get_width (mode), gsd_rr_mode_get_height (mode)); gsd_rr_output_info_set_rotation (info, GSD_RR_ROTATION_0); gsd_rr_output_info_set_refresh_rate (info, gsd_rr_mode_get_freq (mode)); return TRUE; } return FALSE; } static GsdRRConfig * make_laptop_setup (GsdXrandrManager *manager, GsdRRScreen *screen) { /* Turn on the laptop, disable everything else */ GsdRRConfig *result = gsd_rr_config_new_current (screen, NULL); GsdRROutputInfo **outputs = gsd_rr_config_get_outputs (result); int i; gsd_rr_config_set_clone (result, FALSE); for (i = 0; outputs[i] != NULL; ++i) { GsdRROutputInfo *info = outputs[i]; if (is_laptop (screen, info) && !laptop_lid_is_closed (manager)) { if (!turn_on (screen, info, 0, 0)) { g_object_unref (G_OBJECT (result)); result = NULL; break; } } else { gsd_rr_output_info_set_active (info, FALSE); } } if (config_is_all_off (result)) { g_object_unref (G_OBJECT (result)); result = NULL; } print_configuration (result, "Laptop setup"); /* FIXME - Maybe we should return NULL if there is more than * one connected "laptop" screen? */ return result; } static int turn_on_and_get_rightmost_offset (GsdRRScreen *screen, GsdRROutputInfo *info, int x) { if (turn_on (screen, info, x, 0)) { int width; gsd_rr_output_info_get_geometry (info, NULL, NULL, &width, NULL); x += width; } return x; } /* Used from g_ptr_array_sort(); compares outputs based on their X position */ static int compare_output_positions (gconstpointer a, gconstpointer b) { GsdRROutputInfo **oa = (GsdRROutputInfo **) a; GsdRROutputInfo **ob = (GsdRROutputInfo **) b; int xa, xb; gsd_rr_output_info_get_geometry (*oa, &xa, NULL, NULL, NULL); gsd_rr_output_info_get_geometry (*ob, &xb, NULL, NULL, NULL); return xb - xa; } /* A set of outputs with already-set sizes and positions may not fit in the * frame buffer that is available. Turn off outputs right-to-left until we find * a size that fits. Returns whether something applicable was found * (i.e. something that fits and that does not consist of only-off outputs). */ static gboolean trim_rightmost_outputs_that_dont_fit_in_framebuffer (GsdRRScreen *rr_screen, GsdRRConfig *config) { GsdRROutputInfo **outputs; int i; gboolean applicable; GPtrArray *sorted_outputs; outputs = gsd_rr_config_get_outputs (config); g_return_val_if_fail (outputs != NULL, FALSE); /* How many are on? */ sorted_outputs = g_ptr_array_new (); for (i = 0; outputs[i] != NULL; i++) { if (gsd_rr_output_info_is_active (outputs[i])) g_ptr_array_add (sorted_outputs, outputs[i]); } /* Lay them out from left to right */ g_ptr_array_sort (sorted_outputs, compare_output_positions); /* Trim! */ applicable = FALSE; for (i = sorted_outputs->len - 1; i >= 0; i--) { GError *error = NULL; gboolean is_bounds_error; applicable = gsd_rr_config_applicable (config, rr_screen, &error); if (applicable) break; is_bounds_error = g_error_matches (error, GSD_RR_ERROR, GSD_RR_ERROR_BOUNDS_ERROR); g_error_free (error); if (!is_bounds_error) break; gsd_rr_output_info_set_active (sorted_outputs->pdata[i], FALSE); } if (config_is_all_off (config)) applicable = FALSE; g_ptr_array_free (sorted_outputs, FALSE); return applicable; } static gboolean follow_laptop_lid(GsdXrandrManager *manager) { GsdXrandrBootBehaviour val; val = g_settings_get_enum (manager->priv->settings, CONF_KEY_DEFAULT_MONITORS_SETUP); return val == GSD_XRANDR_BOOT_BEHAVIOUR_FOLLOW_LID || val == GSD_XRANDR_BOOT_BEHAVIOUR_CLONE; } static GsdRRConfig * make_xinerama_setup (GsdXrandrManager *manager, GsdRRScreen *screen) { /* Turn on everything that has a preferred mode, and * position it from left to right */ GsdRRConfig *result = gsd_rr_config_new_current (screen, NULL); GsdRROutputInfo **outputs = gsd_rr_config_get_outputs (result); int i; int x; gsd_rr_config_set_clone (result, FALSE); x = 0; for (i = 0; outputs[i] != NULL; ++i) { GsdRROutputInfo *info = outputs[i]; if (is_laptop (screen, info)) { if (laptop_lid_is_closed (manager) && follow_laptop_lid (manager)) gsd_rr_output_info_set_active (info, FALSE); else { gsd_rr_output_info_set_primary (info, TRUE); x = turn_on_and_get_rightmost_offset (screen, info, x); } } } for (i = 0; outputs[i] != NULL; ++i) { GsdRROutputInfo *info = outputs[i]; if (gsd_rr_output_info_is_connected (info) && !is_laptop (screen, info)) { gsd_rr_output_info_set_primary (info, FALSE); x = turn_on_and_get_rightmost_offset (screen, info, x); } } if (!trim_rightmost_outputs_that_dont_fit_in_framebuffer (screen, result)) { g_object_unref (G_OBJECT (result)); result = NULL; } print_configuration (result, "xinerama setup"); return result; } static GsdRRConfig * make_other_setup (GsdRRScreen *screen) { /* Turn off all laptops, and make all external monitors clone * from (0, 0) */ GsdRRConfig *result = gsd_rr_config_new_current (screen, NULL); GsdRROutputInfo **outputs = gsd_rr_config_get_outputs (result); int i; gsd_rr_config_set_clone (result, FALSE); for (i = 0; outputs[i] != NULL; ++i) { GsdRROutputInfo *info = outputs[i]; if (is_laptop (screen, info)) { gsd_rr_output_info_set_active (info, FALSE); } else { if (gsd_rr_output_info_is_connected (info)) turn_on (screen, info, 0, 0); } } if (!trim_rightmost_outputs_that_dont_fit_in_framebuffer (screen, result)) { g_object_unref (G_OBJECT (result)); result = NULL; } print_configuration (result, "other setup"); return result; } static GPtrArray * sanitize (GsdXrandrManager *manager, GPtrArray *array) { int i; GPtrArray *new; g_debug ("before sanitizing"); for (i = 0; i < array->len; ++i) { if (array->pdata[i]) { print_configuration (array->pdata[i], "before"); } } /* Remove configurations that are duplicates of * configurations earlier in the cycle */ for (i = 0; i < array->len; i++) { int j; for (j = i + 1; j < array->len; j++) { GsdRRConfig *this = array->pdata[j]; GsdRRConfig *other = array->pdata[i]; if (this && other && gsd_rr_config_equal (this, other)) { g_debug ("removing duplicate configuration"); g_object_unref (this); array->pdata[j] = NULL; break; } } } for (i = 0; i < array->len; ++i) { GsdRRConfig *config = array->pdata[i]; if (config && config_is_all_off (config)) { g_debug ("removing configuration as all outputs are off"); g_object_unref (array->pdata[i]); array->pdata[i] = NULL; } } /* Do a final sanitization pass. This will remove configurations that * don't fit in the framebuffer's Virtual size. */ for (i = 0; i < array->len; i++) { GsdRRConfig *config = array->pdata[i]; if (config) { GError *error; error = NULL; if (!gsd_rr_config_applicable (config, manager->priv->rw_screen, &error)) { /* NULL-GError */ g_debug ("removing configuration which is not applicable because %s", error->message); g_error_free (error); g_object_unref (config); array->pdata[i] = NULL; } } } /* Remove NULL configurations */ new = g_ptr_array_new (); for (i = 0; i < array->len; ++i) { if (array->pdata[i]) { g_ptr_array_add (new, array->pdata[i]); print_configuration (array->pdata[i], "Final"); } } if (new->len > 0) { g_ptr_array_add (new, NULL); } else { g_ptr_array_free (new, TRUE); new = NULL; } g_ptr_array_free (array, TRUE); return new; } static void generate_fn_f7_configs (GsdXrandrManager *mgr) { GPtrArray *array = g_ptr_array_new (); GsdRRScreen *screen = mgr->priv->rw_screen; g_debug ("Generating configurations"); /* Free any existing list of configurations */ if (mgr->priv->fn_f7_configs) { int i; for (i = 0; mgr->priv->fn_f7_configs[i] != NULL; ++i) g_object_unref (mgr->priv->fn_f7_configs[i]); g_free (mgr->priv->fn_f7_configs); mgr->priv->fn_f7_configs = NULL; mgr->priv->current_fn_f7_config = -1; } g_ptr_array_add (array, gsd_rr_config_new_current (screen, NULL)); g_ptr_array_add (array, make_clone_setup (mgr, screen)); g_ptr_array_add (array, make_xinerama_setup (mgr, screen)); g_ptr_array_add (array, make_other_setup (screen)); g_ptr_array_add (array, make_laptop_setup (mgr, screen)); array = sanitize (mgr, array); if (array) { mgr->priv->fn_f7_configs = (GsdRRConfig **)g_ptr_array_free (array, FALSE); mgr->priv->current_fn_f7_config = 0; } } static void error_message (GsdXrandrManager *mgr, const char *primary_text, GError *error_to_display, const char *secondary_text) { GtkWidget *dialog; dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", primary_text); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error_to_display ? error_to_display->message : secondary_text); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static void handle_fn_f7 (GsdXrandrManager *mgr, guint32 timestamp) { GsdXrandrManagerPrivate *priv = mgr->priv; GsdRRScreen *screen = priv->rw_screen; GsdRRConfig *current; GError *error; /* Theory of fn-F7 operation * * We maintain a datastructure "fn_f7_status", that contains * a list of GsdRRConfig's. Each of the GsdRRConfigs has a * mode (or "off") for each connected output. * * When the user hits fn-F7, we cycle to the next GsdRRConfig * in the data structure. If the data structure does not exist, it * is generated. If the configs in the data structure do not match * the current hardware reality, it is regenerated. * */ g_debug ("Handling fn-f7"); log_open (); log_msg ("Handling XF86Display hotkey - timestamp %u\n", timestamp); error = NULL; if (!gsd_rr_screen_refresh (screen, &error) && error) { char *str; str = g_strdup_printf (_("Could not refresh the screen information: %s"), error->message); g_error_free (error); log_msg ("%s\n", str); error_message (mgr, str, NULL, _("Trying to switch the monitor configuration anyway.")); g_free (str); } if (!priv->fn_f7_configs) { log_msg ("Generating stock configurations:\n"); generate_fn_f7_configs (mgr); log_configurations (priv->fn_f7_configs); } current = gsd_rr_config_new_current (screen, NULL); if (priv->fn_f7_configs && (!gsd_rr_config_match (current, priv->fn_f7_configs[0]) || !gsd_rr_config_equal (current, priv->fn_f7_configs[mgr->priv->current_fn_f7_config]))) { /* Our view of the world is incorrect, so regenerate the * configurations */ generate_fn_f7_configs (mgr); log_msg ("Regenerated stock configurations:\n"); log_configurations (priv->fn_f7_configs); } g_object_unref (current); if (priv->fn_f7_configs) { guint32 server_timestamp; gboolean success; mgr->priv->current_fn_f7_config++; if (priv->fn_f7_configs[mgr->priv->current_fn_f7_config] == NULL) mgr->priv->current_fn_f7_config = 0; g_debug ("cycling to next configuration (%d)", mgr->priv->current_fn_f7_config); print_configuration (priv->fn_f7_configs[mgr->priv->current_fn_f7_config], "new config"); g_debug ("applying"); /* See https://bugzilla.gnome.org/show_bug.cgi?id=610482 * * Sometimes we'll get two rapid XF86Display keypress events, * but their timestamps will be out of order with respect to the * RANDR timestamps. This *may* be due to stupid BIOSes sending * out display-switch keystrokes "to make Windows work". * * The X server will error out if the timestamp provided is * older than a previous change configuration timestamp. We * assume here that we do want this event to go through still, * since kernel timestamps may be skewed wrt the X server. */ gsd_rr_screen_get_timestamps (screen, NULL, &server_timestamp); if (timestamp < server_timestamp) timestamp = server_timestamp; success = apply_configuration (mgr, priv->fn_f7_configs[mgr->priv->current_fn_f7_config], timestamp, TRUE, TRUE); if (success) { log_msg ("Successfully switched to configuration (timestamp %u):\n", timestamp); log_configuration (priv->fn_f7_configs[mgr->priv->current_fn_f7_config]); } } else { g_debug ("no configurations generated"); } log_close (); g_debug ("done handling fn-f7"); } static GsdRRRotation get_next_rotation (GsdRRRotation allowed_rotations, GsdRRRotation current_rotation) { int i; int current_index; /* First, find the index of the current rotation */ current_index = -1; for (i = 0; i < G_N_ELEMENTS (possible_rotations); i++) { GsdRRRotation r; r = possible_rotations[i]; if (r == current_rotation) { current_index = i; break; } } if (current_index == -1) { /* Huh, the current_rotation was not one of the supported rotations. Bail out. */ return current_rotation; } /* Then, find the next rotation that is allowed */ i = (current_index + 1) % G_N_ELEMENTS (possible_rotations); while (1) { GsdRRRotation r; r = possible_rotations[i]; if (r == current_rotation) { /* We wrapped around and no other rotation is suported. Bummer. */ return current_rotation; } else if (r & allowed_rotations) return r; i = (i + 1) % G_N_ELEMENTS (possible_rotations); } } struct { GsdRRRotation rotation; /* Coordinate Transformation Matrix */ gfloat matrix[9]; } evdev_rotations[] = { { GSD_RR_ROTATION_0, {1, 0, 0, 0, 1, 0, 0, 0, 1}}, { GSD_RR_ROTATION_90, {0, -1, 1, 1, 0, 0, 0, 0, 1}}, { GSD_RR_ROTATION_180, {-1, 0, 1, 0, -1, 1, 0, 0, 1}}, { GSD_RR_ROTATION_270, {0, 1, 0, -1, 0, 1, 0, 0, 1}} }; static guint get_rotation_index (GsdRRRotation rotation) { guint i; for (i = 0; i < G_N_ELEMENTS (evdev_rotations); i++) { if (evdev_rotations[i].rotation == rotation) return i; } g_assert_not_reached (); } static gboolean is_wacom_tablet_device (GsdXrandrManager *mgr, XDeviceInfo *device_info) { #ifdef HAVE_WACOM GsdXrandrManagerPrivate *priv = mgr->priv; gchar *device_node; WacomDevice *wacom_device; gboolean is_tablet = FALSE; if (priv->wacom_db == NULL) priv->wacom_db = libwacom_database_new (); device_node = xdevice_get_device_node (device_info->id); if (device_node == NULL) return FALSE; wacom_device = libwacom_new_from_path (priv->wacom_db, device_node, FALSE, NULL); g_free (device_node); if (wacom_device == NULL) { g_free (device_node); return FALSE; } is_tablet = libwacom_has_touch (wacom_device) && libwacom_is_builtin (wacom_device); libwacom_destroy (wacom_device); return is_tablet; #else /* HAVE_WACOM */ return FALSE; #endif /* HAVE_WACOM */ } static void rotate_touchscreens (GsdXrandrManager *mgr, GsdRRRotation rotation) { XDeviceInfo *device_info; gint n_devices; guint i, rot_idx; Atom float_atom; if (!supports_xinput_devices ()) return; g_debug ("Rotating touchscreen devices"); device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) return; rot_idx = get_rotation_index (rotation); float_atom = XInternAtom(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), "FLOAT", True); for (i = 0; i < n_devices; i++) { if (is_wacom_tablet_device (mgr, &device_info[i])) { g_debug ("Not rotating tablet device '%s'", device_info[i].name); continue; } if (device_info_is_touchscreen (&device_info[i]) || device_info_is_tablet (&device_info[i])) { XDevice *device; gfloat *m = evdev_rotations[rot_idx].matrix; PropertyHelper matrix = { .name = "Coordinate Transformation Matrix", .nitems = 9, .format = 32, .type = float_atom, .data.i = (int *)m, }; g_debug ("About to rotate '%s'", device_info[i].name); gdk_error_trap_push (); device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_info[i].id); if (gdk_error_trap_pop () || (device == NULL)) continue; if (device_set_property (device, device_info[i].name, &matrix) != FALSE) { g_debug ("Rotated '%s' to configuration '%f, %f, %f, %f, %f, %f, %f, %f, %f'\n", device_info[i].name, evdev_rotations[rot_idx].matrix[0], evdev_rotations[rot_idx].matrix[1], evdev_rotations[rot_idx].matrix[2], evdev_rotations[rot_idx].matrix[3], evdev_rotations[rot_idx].matrix[4], evdev_rotations[rot_idx].matrix[5], evdev_rotations[rot_idx].matrix[6], evdev_rotations[rot_idx].matrix[7], evdev_rotations[rot_idx].matrix[8]); } XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device); } } XFreeDeviceList (device_info); } /* We use this when the XF86RotateWindows key is pressed, or the * orientation of a tablet changes. The key is present * on some tablet PCs; they use it so that the user can rotate the tablet * easily. Some other tablet PCs will have an accelerometer instead. */ static void handle_rotate_windows (GsdXrandrManager *mgr, GsdRRRotation rotation, guint32 timestamp) { GsdXrandrManagerPrivate *priv = mgr->priv; GsdRRScreen *screen = priv->rw_screen; GsdRRConfig *current; GsdRROutputInfo *rotatable_output_info; int num_allowed_rotations; GsdRRRotation allowed_rotations; GsdRRRotation next_rotation; gboolean success, show_error; g_debug ("Handling XF86RotateWindows with rotation %d", rotation); /* Which output? */ current = gsd_rr_config_new_current (screen, NULL); rotatable_output_info = get_laptop_output_info (screen, current); if (rotatable_output_info == NULL) { g_debug ("No laptop outputs found to rotate; XF86RotateWindows key will do nothing"); goto out; } if (rotation <= GSD_RR_ROTATION_NEXT) { /* Which rotation? */ get_allowed_rotations_for_output (current, priv->rw_screen, rotatable_output_info, &num_allowed_rotations, &allowed_rotations); next_rotation = get_next_rotation (allowed_rotations, gsd_rr_output_info_get_rotation (rotatable_output_info)); if (next_rotation == gsd_rr_output_info_get_rotation (rotatable_output_info)) { g_debug ("No rotations are supported other than the current one; XF86RotateWindows key will do nothing"); goto out; } show_error = TRUE; } else { next_rotation = rotation; show_error = FALSE; } /* Rotate */ gsd_rr_output_info_set_rotation (rotatable_output_info, next_rotation); success = apply_configuration (mgr, current, timestamp, show_error, TRUE); if (success) rotate_touchscreens (mgr, next_rotation); out: g_object_unref (current); } static GsdRRConfig * make_default_setup (GsdXrandrManager *manager) { GsdXrandrManagerPrivate *priv = manager->priv; GsdRRConfig *config; GsdXrandrBootBehaviour boot; boot = g_settings_get_enum (priv->settings, CONF_KEY_DEFAULT_MONITORS_SETUP); g_debug ("xrandr default monitors setup: %d\n", boot); switch (boot) { case GSD_XRANDR_BOOT_BEHAVIOUR_DO_NOTHING: config = make_xinerama_setup (manager, priv->rw_screen); break; case GSD_XRANDR_BOOT_BEHAVIOUR_FOLLOW_LID: if (laptop_lid_is_closed (manager)) config = make_other_setup (priv->rw_screen); else config = make_xinerama_setup (manager, priv->rw_screen); break; case GSD_XRANDR_BOOT_BEHAVIOUR_CLONE: config = make_clone_setup (manager, priv->rw_screen); break; case GSD_XRANDR_BOOT_BEHAVIOUR_DOCK: config = make_other_setup (priv->rw_screen); break; default: g_assert_not_reached (); } return config; } static void auto_configure_outputs (GsdXrandrManager *manager, guint32 timestamp) { GsdRRConfig *config; g_debug ("xrandr auto-configure"); config = make_default_setup (manager); if (config) { apply_configuration (manager, config, timestamp, TRUE, FALSE); g_object_unref (config); } else { g_debug ("No applicable configuration found during auto-configure"); } } static void use_stored_configuration_or_auto_configure_outputs (GsdXrandrManager *manager, guint32 timestamp) { GsdXrandrManagerPrivate *priv = manager->priv; char *intended_filename; GError *error; gboolean success; intended_filename = gsd_rr_config_get_intended_filename (); error = NULL; success = apply_configuration_from_filename (manager, intended_filename, TRUE, timestamp, &error); g_free (intended_filename); if (!success) { /* We don't bother checking the error type. * * Both G_FILE_ERROR_NOENT and * GSD_RR_ERROR_NO_MATCHING_CONFIG would mean, "there * was no configuration to apply, or none that matched * the current outputs", and in that case we need to run * our fallback. * * Any other error means "we couldn't do the smart thing * of using a previously- saved configuration, anyway, * for some other reason. In that case, we also need to * run our fallback to avoid leaving the user with a * bogus configuration. */ if (error) g_error_free (error); if (timestamp != priv->last_config_timestamp || timestamp == GDK_CURRENT_TIME) { priv->last_config_timestamp = timestamp; auto_configure_outputs (manager, timestamp); log_msg (" Automatically configured outputs\n"); } else log_msg (" Ignored autoconfiguration as old and new config timestamps are the same\n"); } else log_msg ("Applied stored configuration\n"); } static void on_randr_event (GsdRRScreen *screen, gpointer data) { GsdXrandrManager *manager = GSD_XRANDR_MANAGER (data); GsdXrandrManagerPrivate *priv = manager->priv; guint32 change_timestamp, config_timestamp; if (!priv->running) return; gsd_rr_screen_get_timestamps (screen, &change_timestamp, &config_timestamp); log_open (); log_msg ("Got RANDR event with timestamps change=%u %c config=%u\n", change_timestamp, timestamp_relationship (change_timestamp, config_timestamp), config_timestamp); if (change_timestamp >= config_timestamp) { GsdRRConfig *rr_config; /* The event is due to an explicit configuration change. * * If the change was performed by us, then we need to do nothing. * * If the change was done by some other X client, we don't need * to do anything, either; the screen is already configured. */ /* Check if we need to update the primary */ rr_config = gsd_rr_config_new_current (priv->rw_screen, NULL); if (gsd_rr_config_ensure_primary (rr_config)) { if (gsd_rr_config_applicable (rr_config, priv->rw_screen, NULL)) { print_configuration (rr_config, "Updating for primary"); priv->last_config_timestamp = config_timestamp; gsd_rr_config_apply_with_time (rr_config, priv->rw_screen, config_timestamp, NULL); } } g_object_unref (rr_config); show_timestamps_dialog (manager, "ignoring since change > config"); log_msg (" Ignoring event since change >= config\n"); } else { /* Here, config_timestamp > change_timestamp. This means that * the screen got reconfigured because of hotplug/unplug; the X * server is just notifying us, and we need to configure the * outputs in a sane way. */ show_timestamps_dialog (manager, "need to deal with reconfiguration, as config > change"); use_stored_configuration_or_auto_configure_outputs (manager, config_timestamp); } if (priv->main_touchscreen_id != -1) { /* Set mapping of input devices onto displays */ log_msg ("\nSetting touchscreen mapping on RandR event\n"); do_touchscreen_mapping (manager); } log_close (); } static void get_allowed_rotations_for_output (GsdRRConfig *config, GsdRRScreen *rr_screen, GsdRROutputInfo *output, int *out_num_rotations, GsdRRRotation *out_rotations) { GsdRRRotation current_rotation; int i; *out_num_rotations = 0; *out_rotations = 0; current_rotation = gsd_rr_output_info_get_rotation (output); /* Yay for brute force */ for (i = 0; i < G_N_ELEMENTS (possible_rotations); i++) { GsdRRRotation rotation_to_test; rotation_to_test = possible_rotations[i]; gsd_rr_output_info_set_rotation (output, rotation_to_test); if (gsd_rr_config_applicable (config, rr_screen, NULL)) { /* NULL-GError */ (*out_num_rotations)++; (*out_rotations) |= rotation_to_test; } } gsd_rr_output_info_set_rotation (output, current_rotation); if (*out_num_rotations == 0 || *out_rotations == 0) { g_warning ("Huh, output %p says it doesn't support any rotations, and yet it has a current rotation?", output); *out_num_rotations = 1; *out_rotations = gsd_rr_output_info_get_rotation (output); } } static gboolean apply_intended_configuration (GsdXrandrManager *manager, const char *intended_filename, guint32 timestamp) { GError *my_error; gboolean result; my_error = NULL; result = apply_configuration_from_filename (manager, intended_filename, TRUE, timestamp, &my_error); if (!result) { if (my_error) { if (!g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT) && !g_error_matches (my_error, GSD_RR_ERROR, GSD_RR_ERROR_NO_MATCHING_CONFIG)) error_message (manager, _("Could not apply the stored configuration for monitors"), my_error, NULL); g_error_free (my_error); } } return result; } static void apply_default_boot_configuration (GsdXrandrManager *mgr, guint32 timestamp) { GsdXrandrManagerPrivate *priv = mgr->priv; GsdRRConfig *config; GsdXrandrBootBehaviour boot; boot = g_settings_get_enum (priv->settings, CONF_KEY_DEFAULT_MONITORS_SETUP); if (boot == GSD_XRANDR_BOOT_BEHAVIOUR_DO_NOTHING) return; config = make_default_setup (mgr); if (config) { /* We don't save the configuration (the "false" parameter to the following function) because we don't want to * install a user-side setting when *here* we are using a system-default setting. */ apply_configuration (mgr, config, timestamp, TRUE, FALSE); g_object_unref (config); } } static gboolean apply_stored_configuration_at_startup (GsdXrandrManager *manager, guint32 timestamp) { GError *my_error; gboolean success; char *backup_filename; char *intended_filename; GsdPnpIds *pnp_ids; /* This avoids the GsdPnpIds object being created multiple times. * See c9240e8b69c5833074508b46bc56307aac12ec19 */ pnp_ids = gsd_pnp_ids_new (); backup_filename = gsd_rr_config_get_backup_filename (); intended_filename = gsd_rr_config_get_intended_filename (); /* 1. See if there was a "saved" configuration. If there is one, it means * that the user had selected to change the display configuration, but the * machine crashed. In that case, we'll apply *that* configuration and save it on top of the * "intended" one. */ my_error = NULL; success = apply_configuration_from_filename (manager, backup_filename, FALSE, timestamp, &my_error); if (success) { /* The backup configuration existed, and could be applied * successfully, so we must restore it on top of the * failed/intended one. */ restore_backup_configuration (manager, backup_filename, intended_filename, timestamp); goto out; } if (!g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { /* Epic fail: there (probably) was a backup configuration, but * we could not apply it. The only thing we can do is delete * the backup configuration. Let's hope that the user doesn't * get left with an unusable display... */ unlink (backup_filename); goto out; } /* 2. There was no backup configuration! This means we are * good. Apply the intended configuration instead. */ success = apply_intended_configuration (manager, intended_filename, timestamp); out: g_object_unref (pnp_ids); if (my_error) g_error_free (my_error); g_free (backup_filename); g_free (intended_filename); return success; } static gboolean apply_default_configuration_from_file (GsdXrandrManager *manager, guint32 timestamp) { GsdXrandrManagerPrivate *priv = manager->priv; char *default_config_filename; gboolean result; default_config_filename = g_settings_get_string (priv->settings, CONF_KEY_DEFAULT_CONFIGURATION_FILE); if (!default_config_filename) return FALSE; result = apply_configuration_from_filename (manager, default_config_filename, TRUE, timestamp, NULL); g_free (default_config_filename); return result; } static void turn_off_laptop_display (GsdXrandrManager *manager, guint32 timestamp) { GsdXrandrManagerPrivate *priv = manager->priv; GsdRRConfig *config; config = gsd_rr_config_new_current (priv->rw_screen, NULL); turn_off_laptop_display_in_configuration (priv->rw_screen, config); /* We don't turn the laptop's display off if it is the only display present. */ if (!config_is_all_off (config)) { /* We don't save the configuration (the "false" parameter to the following function) because we * wouldn't want to restore a configuration with the laptop's display turned off, if at some * point later the user booted his laptop with the lid open. */ apply_configuration (manager, config, timestamp, FALSE, FALSE); } g_object_unref (config); } static void lid_state_changed_cb (UpClient *client, GParamSpec *pspec, gpointer data) { GsdXrandrManager *manager = data; GsdXrandrManagerPrivate *priv = manager->priv; gboolean is_closed; is_closed = up_client_get_lid_is_closed (priv->upower_client); if (is_closed != priv->laptop_lid_is_closed) { priv->laptop_lid_is_closed = is_closed; if (!follow_laptop_lid(manager)) return; /* Refresh the RANDR state. The lid just got opened/closed, so we can afford to * probe the outputs right now. It will also help the case where we can't detect * hotplug/unplug, but the fact that the lid's state changed lets us know that the * user probably did something interesting. */ gsd_rr_screen_refresh (priv->rw_screen, NULL); /* NULL-GError */ if (is_closed) turn_off_laptop_display (manager, GDK_CURRENT_TIME); /* sucks not to have a timestamp for the notification */ else use_stored_configuration_or_auto_configure_outputs (manager, GDK_CURRENT_TIME); } } static gboolean matches_name (GsdRROutputInfo *output, GsdRROutput *to_match) { return (g_strcmp0 (gsd_rr_output_info_get_name (output), gsd_rr_output_get_name (to_match) ) == 0); } static gint monitor_for_output (GsdRROutput *output) { GdkScreen *screen = gdk_screen_get_default (); GsdRRCrtc *crtc = gsd_rr_output_get_crtc (output); gint x, y; if (!crtc) return -1; gsd_rr_crtc_get_position (crtc, &x, &y); return gdk_screen_get_monitor_at_point (screen, x, y); } static gboolean output_get_dimensions (GsdRROutput *output, guint *width, guint *height) { GdkScreen *screen = gdk_screen_get_default (); gint monitor_num; monitor_num = monitor_for_output (output); if (monitor_num < 0) return FALSE; *width = gdk_screen_get_monitor_width_mm (screen, monitor_num); *height = gdk_screen_get_monitor_height_mm (screen, monitor_num); return TRUE; } static GsdRROutput * input_info_find_size_match (GsdXrandrManager *manager, GsdRRScreen *rr_screen) { guint i, input_width, input_height, output_width, output_height; gdouble min_width_diff, min_height_diff; GsdRROutput **outputs, *match = NULL; GsdXrandrManagerPrivate *priv = manager->priv; g_return_val_if_fail (rr_screen != NULL, NULL); if (!xdevice_get_dimensions (priv->main_touchscreen_id, &input_width, &input_height)) return NULL; /* Restrict the matches to be below a narrow percentage */ min_width_diff = min_height_diff = 0.05; g_debug ("Input device '%s' has %dx%d mm", priv->main_touchscreen_name, input_width, input_height); outputs = gsd_rr_screen_list_outputs (rr_screen); for (i = 0; outputs[i] != NULL; i++) { gdouble width_diff, height_diff; if (!output_get_dimensions (outputs[i], &output_width, &output_height)) continue; width_diff = ABS (1 - ((gdouble) output_width / input_width)); height_diff = ABS (1 - ((gdouble) output_height / input_height)); g_debug ("Output '%s' has size %dx%d mm, deviation from " "input device size: %.2f width, %.2f height ", gsd_rr_output_get_name (outputs[i]), output_width, output_height, width_diff, height_diff); if (width_diff <= min_width_diff && height_diff <= min_height_diff) { match = outputs[i]; min_width_diff = width_diff; min_height_diff = height_diff; } } if (match) { g_debug ("Output '%s' is considered a best size match (%.2f / %.2f)", gsd_rr_output_get_name (match), min_width_diff, min_height_diff); } else { g_debug ("No input/output size match was found\n"); } return match; } static GsdRROutputInfo * get_mappable_output_info (GsdXrandrManager *manager, GsdRRScreen *screen, GsdRRConfig *config) { int i; GsdRROutputInfo **outputs = gsd_rr_config_get_outputs (config); GsdRROutput *size_match = NULL; size_match = input_info_find_size_match (manager, screen); for (i = 0; outputs[i] != NULL; i++) { if (is_laptop (screen, outputs[i]) || (size_match && matches_name (outputs[i], size_match))) return outputs[i]; } return NULL; } static void set_touchscreen_id (GsdXrandrManager *manager) { GsdXrandrManagerPrivate *priv = manager->priv; XDeviceInfo *device_info; int n_devices; int i; device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) return; for (i = 0; i < n_devices; i++) { if (device_info_is_touchscreen (&device_info[i])) { /* Let's deal only with the 1st touchscreen */ priv->main_touchscreen_id = (int)device_info[i].id; priv->main_touchscreen_name = g_strdup (device_info[i].name); break; } } XFreeDeviceList (device_info); } /* Kill a mapping process */ static gboolean cb_mapping_child_kill (gpointer data) { int kill_status; gchar *message = NULL; TouchMappingPrivate *mapping_data = (TouchMappingPrivate*) data; message = g_strdup_printf ("Killing touchscreen mapping process %d after %d second(s) timeout...", mapping_data->mapping_pid, MAPPING_TIMEOUT); if (!message) { g_error ("Failed to allocate memory to log the killing of the mapping process"); goto out; } g_warning ("%s", message); g_free (message); kill_status = kill (mapping_data->mapping_pid, SIGTERM); /* Mark as killed */ mapping_data->mapping_killed = TRUE; g_debug ("Kill status %d...", kill_status); if (kill_status != 0) g_error ("Failed to kill mapping process: %s", strerror (errno)); out: return G_SOURCE_REMOVE; } /* Clean up spawned processes when they are done */ static void cb_mapping_child_watch (GPid pid, gint status, gpointer data) { TouchMappingPrivate *mapping_data = (TouchMappingPrivate*) data; g_debug ("Cleaning up spawned mapping"); /* Close pid */ g_spawn_close_pid (pid); /* No need to kill a process that ended */ if (mapping_data->mapping_kill_id > 0) { g_debug ("Cancelling killing of process that ended"); g_source_remove (mapping_data->mapping_kill_id); } /* No need to retry if it succeeded */ if (!mapping_data->mapping_killed) { g_debug ("Cancelling retry id %d", mapping_data->mapping_retry_id); g_source_remove (mapping_data->mapping_retry_id); } /* Free mapping data */ g_slice_free (TouchMappingPrivate, mapping_data); mapping_data = NULL; } static int map_touch_to_output (GsdXrandrManager *manager, GsdRROutputInfo *output) { TouchMappingPrivate *mapping_data = NULL; gchar *command_str = NULL; GError **error = NULL; gboolean success = FALSE; gchar **command = NULL; GsdXrandrManagerPrivate *priv = manager->priv; gchar *name = gsd_rr_output_info_get_name (output); if (!name) { g_debug ("Failure to map screen with missing name"); goto out; } if (gsd_rr_output_info_is_active (output)) { g_debug ("Mapping touchscreen %d onto output %s", priv->main_touchscreen_id, name); command_str = g_strdup_printf ("/usr/bin/xinput --map-to-output %d %s", priv->main_touchscreen_id, name); if (!command_str) goto out; if (!g_shell_parse_argv (command_str, NULL, &command, NULL)) goto out; /* Each spawned process gets its own mapping data */ mapping_data = g_slice_new (TouchMappingPrivate); if (!mapping_data) { g_error ("Touchscreen mapping resource allocation failed"); goto out; } /* Initialise mapping data */ mapping_data->mapping_pid = -1; mapping_data->mapping_kill_id = 0; mapping_data->mapping_retry_id = 0; mapping_data->mapping_killed = FALSE; success = g_spawn_async (NULL, command, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &(mapping_data->mapping_pid), error); g_strfreev (command); if (success) { g_debug ("Touchscreen mapping spawn succeeded"); /* Clean up after child is done */ g_child_watch_add (mapping_data->mapping_pid, (GChildWatchFunc) cb_mapping_child_watch, mapping_data); /* Kill the child after n seconds */ mapping_data->mapping_kill_id = g_timeout_add_seconds (MAPPING_TIMEOUT, (GSourceFunc) cb_mapping_child_kill, mapping_data); /* Set potential retry */ g_debug ("Retrying in %d second(s)", RETRY_TIMEOUT+MAPPING_TIMEOUT); mapping_data->mapping_retry_id = g_timeout_add_seconds (RETRY_TIMEOUT+MAPPING_TIMEOUT, (GSourceFunc) do_touchscreen_mapping, manager); g_debug ("Retry id: %d", mapping_data->mapping_retry_id); } else { g_error ("Touchscreen mapping failed"); if (error != NULL) g_error ("%s", (*error)->message); g_clear_error (error); /* Free mapping data */ g_slice_free(TouchMappingPrivate, mapping_data); mapping_data = NULL; } } else { g_debug ("No need to map %d onto output %s. The output is off", priv->main_touchscreen_id, name); } out: g_free (command_str); return success; } static gboolean do_touchscreen_mapping (GsdXrandrManager *manager) { GsdXrandrManagerPrivate *priv = manager->priv; GsdRRScreen *screen = priv->rw_screen; GsdRRConfig *current; GsdRROutputInfo *laptop_output; if (!supports_xinput_devices ()) return; current = gsd_rr_config_new_current (screen, NULL); laptop_output = get_mappable_output_info (manager, screen, current); if (laptop_output == NULL) { g_debug ("No laptop screen detected"); goto out; } if (priv->main_touchscreen_id != -1) { /* Set initial mapping */ g_debug ("Setting initial touchscreen mapping"); map_touch_to_output (manager, laptop_output); } else { g_debug ("No main touchscreen detected"); } out: g_object_unref (current); return G_SOURCE_REMOVE; } gboolean gsd_xrandr_manager_start (GsdXrandrManager *manager, GError **error) { g_debug ("Starting xrandr manager"); gnome_settings_profile_start (NULL); log_open (); log_msg ("------------------------------------------------------------\nSTARTING XRANDR PLUGIN\n"); manager->priv->rw_screen = gsd_rr_screen_new (gdk_screen_get_default (), error); if (manager->priv->rw_screen == NULL) { log_msg ("Could not initialize the RANDR plugin%s%s\n", (error && *error) ? ": " : "", (error && *error) ? (*error)->message : ""); log_close (); return FALSE; } g_signal_connect (manager->priv->rw_screen, "changed", G_CALLBACK (on_randr_event), manager); manager->priv->upower_client = up_client_new (); manager->priv->laptop_lid_is_closed = up_client_get_lid_is_closed (manager->priv->upower_client); g_signal_connect (manager->priv->upower_client, "notify::lid-is-closed", G_CALLBACK (lid_state_changed_cb), manager); log_msg ("State of screen at startup:\n"); log_screen (manager->priv->rw_screen); manager->priv->running = TRUE; manager->priv->settings = g_settings_new (CONF_SCHEMA); register_manager_dbus (manager_object); show_timestamps_dialog (manager, "Startup"); if (!apply_stored_configuration_at_startup (manager, GDK_CURRENT_TIME)) /* we don't have a real timestamp at startup anyway */ if (!apply_default_configuration_from_file (manager, GDK_CURRENT_TIME)) apply_default_boot_configuration (manager, GDK_CURRENT_TIME); /* Initialise touchscreen mapping */ set_touchscreen_id (manager); if (manager->priv->main_touchscreen_id != -1) do_touchscreen_mapping (manager); log_msg ("State of screen after initial configuration:\n"); log_screen (manager->priv->rw_screen); log_close (); gnome_settings_profile_end (NULL); return TRUE; } void gsd_xrandr_manager_stop (GsdXrandrManager *manager) { g_debug ("Stopping xrandr manager"); manager->priv->running = FALSE; if (manager->priv->bus_cancellable != NULL) { g_cancellable_cancel (manager->priv->bus_cancellable); g_object_unref (manager->priv->bus_cancellable); manager->priv->bus_cancellable = NULL; } if (manager->priv->settings != NULL) { g_object_unref (manager->priv->settings); manager->priv->settings = NULL; } if (manager->priv->rw_screen != NULL) { g_object_unref (manager->priv->rw_screen); manager->priv->rw_screen = NULL; } if (manager->priv->upower_client != NULL) { g_signal_handlers_disconnect_by_data (manager->priv->upower_client, manager); g_object_unref (manager->priv->upower_client); manager->priv->upower_client = NULL; } if (manager->priv->name_id != 0) g_bus_unown_name (manager->priv->name_id); if (manager->priv->introspection_data) { g_dbus_node_info_unref (manager->priv->introspection_data); manager->priv->introspection_data = NULL; } if (manager->priv->connection != NULL) { g_object_unref (manager->priv->connection); manager->priv->connection = NULL; } #ifdef HAVE_WACOM if (manager->priv->wacom_db != NULL) { libwacom_database_destroy (manager->priv->wacom_db); manager->priv->wacom_db = NULL; } #endif /* HAVE_WACOM */ g_free (manager->priv->main_touchscreen_name); log_open (); log_msg ("STOPPING XRANDR PLUGIN\n------------------------------------------------------------\n"); log_close (); } static void gsd_xrandr_manager_class_init (GsdXrandrManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_xrandr_manager_finalize; g_type_class_add_private (klass, sizeof (GsdXrandrManagerPrivate)); } static void gsd_xrandr_manager_init (GsdXrandrManager *manager) { manager->priv = GSD_XRANDR_MANAGER_GET_PRIVATE (manager); manager->priv->current_fn_f7_config = -1; manager->priv->fn_f7_configs = NULL; /* For touchscreen mapping */ manager->priv->main_touchscreen_id = -1; manager->priv->main_touchscreen_name = NULL; } static void gsd_xrandr_manager_finalize (GObject *object) { GsdXrandrManager *manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_XRANDR_MANAGER (object)); manager = GSD_XRANDR_MANAGER (object); g_return_if_fail (manager->priv != NULL); G_OBJECT_CLASS (gsd_xrandr_manager_parent_class)->finalize (object); } static guint32 clamp_timestamp (gint64 timestamp) { if (timestamp < 0) return 0; if (timestamp > G_MAXUINT32) return G_MAXUINT32; return timestamp; } static void handle_method_call_xrandr_2 (GsdXrandrManager *manager, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation) { gint64 timestamp; GError *error = NULL; g_debug ("Calling method '%s' for org.gnome.SettingsDaemon.XRANDR_2", method_name); if (g_strcmp0 (method_name, "ApplyConfiguration") == 0) { gint64 parent_window_id; g_variant_get (parameters, "(xx)", &parent_window_id, ×tamp); if (gsd_xrandr_manager_2_apply_configuration (manager, parent_window_id, clamp_timestamp(timestamp), &error) == FALSE) { g_dbus_method_invocation_return_gerror (invocation, error); } else { g_dbus_method_invocation_return_value (invocation, NULL); } } else if (g_strcmp0 (method_name, "VideoModeSwitch") == 0) { g_variant_get (parameters, "(x)", ×tamp); gsd_xrandr_manager_2_video_mode_switch (manager, clamp_timestamp(timestamp), NULL); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "Rotate") == 0) { g_variant_get (parameters, "(x)", ×tamp); gsd_xrandr_manager_2_rotate (manager, clamp_timestamp(timestamp), NULL); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "RotateTo") == 0) { GsdRRRotation rotation; g_variant_get (parameters, "(ix)", &rotation, ×tamp); gsd_xrandr_manager_2_rotate_to (manager, rotation, clamp_timestamp(timestamp), NULL); g_dbus_method_invocation_return_value (invocation, NULL); } } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GsdXrandrManager *manager = (GsdXrandrManager *) user_data; g_debug ("Handling method call %s.%s", interface_name, method_name); if (g_strcmp0 (interface_name, "org.gnome.SettingsDaemon.XRANDR_2") == 0) handle_method_call_xrandr_2 (manager, method_name, parameters, invocation); else g_warning ("unknown interface: %s", interface_name); } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, /* Get Property */ NULL, /* Set Property */ }; static void on_bus_gotten (GObject *source_object, GAsyncResult *res, GsdXrandrManager *manager) { GDBusConnection *connection; GError *error = NULL; GDBusInterfaceInfo **infos; int i; if (manager->priv->bus_cancellable == NULL || g_cancellable_is_cancelled (manager->priv->bus_cancellable)) { g_warning ("Operation has been cancelled, so not retrieving session bus"); return; } connection = g_bus_get_finish (res, &error); if (connection == NULL) { g_warning ("Could not get session bus: %s", error->message); g_error_free (error); return; } manager->priv->connection = connection; infos = manager->priv->introspection_data->interfaces; for (i = 0; infos[i] != NULL; i++) { g_dbus_connection_register_object (connection, GSD_XRANDR_DBUS_PATH, infos[i], &interface_vtable, manager, NULL, NULL); } manager->priv->name_id = g_bus_own_name_on_connection (connection, GSD_XRANDR_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, NULL, NULL, NULL); } static void register_manager_dbus (GsdXrandrManager *manager) { manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); manager->priv->bus_cancellable = g_cancellable_new (); g_assert (manager->priv->introspection_data != NULL); g_bus_get (G_BUS_TYPE_SESSION, manager->priv->bus_cancellable, (GAsyncReadyCallback) on_bus_gotten, manager); } GsdXrandrManager * gsd_xrandr_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_XRANDR_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_XRANDR_MANAGER (manager_object); } ./plugins/xrandr/gsd-xrandr-plugin.c0000644000004100000410000000202012735467744017750 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-xrandr-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdXrandr, gsd_xrandr) ./plugins/xrandr/xrandr.gnome-settings-plugin.in0000644000004100000410000000026712735467744022336 0ustar www-datawww-data[GNOME Settings Plugin] Module=xrandr IAge=0 Priority=2 _Name=XRandR _Description=Set up screen size and rotation settings Authors=Various Copyright=Copyright © 2007 Novell Website= ./plugins/xrandr/test-xrandr.c0000644000004100000410000000031212735467744016660 0ustar www-datawww-data#define NEW gsd_xrandr_manager_new #define START gsd_xrandr_manager_start #define STOP gsd_xrandr_manager_stop #define MANAGER GsdXrandrManager #include "gsd-xrandr-manager.h" #include "test-plugin.h" ./plugins/xrandr/Makefile.am0000644000004100000410000000621112735467744016301 0ustar www-datawww-dataplugin_name = xrandr icondir = $(datadir)/icons/hicolor context = apps ICON_FILES = \ usd-xrandr-16.png \ usd-xrandr-22.png \ usd-xrandr-24.png \ usd-xrandr-32.png \ usd-xrandr.svg #$(mkinstalldirs) $(DESTDIR)$(sysconfdir)/gnome-settings-daemon/xrandr # FIXME: Conflicts with g-s-d, but we might want to use the same config file install-data-local: $(mkinstalldirs) $(DESTDIR)$(icondir)/16x16/$(context) $(mkinstalldirs) $(DESTDIR)$(icondir)/22x22/$(context) $(mkinstalldirs) $(DESTDIR)$(icondir)/24x24/$(context) $(mkinstalldirs) $(DESTDIR)$(icondir)/32x32/$(context) $(mkinstalldirs) $(DESTDIR)$(icondir)/scalable/$(context) $(INSTALL_DATA) $(srcdir)/usd-xrandr-16.png $(DESTDIR)$(icondir)/16x16/$(context)/usd-xrandr.png $(INSTALL_DATA) $(srcdir)/usd-xrandr-22.png $(DESTDIR)$(icondir)/22x22/$(context)/usd-xrandr.png $(INSTALL_DATA) $(srcdir)/usd-xrandr-24.png $(DESTDIR)$(icondir)/24x24/$(context)/usd-xrandr.png $(INSTALL_DATA) $(srcdir)/usd-xrandr-32.png $(DESTDIR)$(icondir)/32x32/$(context)/usd-xrandr.png $(INSTALL_DATA) $(srcdir)/usd-xrandr.svg $(DESTDIR)$(icondir)/scalable/$(context)/usd-xrandr.svg uninstall-local: rm -f $(DESTDIR)$(icondir)/16x16/$(context)/usd-xrandr.png rm -f $(DESTDIR)$(icondir)/22x22/$(context)/usd-xrandr.png rm -f $(DESTDIR)$(icondir)/24x24/$(context)/usd-xrandr.png rm -f $(DESTDIR)$(icondir)/32x32/$(context)/usd-xrandr.png rm -f $(DESTDIR)$(icondir)/scalable/$(context)/usd-xrandr.svg libexec_PROGRAMS = usd-test-xrandr usd_test_xrandr_SOURCES = \ test-xrandr.c \ gsd-xrandr-manager.c \ gsd-xrandr-manager.h usd_test_xrandr_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) usd_test_xrandr_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(XRANDR_CFLAGS) \ $(AM_CFLAGS) usd_test_xrandr_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/gnome-settings-daemon/libunity-settings-daemon.la \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ $(XRANDR_LIBS) plugin_LTLIBRARIES = \ libxrandr.la libxrandr_la_SOURCES = \ gsd-xrandr-plugin.c \ gsd-xrandr-manager.h \ gsd-xrandr-manager.c libxrandr_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/plugins/common/ \ -DBINDIR=\"$(bindir)\" \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libxrandr_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(XRANDR_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(WACOM_CFLAGS) \ $(AM_CFLAGS) libxrandr_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libxrandr_la_LIBADD = \ $(top_builddir)/plugins/common/libcommon.la \ $(XRANDR_LIBS) \ $(WACOM_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = \ xrandr.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = $(plugin_in_files) $(ICON_FILES) CLEANFILES = $(plugin_DATA) DISTCLEANFILES = $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/xrandr/usd-xrandr-22.png0000644000004100000410000000154212735467744017265 0ustar www-datawww-dataPNG  IHDRĴl;bKGD pHYs B(xtIME "IDAT8˭OhWޛM0!bBR g"U=bA/zCQВSS!ZB,!~_vvנHȃoo~=HF:X<{=/ezqoY;sgȍVQյYPǍs[  sf7_^dͪ-jG6W>}m @U}g*T XUX&3{TU*"\UO9U$Q2SAUy\;~@UUATY VoUfGS[z$/U`LS?hkU"ii_.Q.;.[mF?q1qL^#jT+ii11ݗy9KKKԢ*MNc c-"0dsy LpH o"1ѳqC<}͟a304VhƼL3 hG7@{}XM$vߛ7vhMzsm{1ƶuCk]Q*=dppp]RG|m+7m ±?o`9G{c k-3338A&0tOr.۸kCm(ǿu r޲9~||Ѹ߽z;`SRXYyX<ՋM@[s56y޺-e·6 scGt4 IENDB`./plugins/xrandr/usd-xrandr-16.png0000644000004100000410000000114512735467744017267 0ustar www-datawww-dataPNG  IHDRabKGDC pHYs B(xtIME 6 IDAT8˕kAM&I!Q VZ AC@`iS@MAň`ՋUmڔ=kjgz$ƾ7+Q^55;jM,ٜMZwjFySS8 @6g(Zm9WvsrJ)U+֬x~YX8i. g]g  }h1 rv: ] xhR=HH Ï:ٜɛɇ$ _2<Ю{Z"{&/7O*; `.0B'czqXX9&@Jidk+S-E[)IR()0M D˒ASGWłN$x * * 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. * */ #ifndef __GSD_XRANDR_MANAGER_H #define __GSD_XRANDR_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_XRANDR_MANAGER (gsd_xrandr_manager_get_type ()) #define GSD_XRANDR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_XRANDR_MANAGER, GsdXrandrManager)) #define GSD_XRANDR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_XRANDR_MANAGER, GsdXrandrManagerClass)) #define GSD_IS_XRANDR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_XRANDR_MANAGER)) #define GSD_IS_XRANDR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_XRANDR_MANAGER)) #define GSD_XRANDR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_XRANDR_MANAGER, GsdXrandrManagerClass)) typedef struct GsdXrandrManagerPrivate GsdXrandrManagerPrivate; typedef struct { GObject parent; GsdXrandrManagerPrivate *priv; } GsdXrandrManager; typedef struct { GObjectClass parent_class; } GsdXrandrManagerClass; GType gsd_xrandr_manager_get_type (void); GsdXrandrManager * gsd_xrandr_manager_new (void); gboolean gsd_xrandr_manager_start (GsdXrandrManager *manager, GError **error); void gsd_xrandr_manager_stop (GsdXrandrManager *manager); G_END_DECLS #endif /* __GSD_XRANDR_MANAGER_H */ ./plugins/xrandr/usd-xrandr.svg0000644000004100000410000004573712735467744017075 0ustar www-datawww-data image/svg+xml Change Resolution Jakub Steiner display resolution video Andreas Nilsson Luca Ferretti <elle.uca@libero.it> http://www.gnome.org ./plugins/xrandr/usd-xrandr-24.png0000644000004100000410000000161512735467744017270 0ustar www-datawww-dataPNG  IHDRw=bKGDC pHYs B(xtIME !HIDATHǽMh\Uw}`B,D]ή;Ak6E7-FOJQ1.B:şViZH߻3oq$Ɵp{{sy+oNIz0!F;| [sZi!q\ߖX[$N i/r7O=ЯLO떝"_57M(uHGud;ZFZLPdxG"~Υ";nXkSC"꽋 E^1""!έq[DMaafyO,z/Np.c50L%`a rOI;K粒,/QVȚ:o0BF ?H:։㵬Z3h:'3w.KKKmR Ey@+7t#QZCiCAc|Hq>8?OM8p{~]C ?  ̍ʯ9) MZ^A.i2<v?Okh<#( Mly뮗xִwQJACT288#cRljh|t|i'?gTz{4)Ji0DkOǎw`st)t'N|0iL8׷kRwu[OO,.VX[w,`MG@ѣc GNͿ ]VD}ɺm 4}=F[kj>z}tM/keŋ}Uھd6IENDB`./plugins/xrandr/usd-xrandr-32.png0000644000004100000410000000310212735467744017260 0ustar www-datawww-dataPNG  IHDR szzbKGDtIME  jVIDATXkpyn+ q"TA.PNUdg:#tQLTL/c[QT*BBj  H0l"Bfwl69y?dO[!7{;<؎"ڵkKwe~fj"H#[nбao~(onO˪G9Py+Wj3y#[>%iWJ(tJ*dt 2p{~\+25!RP[['\B#yCꖵ`pujGbY&x2-J#@~"'1NǕyZB#׳~9\mQ c LL&m#3RO#iz~ y`77GIbս[inh/FCCC\vX,1m;s H=0s Wb~ Ma&Α(-[Kk(@ILʬhEkyH~`aͦfZz3[sE@D*(l{2wJ)M@+I_fEʫH Vox}]:9vf`PJJ$=|zJORUW~ 31@|0wtQ~O'=\?AkG#B:"9E+ V;7Et{ځRJ9t𝷣Riס -/O;;LiaA7>ANJ]::s  8K>̂j\z@٢Ǖ),xTeߤ0-cU"U+ZP\rB߇ |E%066}Hkz栮9 "k/3{]H~`\1>WN% Y eb۹-4x4yW>L 3gey.?}6ue߼|ٛ7L|1UO{Ou[. Ǎ:Bt]GuҿD>_`&C<Řhl2:7Frdt~`Ny#ѱ_@V- Tv4MZ ȴqz`Em[GQ><}=RJu+T/_;P )pj΂/hJ1@b@:%R:W^{+_ZwNʧ ܤ * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-a11y-settings-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdA11ySettings, gsd_a11y_settings) ./plugins/a11y-settings/Makefile.am0000644000004100000410000000260512735467744017417 0ustar www-datawww-dataplugin_name = a11y-settings libexec_PROGRAMS = usd-test-a11y-settings usd_test_a11y_settings_SOURCES = \ gsd-a11y-settings-manager.h \ gsd-a11y-settings-manager.c \ test-a11y-settings.c usd_test_a11y_settings_CFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_a11y_settings_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_PLUGIN_LIBS) plugin_LTLIBRARIES = \ liba11y-settings.la liba11y_settings_la_SOURCES = \ gsd-a11y-settings-manager.c \ gsd-a11y-settings-manager.h \ gsd-a11y-settings-plugin.c liba11y_settings_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) liba11y_settings_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) liba11y_settings_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) liba11y_settings_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = \ a11y-settings.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/a11y-settings/gsd-a11y-settings-manager.h0000644000004100000410000000474112735467744022333 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_A11Y_SETTINGS_MANAGER_H #define __GSD_A11Y_SETTINGS_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_A11Y_SETTINGS_MANAGER (gsd_a11y_settings_manager_get_type ()) #define GSD_A11Y_SETTINGS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_A11Y_SETTINGS_MANAGER, GsdA11ySettingsManager)) #define GSD_A11Y_SETTINGS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_A11Y_SETTINGS_MANAGER, GsdA11ySettingsManagerClass)) #define GSD_IS_A11Y_SETTINGS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_A11Y_SETTINGS_MANAGER)) #define GSD_IS_A11Y_SETTINGS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_A11Y_SETTINGS_MANAGER)) #define GSD_A11Y_SETTINGS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_A11Y_SETTINGS_MANAGER, GsdA11ySettingsManagerClass)) typedef struct GsdA11ySettingsManagerPrivate GsdA11ySettingsManagerPrivate; typedef struct { GObject parent; GsdA11ySettingsManagerPrivate *priv; } GsdA11ySettingsManager; typedef struct { GObjectClass parent_class; } GsdA11ySettingsManagerClass; GType gsd_a11y_settings_manager_get_type (void); GsdA11ySettingsManager *gsd_a11y_settings_manager_new (void); gboolean gsd_a11y_settings_manager_start (GsdA11ySettingsManager *manager, GError **error); void gsd_a11y_settings_manager_stop (GsdA11ySettingsManager *manager); G_END_DECLS #endif /* __GSD_A11Y_SETTINGS_MANAGER_H */ ./plugins/a11y-settings/gsd-a11y-settings-manager.c0000644000004100000410000001541212735467744022323 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-profile.h" #include "gsd-a11y-settings-manager.h" #define GSD_A11Y_SETTINGS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_A11Y_SETTINGS_MANAGER, GsdA11ySettingsManagerPrivate)) struct GsdA11ySettingsManagerPrivate { GSettings *interface_settings; GSettings *a11y_apps_settings; }; enum { PROP_0, }; static void gsd_a11y_settings_manager_class_init (GsdA11ySettingsManagerClass *klass); static void gsd_a11y_settings_manager_init (GsdA11ySettingsManager *a11y_settings_manager); static void gsd_a11y_settings_manager_finalize (GObject *object); G_DEFINE_TYPE (GsdA11ySettingsManager, gsd_a11y_settings_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static void apps_settings_changed (GSettings *settings, const char *key, GsdA11ySettingsManager *manager) { gboolean screen_reader, keyboard; if (g_str_equal (key, "screen-reader-enabled") == FALSE && g_str_equal (key, "screen-keyboard-enabled") == FALSE) return; g_debug ("screen reader or OSK enablement changed"); screen_reader = g_settings_get_boolean (manager->priv->a11y_apps_settings, "screen-reader-enabled"); keyboard = g_settings_get_boolean (manager->priv->a11y_apps_settings, "screen-keyboard-enabled"); if (screen_reader || keyboard) { g_debug ("Enabling toolkit-accessibility, screen reader or OSK enabled"); g_settings_set_boolean (manager->priv->interface_settings, "toolkit-accessibility", TRUE); } else if (screen_reader == FALSE && keyboard == FALSE) { g_debug ("Disabling toolkit-accessibility, screen reader and OSK disabled"); g_settings_set_boolean (manager->priv->interface_settings, "toolkit-accessibility", FALSE); } } gboolean gsd_a11y_settings_manager_start (GsdA11ySettingsManager *manager, GError **error) { g_debug ("Starting a11y_settings manager"); gnome_settings_profile_start (NULL); manager->priv->interface_settings = g_settings_new ("org.gnome.desktop.interface"); manager->priv->a11y_apps_settings = g_settings_new ("org.gnome.desktop.a11y.applications"); g_signal_connect (G_OBJECT (manager->priv->a11y_apps_settings), "changed", G_CALLBACK (apps_settings_changed), manager); /* If any of the screen reader or on-screen keyboard are enabled, * make sure a11y is enabled for the toolkits. * We don't do the same thing for the reverse so it's possible to * enable AT-SPI for the toolkits without using an a11y app */ if (g_settings_get_boolean (manager->priv->a11y_apps_settings, "screen-keyboard-enabled") || g_settings_get_boolean (manager->priv->a11y_apps_settings, "screen-reader-enabled")) g_settings_set_boolean (manager->priv->interface_settings, "toolkit-accessibility", TRUE); gnome_settings_profile_end (NULL); return TRUE; } void gsd_a11y_settings_manager_stop (GsdA11ySettingsManager *manager) { if (manager->priv->interface_settings) { g_object_unref (manager->priv->interface_settings); manager->priv->interface_settings = NULL; } if (manager->priv->a11y_apps_settings) { g_object_unref (manager->priv->a11y_apps_settings); manager->priv->a11y_apps_settings = NULL; } g_debug ("Stopping a11y_settings manager"); } static GObject * gsd_a11y_settings_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdA11ySettingsManager *a11y_settings_manager; a11y_settings_manager = GSD_A11Y_SETTINGS_MANAGER (G_OBJECT_CLASS (gsd_a11y_settings_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (a11y_settings_manager); } static void gsd_a11y_settings_manager_dispose (GObject *object) { G_OBJECT_CLASS (gsd_a11y_settings_manager_parent_class)->dispose (object); } static void gsd_a11y_settings_manager_class_init (GsdA11ySettingsManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_a11y_settings_manager_constructor; object_class->dispose = gsd_a11y_settings_manager_dispose; object_class->finalize = gsd_a11y_settings_manager_finalize; g_type_class_add_private (klass, sizeof (GsdA11ySettingsManagerPrivate)); } static void gsd_a11y_settings_manager_init (GsdA11ySettingsManager *manager) { manager->priv = GSD_A11Y_SETTINGS_MANAGER_GET_PRIVATE (manager); } static void gsd_a11y_settings_manager_finalize (GObject *object) { GsdA11ySettingsManager *a11y_settings_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_A11Y_SETTINGS_MANAGER (object)); a11y_settings_manager = GSD_A11Y_SETTINGS_MANAGER (object); g_return_if_fail (a11y_settings_manager->priv != NULL); G_OBJECT_CLASS (gsd_a11y_settings_manager_parent_class)->finalize (object); } GsdA11ySettingsManager * gsd_a11y_settings_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_A11Y_SETTINGS_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_A11Y_SETTINGS_MANAGER (manager_object); } ./plugins/a11y-settings/a11y-settings.gnome-settings-plugin.in0000644000004100000410000000034412735467744024560 0ustar www-datawww-data[GNOME Settings Plugin] Module=a11y-settings IAge=0 Priority=7 _Name=Accessibility settings _Description=Accessibility settings plugin Authors=Bastien Nocera Copyright=Copyright © 2011 Red Hat Inc. Website= ./plugins/a11y-settings/test-a11y-settings.c0000644000004100000410000000035412735467744021114 0ustar www-datawww-data#define NEW gsd_a11y_settings_manager_new #define START gsd_a11y_settings_manager_start #define STOP gsd_a11y_settings_manager_stop #define MANAGER GsdA11ySettingsManager #include "gsd-a11y-settings-manager.h" #include "test-plugin.h" ./plugins/print-notifications/0000755000004100000410000000000012735467744016752 5ustar www-datawww-data./plugins/print-notifications/Makefile.am0000644000004100000410000000400612735467744021006 0ustar www-datawww-dataplugin_name = print-notifications plugin_LTLIBRARIES = \ libprint-notifications.la libprint_notifications_la_SOURCES = \ gsd-print-notifications-manager.c \ gsd-print-notifications-manager.h \ gsd-print-notifications-plugin.c libprint_notifications_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) \ $(CUPS_CPPFLAGS) libprint_notifications_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) libprint_notifications_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libprint_notifications_la_LIBADD = \ $(CUPS_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = \ print-notifications.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) libexec_PROGRAMS = usd-printer usd_printer_SOURCES = \ gsd-printer.c usd_printer_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(LIBNOTIFY_CFLAGS) \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CFLAGS) usd_printer_LDADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(CUPS_LIBS) \ $(LIBNOTIFY_LIBS) libexec_PROGRAMS += usd-test-print-notifications usd_test_print_notifications_SOURCES = \ gsd-print-notifications-manager.c \ gsd-print-notifications-manager.h \ test-print-notifications.c usd_test_print_notifications_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) \ $(CUPS_CPPFLAGS) usd_test_print_notifications_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_print_notifications_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ $(CUPS_LIBS) \ $(LIBNOTIFY_LIBS) EXTRA_DIST = \ $(plugin_in_files) CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/print-notifications/test-print-notifications.c0000644000004100000410000000041212735467744024073 0ustar www-datawww-data#define NEW gsd_print_notifications_manager_new #define START gsd_print_notifications_manager_start #define STOP gsd_print_notifications_manager_stop #define MANAGER GsdPrintNotificationsManager #include "gsd-print-notifications-manager.h" #include "test-plugin.h" ./plugins/print-notifications/gsd-print-notifications-manager.c0000644000004100000410000021263112735467744025311 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * 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 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-profile.h" #include "gsd-print-notifications-manager.h" #define GSD_PRINT_NOTIFICATIONS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER, GsdPrintNotificationsManagerPrivate)) #define CUPS_DBUS_NAME "org.cups.cupsd.Notifier" #define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier" #define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier" #define RENEW_INTERVAL 3500 #define SUBSCRIPTION_DURATION 3600 #define CONNECTING_TIMEOUT 60 #define REASON_TIMEOUT 15000 #define CUPS_CONNECTION_TEST_INTERVAL 300 #define CHECK_INTERVAL 60 /* secs */ #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) #define HAVE_CUPS_1_6 1 #endif #ifndef HAVE_CUPS_1_6 #define ippGetStatusCode(ipp) ipp->request.status.status_code #define ippGetInteger(attr, element) attr->values[element].integer #define ippGetString(attr, element, language) attr->values[element].string.text #define ippGetName(attr) attr->name #define ippGetCount(attr) attr->num_values #define ippGetBoolean(attr, index) attr->values[index].boolean static ipp_attribute_t * ippNextAttribute (ipp_t *ipp) { if (!ipp || !ipp->current) return (NULL); return (ipp->current = ipp->current->next); } #endif struct GsdPrintNotificationsManagerPrivate { GDBusConnection *cups_bus_connection; gint subscription_id; cups_dest_t *dests; gint num_dests; gboolean scp_handler_spawned; GPid scp_handler_pid; GList *timeouts; GHashTable *printing_printers; GList *active_notifications; guint cups_connection_timeout_id; guint check_source_id; guint cups_dbus_subscription_id; guint renew_source_id; gint last_notify_sequence_number; }; enum { PROP_0, }; static void gsd_print_notifications_manager_class_init (GsdPrintNotificationsManagerClass *klass); static void gsd_print_notifications_manager_init (GsdPrintNotificationsManager *print_notifications_manager); static void gsd_print_notifications_manager_finalize (GObject *object); static gboolean cups_connection_test (gpointer user_data); static gboolean process_new_notifications (gpointer user_data); G_DEFINE_TYPE (GsdPrintNotificationsManager, gsd_print_notifications_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static char * get_dest_attr (const char *dest_name, const char *attr, cups_dest_t *dests, int num_dests) { cups_dest_t *dest; const char *value; char *ret; if (dest_name == NULL) return NULL; ret = NULL; dest = cupsGetDest (dest_name, NULL, num_dests, dests); if (dest == NULL) { g_debug ("Unable to find a printer named '%s'", dest_name); goto out; } value = cupsGetOption (attr, dest->num_options, dest->options); if (value == NULL) { g_debug ("Unable to get %s for '%s'", attr, dest_name); goto out; } ret = g_strdup (value); out: return ret; } static gboolean is_local_dest (const char *name, cups_dest_t *dests, int num_dests) { char *type_str; cups_ptype_t type; gboolean is_remote; is_remote = TRUE; type_str = get_dest_attr (name, "printer-type", dests, num_dests); if (type_str == NULL) { goto out; } type = atoi (type_str); is_remote = type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT); g_free (type_str); out: return !is_remote; } static gboolean server_is_local (const gchar *server_name) { if (server_name != NULL && (g_ascii_strncasecmp (server_name, "localhost", 9) == 0 || g_ascii_strncasecmp (server_name, "127.0.0.1", 9) == 0 || g_ascii_strncasecmp (server_name, "::1", 3) == 0 || server_name[0] == '/')) { return TRUE; } else { return FALSE; } } static int strcmp0(const void *a, const void *b) { return g_strcmp0 (*((gchar **) a), *((gchar **) b)); } struct { gchar *printer_name; gchar *primary_text; gchar *secondary_text; guint timeout_id; GsdPrintNotificationsManager *manager; } typedef TimeoutData; struct { gchar *printer_name; gchar *reason; NotifyNotification *notification; gulong notification_close_id; GsdPrintNotificationsManager *manager; } typedef ReasonData; static void free_timeout_data (gpointer user_data) { TimeoutData *data = (TimeoutData *) user_data; if (data) { g_free (data->printer_name); g_free (data->primary_text); g_free (data->secondary_text); g_free (data); } } static void free_reason_data (gpointer user_data) { ReasonData *data = (ReasonData *) user_data; if (data) { if (data->notification_close_id > 0 && g_signal_handler_is_connected (data->notification, data->notification_close_id)) g_signal_handler_disconnect (data->notification, data->notification_close_id); g_object_unref (data->notification); g_free (data->printer_name); g_free (data->reason); g_free (data); } } static void notification_closed_cb (NotifyNotification *notification, gpointer user_data) { ReasonData *data = (ReasonData *) user_data; if (data) { data->manager->priv->active_notifications = g_list_remove (data->manager->priv->active_notifications, data); free_reason_data (data); } } static gboolean show_notification (gpointer user_data) { NotifyNotification *notification; TimeoutData *data = (TimeoutData *) user_data; ReasonData *reason_data; GList *tmp; if (!data) return FALSE; notification = notify_notification_new (data->primary_text, data->secondary_text, "printer-symbolic"); notify_notification_set_app_name (notification, _("Printers")); notify_notification_set_hint (notification, "resident", g_variant_new_boolean (TRUE)); notify_notification_set_timeout (notification, REASON_TIMEOUT); reason_data = g_new0 (ReasonData, 1); reason_data->printer_name = g_strdup (data->printer_name); reason_data->reason = g_strdup ("connecting-to-device"); reason_data->notification = notification; reason_data->manager = data->manager; reason_data->notification_close_id = g_signal_connect (notification, "closed", G_CALLBACK (notification_closed_cb), reason_data); reason_data->manager->priv->active_notifications = g_list_append (reason_data->manager->priv->active_notifications, reason_data); notify_notification_show (notification, NULL); tmp = g_list_find (data->manager->priv->timeouts, data); if (tmp) { data->manager->priv->timeouts = g_list_remove_link (data->manager->priv->timeouts, tmp); g_list_free_full (tmp, free_timeout_data); } return FALSE; } static gboolean reason_is_blacklisted (const gchar *reason) { if (g_str_equal (reason, "none")) return TRUE; if (g_str_equal (reason, "other")) return TRUE; if (g_str_equal (reason, "com.apple.print.recoverable")) return TRUE; /* https://bugzilla.redhat.com/show_bug.cgi?id=883401 */ if (g_str_has_prefix (reason, "cups-remote-")) return TRUE; return FALSE; } static void on_cups_notification (GDBusConnection *connection, const char *sender_name, const char *object_path, const char *interface_name, const char *signal_name, GVariant *parameters, gpointer user_data) { process_new_notifications (user_data); } static void process_cups_notification (GsdPrintNotificationsManager *manager, const char *notify_subscribed_event, const char *notify_text, const char *notify_printer_uri, const char *printer_name, gint printer_state, const char *printer_state_reasons, gboolean printer_is_accepting_jobs, guint notify_job_id, gint job_state, const char *job_state_reasons, const char *job_name, gint job_impressions_completed) { ipp_attribute_t *attr; gboolean my_job = FALSE; gboolean known_reason; http_t *http; gchar *primary_text = NULL; gchar *secondary_text = NULL; gchar *job_uri = NULL; ipp_t *request, *response; static const char * const reasons[] = { "toner-low", "toner-empty", /*"connecting-to-device",*/ "cover-open", "cups-missing-filter", "door-open", "marker-supply-low", "marker-supply-empty", "media-low", "media-empty", "offline", "other"}; static const char * statuses_first[] = { /* Translators: The printer is low on toner (same as in system-config-printer) */ N_("Toner low"), /* Translators: The printer has no toner left (same as in system-config-printer) */ N_("Toner empty"), /* Translators: The printer is in the process of connecting to a shared network output device (same as in system-config-printer) */ /*N_("Not connected?"),*/ /* Translators: One or more covers on the printer are open (same as in system-config-printer) */ N_("Cover open"), /* Translators: A filter or backend is not installed (same as in system-config-printer) */ N_("Printer configuration error"), /* Translators: One or more doors on the printer are open (same as in system-config-printer) */ N_("Door open"), /* Translators: "marker" is one color bin of the printer */ N_("Marker supply low"), /* Translators: "marker" is one color bin of the printer */ N_("Out of a marker supply"), /* Translators: At least one input tray is low on media (same as in system-config-printer) */ N_("Paper low"), /* Translators: At least one input tray is empty (same as in system-config-printer) */ N_("Out of paper"), /* Translators: The printer is offline (same as in system-config-printer) */ N_("Printer off-line"), /* Translators: The printer has detected an error (same as in system-config-printer) */ N_("Printer error") }; static const char * statuses_second[] = { /* Translators: The printer is low on toner (same as in system-config-printer) */ N_("Printer '%s' is low on toner."), /* Translators: The printer has no toner left (same as in system-config-printer) */ N_("Printer '%s' has no toner left."), /* Translators: The printer is in the process of connecting to a shared network output device (same as in system-config-printer) */ /*N_("Printer '%s' may not be connected."),*/ /* Translators: One or more covers on the printer are open (same as in system-config-printer) */ N_("The cover is open on printer '%s'."), /* Translators: A filter or backend is not installed (same as in system-config-printer) */ N_("There is a missing print filter for " "printer '%s'."), /* Translators: One or more doors on the printer are open (same as in system-config-printer) */ N_("The door is open on printer '%s'."), /* Translators: "marker" is one color bin of the printer */ N_("Printer '%s' is low on a marker supply."), /* Translators: "marker" is one color bin of the printer */ N_("Printer '%s' is out of a marker supply."), /* Translators: At least one input tray is low on media (same as in system-config-printer) */ N_("Printer '%s' is low on paper."), /* Translators: At least one input tray is empty (same as in system-config-printer) */ N_("Printer '%s' is out of paper."), /* Translators: The printer is offline (same as in system-config-printer) */ N_("Printer '%s' is currently off-line."), /* Translators: The printer has detected an error (same as in system-config-printer) */ N_("There is a problem on printer '%s'.") }; if (g_strcmp0 (notify_subscribed_event, "printer-added") != 0 && g_strcmp0 (notify_subscribed_event, "printer-deleted") != 0 && g_strcmp0 (notify_subscribed_event, "printer-state-changed") != 0 && g_strcmp0 (notify_subscribed_event, "job-completed") != 0 && g_strcmp0 (notify_subscribed_event, "job-state-changed") != 0 && g_strcmp0 (notify_subscribed_event, "job-created") != 0) return; if (notify_job_id > 0) { if ((http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ())) == NULL) { g_debug ("Connection to CUPS server \'%s\' failed.", cupsServer ()); } else { job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", notify_job_id); request = ippNewRequest (IPP_GET_JOB_ATTRIBUTES); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, job_uri); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser ()); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "job-originating-user-name"); response = cupsDoRequest (http, request, "/"); if (response) { if (ippGetStatusCode (response) <= IPP_OK_CONFLICT && (attr = ippFindAttribute(response, "job-originating-user-name", IPP_TAG_NAME))) { if (g_strcmp0 (ippGetString (attr, 0, NULL), cupsUser ()) == 0) my_job = TRUE; } ippDelete(response); } g_free (job_uri); } } if (g_strcmp0 (notify_subscribed_event, "printer-added") == 0) { cupsFreeDests (manager->priv->num_dests, manager->priv->dests); manager->priv->num_dests = cupsGetDests (&manager->priv->dests); if (is_local_dest (printer_name, manager->priv->dests, manager->priv->num_dests)) { /* Translators: New printer has been added */ primary_text = g_strdup (_("Printer added")); secondary_text = g_strdup (printer_name); } } else if (g_strcmp0 (notify_subscribed_event, "printer-deleted") == 0) { if (is_local_dest (printer_name, manager->priv->dests, manager->priv->num_dests)) { /* Translators: A printer has been removed */ primary_text = g_strdup (_("Printer removed")); secondary_text = g_strdup (printer_name); } cupsFreeDests (manager->priv->num_dests, manager->priv->dests); manager->priv->num_dests = cupsGetDests (&manager->priv->dests); } else if (g_strcmp0 (notify_subscribed_event, "job-completed") == 0 && my_job) { g_hash_table_remove (manager->priv->printing_printers, printer_name); switch (job_state) { case IPP_JOB_PENDING: case IPP_JOB_HELD: case IPP_JOB_PROCESSING: break; case IPP_JOB_STOPPED: /* Translators: A print job has been stopped */ primary_text = g_strdup (_("Printing stopped")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; case IPP_JOB_CANCELED: /* Translators: A print job has been canceled */ primary_text = g_strdup (_("Printing canceled")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; case IPP_JOB_ABORTED: /* Translators: A print job has been aborted */ primary_text = g_strdup (_("Printing aborted")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; case IPP_JOB_COMPLETED: /* Translators: A print job has been completed */ primary_text = g_strdup (_("Printing completed")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; } } else if (g_strcmp0 (notify_subscribed_event, "job-state-changed") == 0 && my_job) { switch (job_state) { case IPP_JOB_PROCESSING: g_hash_table_insert (manager->priv->printing_printers, g_strdup (printer_name), NULL); /* Translators: A job is printing */ primary_text = g_strdup (_("Printing")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; case IPP_JOB_STOPPED: g_hash_table_remove (manager->priv->printing_printers, printer_name); /* Translators: A print job has been stopped */ primary_text = g_strdup (_("Printing stopped")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; case IPP_JOB_CANCELED: g_hash_table_remove (manager->priv->printing_printers, printer_name); /* Translators: A print job has been canceled */ primary_text = g_strdup (_("Printing canceled")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; case IPP_JOB_ABORTED: g_hash_table_remove (manager->priv->printing_printers, printer_name); /* Translators: A print job has been aborted */ primary_text = g_strdup (_("Printing aborted")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; case IPP_JOB_COMPLETED: g_hash_table_remove (manager->priv->printing_printers, printer_name); /* Translators: A print job has been completed */ primary_text = g_strdup (_("Printing completed")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); break; default: break; } } else if (g_strcmp0 (notify_subscribed_event, "job-created") == 0 && my_job) { if (job_state == IPP_JOB_PROCESSING) { g_hash_table_insert (manager->priv->printing_printers, g_strdup (printer_name), NULL); /* Translators: A job is printing */ primary_text = g_strdup (_("Printing")); /* Translators: "print-job xy" on a printer */ secondary_text = g_strdup_printf (_("\"%s\" on %s"), job_name, printer_name); } } else if (g_strcmp0 (notify_subscribed_event, "printer-state-changed") == 0) { cups_dest_t *dest = NULL; const gchar *tmp_printer_state_reasons = NULL; GSList *added_reasons = NULL; GSList *tmp_list = NULL; GList *tmp; gchar **old_state_reasons = NULL; gchar **new_state_reasons = NULL; gint i, j; /* Remove timeout which shows notification about possible disconnection of printer * if "connecting-to-device" has vanished. */ if (printer_state_reasons == NULL || g_strrstr (printer_state_reasons, "connecting-to-device") == NULL) { TimeoutData *data; for (tmp = manager->priv->timeouts; tmp; tmp = g_list_next (tmp)) { data = (TimeoutData *) tmp->data; if (g_strcmp0 (printer_name, data->printer_name) == 0) { g_source_remove (data->timeout_id); manager->priv->timeouts = g_list_remove_link (manager->priv->timeouts, tmp); g_list_free_full (tmp, free_timeout_data); break; } } } for (tmp = manager->priv->active_notifications; tmp; tmp = g_list_next (tmp)) { ReasonData *reason_data = (ReasonData *) tmp->data; GList *remove_list; if (printer_state_reasons == NULL || (g_strcmp0 (printer_name, reason_data->printer_name) == 0 && g_strrstr (printer_state_reasons, reason_data->reason) == NULL)) { if (reason_data->notification_close_id > 0 && g_signal_handler_is_connected (reason_data->notification, reason_data->notification_close_id)) { g_signal_handler_disconnect (reason_data->notification, reason_data->notification_close_id); reason_data->notification_close_id = 0; } notify_notification_close (reason_data->notification, NULL); remove_list = tmp; tmp = g_list_next (tmp); manager->priv->active_notifications = g_list_remove_link (manager->priv->active_notifications, remove_list); g_list_free_full (remove_list, free_reason_data); } } /* Check whether we are printing on this printer right now. */ if (g_hash_table_lookup_extended (manager->priv->printing_printers, printer_name, NULL, NULL)) { dest = cupsGetDest (printer_name, NULL, manager->priv->num_dests, manager->priv->dests); if (dest) tmp_printer_state_reasons = cupsGetOption ("printer-state-reasons", dest->num_options, dest->options); if (tmp_printer_state_reasons) old_state_reasons = g_strsplit (tmp_printer_state_reasons, ",", -1); cupsFreeDests (manager->priv->num_dests, manager->priv->dests); manager->priv->num_dests = cupsGetDests (&manager->priv->dests); dest = cupsGetDest (printer_name, NULL, manager->priv->num_dests, manager->priv->dests); if (dest) tmp_printer_state_reasons = cupsGetOption ("printer-state-reasons", dest->num_options, dest->options); if (tmp_printer_state_reasons) new_state_reasons = g_strsplit (tmp_printer_state_reasons, ",", -1); if (new_state_reasons) qsort (new_state_reasons, g_strv_length (new_state_reasons), sizeof (gchar *), strcmp0); if (old_state_reasons) { qsort (old_state_reasons, g_strv_length (old_state_reasons), sizeof (gchar *), strcmp0); j = 0; for (i = 0; new_state_reasons && i < g_strv_length (new_state_reasons); i++) { while (old_state_reasons[j] && g_strcmp0 (old_state_reasons[j], new_state_reasons[i]) < 0) j++; if (old_state_reasons[j] == NULL || g_strcmp0 (old_state_reasons[j], new_state_reasons[i]) != 0) added_reasons = g_slist_append (added_reasons, new_state_reasons[i]); } } else { for (i = 0; new_state_reasons && i < g_strv_length (new_state_reasons); i++) { added_reasons = g_slist_append (added_reasons, new_state_reasons[i]); } } for (tmp_list = added_reasons; tmp_list; tmp_list = tmp_list->next) { gchar *data = (gchar *) tmp_list->data; known_reason = FALSE; for (j = 0; j < G_N_ELEMENTS (reasons); j++) { if (strncmp (data, reasons[j], strlen (reasons[j])) == 0) { NotifyNotification *notification; known_reason = TRUE; if (g_strcmp0 (reasons[j], "connecting-to-device") == 0) { TimeoutData *data; data = g_new0 (TimeoutData, 1); data->printer_name = g_strdup (printer_name); data->primary_text = g_strdup ( _(statuses_first[j])); data->secondary_text = g_strdup_printf ( _(statuses_second[j]), printer_name); data->manager = manager; data->timeout_id = g_timeout_add_seconds (CONNECTING_TIMEOUT, show_notification, data); manager->priv->timeouts = g_list_append (manager->priv->timeouts, data); } else { ReasonData *reason_data; gchar *second_row = g_strdup_printf ( _(statuses_second[j]), printer_name); notification = notify_notification_new ( _(statuses_first[j]), second_row, "printer-symbolic"); notify_notification_set_app_name (notification, _("Printers")); notify_notification_set_hint (notification, "resident", g_variant_new_boolean (TRUE)); notify_notification_set_timeout (notification, REASON_TIMEOUT); reason_data = g_new0 (ReasonData, 1); reason_data->printer_name = g_strdup (printer_name); reason_data->reason = g_strdup (reasons[j]); reason_data->notification = notification; reason_data->manager = manager; reason_data->notification_close_id = g_signal_connect (notification, "closed", G_CALLBACK (notification_closed_cb), reason_data); manager->priv->active_notifications = g_list_append (manager->priv->active_notifications, reason_data); notify_notification_show (notification, NULL); g_free (second_row); } } } if (!known_reason && !reason_is_blacklisted (data)) { NotifyNotification *notification; ReasonData *reason_data; gchar *first_row; gchar *second_row; gchar *text = NULL; gchar *ppd_file_name; ppd_file_t *ppd_file; char buffer[8192]; ppd_file_name = g_strdup (cupsGetPPD (printer_name)); if (ppd_file_name) { ppd_file = ppdOpenFile (ppd_file_name); if (ppd_file) { gchar **tmpv; static const char * const schemes[] = { "text", "http", "help", "file" }; tmpv = g_new0 (gchar *, G_N_ELEMENTS (schemes) + 1); i = 0; for (j = 0; j < G_N_ELEMENTS (schemes); j++) { if (ppdLocalizeIPPReason (ppd_file, data, schemes[j], buffer, sizeof (buffer))) { tmpv[i++] = g_strdup (buffer); } } if (i > 0) text = g_strjoinv (", ", tmpv); g_strfreev (tmpv); ppdClose (ppd_file); } g_unlink (ppd_file_name); g_free (ppd_file_name); } if (g_str_has_suffix (data, "-report")) /* Translators: This is a title of a report notification for a printer */ first_row = g_strdup (_("Printer report")); else if (g_str_has_suffix (data, "-warning")) /* Translators: This is a title of a warning notification for a printer */ first_row = g_strdup (_("Printer warning")); else /* Translators: This is a title of an error notification for a printer */ first_row = g_strdup (_("Printer error")); if (text == NULL) text = g_strdup (data); /* Translators: "Printer 'MyPrinterName': 'Description of the report/warning/error from a PPD file'." */ second_row = g_strdup_printf (_("Printer '%s': '%s'."), printer_name, text); g_free (text); notification = notify_notification_new (first_row, second_row, "printer-symbolic"); notify_notification_set_app_name (notification, _("Printers")); notify_notification_set_hint (notification, "resident", g_variant_new_boolean (TRUE)); notify_notification_set_timeout (notification, REASON_TIMEOUT); reason_data = g_new0 (ReasonData, 1); reason_data->printer_name = g_strdup (printer_name); reason_data->reason = g_strdup (data); reason_data->notification = notification; reason_data->manager = manager; reason_data->notification_close_id = g_signal_connect (notification, "closed", G_CALLBACK (notification_closed_cb), reason_data); manager->priv->active_notifications = g_list_append (manager->priv->active_notifications, reason_data); notify_notification_show (notification, NULL); g_free (first_row); g_free (second_row); } } g_slist_free (added_reasons); } if (new_state_reasons) g_strfreev (new_state_reasons); if (old_state_reasons) g_strfreev (old_state_reasons); } if (primary_text) { NotifyNotification *notification; notification = notify_notification_new (primary_text, secondary_text, "printer-symbolic"); notify_notification_set_app_name (notification, _("Printers")); notify_notification_set_hint (notification, "transient", g_variant_new_boolean (TRUE)); notify_notification_show (notification, NULL); g_object_unref (notification); g_free (primary_text); g_free (secondary_text); } } static gboolean process_new_notifications (gpointer user_data) { GsdPrintNotificationsManager *manager = (GsdPrintNotificationsManager *) user_data; ipp_attribute_t *attr; const gchar *notify_subscribed_event = NULL; const gchar *printer_name = NULL; const gchar *notify_text = NULL; const gchar *notify_printer_uri = NULL; const gchar *job_state_reasons = NULL; const gchar *job_name = NULL; const char *attr_name; gboolean printer_is_accepting_jobs = FALSE; gchar *printer_state_reasons = NULL; gchar **reasons; guint notify_job_id = 0; ipp_t *request; ipp_t *response; gint printer_state = -1; gint job_state = -1; gint job_impressions_completed = -1; gint notify_sequence_number = -1; gint i; request = ippNewRequest (IPP_GET_NOTIFICATIONS); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser ()); ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-subscription-ids", manager->priv->subscription_id); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "/printers/"); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, "/jobs/"); ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-sequence-numbers", manager->priv->last_notify_sequence_number + 1); response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/"); for (attr = ippFindAttribute (response, "notify-sequence-number", IPP_TAG_INTEGER); attr != NULL; attr = ippNextAttribute (response)) { attr_name = ippGetName (attr); if (g_strcmp0 (attr_name, "notify-sequence-number") == 0) { notify_sequence_number = ippGetInteger (attr, 0); if (notify_sequence_number > manager->priv->last_notify_sequence_number) manager->priv->last_notify_sequence_number = notify_sequence_number; if (notify_subscribed_event != NULL) { process_cups_notification (manager, notify_subscribed_event, notify_text, notify_printer_uri, printer_name, printer_state, printer_state_reasons, printer_is_accepting_jobs, notify_job_id, job_state, job_state_reasons, job_name, job_impressions_completed); g_clear_pointer (&printer_state_reasons, g_free); g_clear_pointer (&job_state_reasons, g_free); } notify_subscribed_event = NULL; notify_text = NULL; notify_printer_uri = NULL; printer_name = NULL; printer_state = -1; printer_state_reasons = NULL; printer_is_accepting_jobs = FALSE; notify_job_id = 0; job_state = -1; job_state_reasons = NULL; job_name = NULL; job_impressions_completed = -1; } else if (g_strcmp0 (attr_name, "notify-subscribed-event") == 0) { notify_subscribed_event = ippGetString (attr, 0, NULL); } else if (g_strcmp0 (attr_name, "notify-text") == 0) { notify_text = ippGetString (attr, 0, NULL); } else if (g_strcmp0 (attr_name, "notify-printer-uri") == 0) { notify_printer_uri = ippGetString (attr, 0, NULL); } else if (g_strcmp0 (attr_name, "printer-name") == 0) { printer_name = ippGetString (attr, 0, NULL); } else if (g_strcmp0 (attr_name, "printer-state") == 0) { printer_state = ippGetInteger (attr, 0); } else if (g_strcmp0 (attr_name, "printer-state-reasons") == 0) { reasons = g_new0 (gchar *, ippGetCount (attr) + 1); for (i = 0; i < ippGetCount (attr); i++) reasons[i] = g_strdup (ippGetString (attr, i, NULL)); printer_state_reasons = g_strjoinv (",", reasons); g_strfreev (reasons); } else if (g_strcmp0 (attr_name, "printer-is-accepting-jobs") == 0) { printer_is_accepting_jobs = ippGetBoolean (attr, 0); } else if (g_strcmp0 (attr_name, "notify-job-id") == 0) { notify_job_id = ippGetInteger (attr, 0); } else if (g_strcmp0 (attr_name, "job-state") == 0) { job_state = ippGetInteger (attr, 0); } else if (g_strcmp0 (attr_name, "job-state-reasons") == 0) { reasons = g_new0 (gchar *, ippGetCount (attr) + 1); for (i = 0; i < ippGetCount (attr); i++) reasons[i] = g_strdup (ippGetString (attr, i, NULL)); job_state_reasons = g_strjoinv (",", reasons); g_strfreev (reasons); } else if (g_strcmp0 (attr_name, "job-name") == 0) { job_name = ippGetString (attr, 0, NULL); } else if (g_strcmp0 (attr_name, "job-impressions-completed") == 0) { job_impressions_completed = ippGetInteger (attr, 0); } } if (notify_subscribed_event != NULL) { process_cups_notification (manager, notify_subscribed_event, notify_text, notify_printer_uri, printer_name, printer_state, printer_state_reasons, printer_is_accepting_jobs, notify_job_id, job_state, job_state_reasons, job_name, job_impressions_completed); g_clear_pointer (&printer_state_reasons, g_free); g_clear_pointer (&job_state_reasons, g_free); } if (response != NULL) ippDelete (response); return TRUE; } static void scp_handler (GsdPrintNotificationsManager *manager, gboolean start) { if (start) { GError *error = NULL; char *args[2]; if (manager->priv->scp_handler_spawned) return; args[0] = LIBEXECDIR "/usd-printer"; args[1] = NULL; g_spawn_async (NULL, args, NULL, 0, NULL, NULL, &manager->priv->scp_handler_pid, &error); manager->priv->scp_handler_spawned = (error == NULL); if (error) { g_warning ("Could not execute system-config-printer-udev handler: %s", error->message); g_error_free (error); } } else if (manager->priv->scp_handler_spawned) { kill (manager->priv->scp_handler_pid, SIGHUP); g_spawn_close_pid (manager->priv->scp_handler_pid); manager->priv->scp_handler_spawned = FALSE; } } static void cancel_subscription (gint id) { http_t *http; ipp_t *request; if (id >= 0 && ((http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ())) != NULL)) { request = ippNewRequest (IPP_CANCEL_SUBSCRIPTION); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "/"); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser ()); ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-subscription-id", id); ippDelete (cupsDoRequest (http, request, "/")); } } static gboolean renew_subscription (gpointer data) { GsdPrintNotificationsManager *manager = (GsdPrintNotificationsManager *) data; ipp_attribute_t *attr = NULL; http_t *http; ipp_t *request; ipp_t *response; gint num_events = 7; static const char * const events[] = { "job-created", "job-completed", "job-state-changed", "job-state", "printer-added", "printer-deleted", "printer-state-changed"}; if ((http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ())) == NULL) { g_debug ("Connection to CUPS server \'%s\' failed.", cupsServer ()); } else { if (manager->priv->subscription_id >= 0) { request = ippNewRequest (IPP_RENEW_SUBSCRIPTION); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "/"); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser ()); ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-subscription-id", manager->priv->subscription_id); ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-lease-duration", SUBSCRIPTION_DURATION); ippDelete (cupsDoRequest (http, request, "/")); } else { request = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "/"); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser ()); ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events", num_events, NULL, events); ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-pull-method", NULL, "ippget"); if (server_is_local (cupsServer ())) { ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, "notify-recipient-uri", NULL, "dbus://"); } ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-lease-duration", SUBSCRIPTION_DURATION); response = cupsDoRequest (http, request, "/"); if (response != NULL && ippGetStatusCode (response) <= IPP_OK_CONFLICT) { if ((attr = ippFindAttribute (response, "notify-subscription-id", IPP_TAG_INTEGER)) == NULL) g_debug ("No notify-subscription-id in response!\n"); else manager->priv->subscription_id = ippGetInteger (attr, 0); } if (response) ippDelete (response); } httpClose (http); } return TRUE; } static void renew_subscription_with_connection_test_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GSocketConnection *connection; GError *error = NULL; connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object), res, &error); if (connection) { g_debug ("Test connection to CUPS server \'%s:%d\' succeeded.", cupsServer (), ippPort ()); g_io_stream_close (G_IO_STREAM (connection), NULL, NULL); g_object_unref (connection); renew_subscription (user_data); } else { g_debug ("Test connection to CUPS server \'%s:%d\' failed.", cupsServer (), ippPort ()); } } static gboolean renew_subscription_with_connection_test (gpointer user_data) { GSocketClient *client; gchar *address; int port; port = ippPort (); address = g_strdup_printf ("%s:%d", cupsServer (), port); if (address && address[0] != '/') { client = g_socket_client_new (); g_debug ("Initiating test connection to CUPS server \'%s:%d\'.", cupsServer (), port); g_socket_client_connect_to_host_async (client, address, port, NULL, renew_subscription_with_connection_test_cb, user_data); g_object_unref (client); } else { renew_subscription (user_data); } g_free (address); return TRUE; } static void renew_subscription_timeout_enable (GsdPrintNotificationsManager *manager, gboolean enable, gboolean with_connection_test) { if (manager->priv->renew_source_id > 0) g_source_remove (manager->priv->renew_source_id); if (enable) { renew_subscription (manager); if (with_connection_test) manager->priv->renew_source_id = g_timeout_add_seconds (RENEW_INTERVAL, renew_subscription_with_connection_test, manager); else manager->priv->renew_source_id = g_timeout_add_seconds (RENEW_INTERVAL, renew_subscription, manager); } else { manager->priv->renew_source_id = 0; } } static void cups_connection_test_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GsdPrintNotificationsManager *manager = (GsdPrintNotificationsManager *) user_data; GSocketConnection *connection; GError *error = NULL; connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source_object), res, &error); if (connection) { g_debug ("Test connection to CUPS server \'%s:%d\' succeeded.", cupsServer (), ippPort ()); g_io_stream_close (G_IO_STREAM (connection), NULL, NULL); g_object_unref (connection); manager->priv->num_dests = cupsGetDests (&manager->priv->dests); g_debug ("Got dests from remote CUPS server."); renew_subscription_timeout_enable (manager, TRUE, TRUE); manager->priv->check_source_id = g_timeout_add_seconds (CHECK_INTERVAL, process_new_notifications, manager); } else { g_debug ("Test connection to CUPS server \'%s:%d\' failed.", cupsServer (), ippPort ()); if (manager->priv->cups_connection_timeout_id == 0) manager->priv->cups_connection_timeout_id = g_timeout_add_seconds (CUPS_CONNECTION_TEST_INTERVAL, cups_connection_test, manager); } } static gboolean cups_connection_test (gpointer user_data) { GsdPrintNotificationsManager *manager = (GsdPrintNotificationsManager *) user_data; GSocketClient *client; gchar *address; int port = ippPort (); if (!manager->priv->dests) { address = g_strdup_printf ("%s:%d", cupsServer (), port); client = g_socket_client_new (); g_debug ("Initiating test connection to CUPS server \'%s:%d\'.", cupsServer (), port); g_socket_client_connect_to_host_async (client, address, port, NULL, cups_connection_test_cb, manager); g_object_unref (client); g_free (address); } if (manager->priv->dests) { manager->priv->cups_connection_timeout_id = 0; return FALSE; } else { return TRUE; } } static void gsd_print_notifications_manager_got_dbus_connection (GObject *source_object, GAsyncResult *res, gpointer user_data) { GsdPrintNotificationsManager *manager = (GsdPrintNotificationsManager *) user_data; GError *error = NULL; manager->priv->cups_bus_connection = g_bus_get_finish (res, &error); if (manager->priv->cups_bus_connection != NULL) { manager->priv->cups_dbus_subscription_id = g_dbus_connection_signal_subscribe (manager->priv->cups_bus_connection, NULL, CUPS_DBUS_INTERFACE, NULL, CUPS_DBUS_PATH, NULL, 0, on_cups_notification, manager, NULL); } else { g_warning ("Connection to message bus failed: %s", error->message); g_error_free (error); } } static gboolean gsd_print_notifications_manager_start_idle (gpointer data) { GsdPrintNotificationsManager *manager = data; gnome_settings_profile_start (NULL); manager->priv->printing_printers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); if (server_is_local (cupsServer ())) { manager->priv->num_dests = cupsGetDests (&manager->priv->dests); g_debug ("Got dests from local CUPS server."); renew_subscription_timeout_enable (manager, TRUE, FALSE); g_bus_get (G_BUS_TYPE_SYSTEM, NULL, gsd_print_notifications_manager_got_dbus_connection, data); } else { cups_connection_test (manager); } scp_handler (manager, TRUE); gnome_settings_profile_end (NULL); return G_SOURCE_REMOVE; } gboolean gsd_print_notifications_manager_start (GsdPrintNotificationsManager *manager, GError **error) { g_debug ("Starting print-notifications manager"); gnome_settings_profile_start (NULL); manager->priv->subscription_id = -1; manager->priv->dests = NULL; manager->priv->num_dests = 0; manager->priv->scp_handler_spawned = FALSE; manager->priv->timeouts = NULL; manager->priv->printing_printers = NULL; manager->priv->active_notifications = NULL; manager->priv->cups_bus_connection = NULL; manager->priv->cups_connection_timeout_id = 0; manager->priv->last_notify_sequence_number = -1; g_idle_add (gsd_print_notifications_manager_start_idle, manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_print_notifications_manager_stop (GsdPrintNotificationsManager *manager) { TimeoutData *data; ReasonData *reason_data; GList *tmp; g_debug ("Stopping print-notifications manager"); cupsFreeDests (manager->priv->num_dests, manager->priv->dests); manager->priv->num_dests = 0; manager->priv->dests = NULL; if (manager->priv->cups_dbus_subscription_id > 0 && manager->priv->cups_bus_connection != NULL) { g_dbus_connection_signal_unsubscribe (manager->priv->cups_bus_connection, manager->priv->cups_dbus_subscription_id); manager->priv->cups_dbus_subscription_id = 0; } renew_subscription_timeout_enable (manager, FALSE, FALSE); if (manager->priv->check_source_id > 0) { g_source_remove (manager->priv->check_source_id); manager->priv->check_source_id = 0; } if (manager->priv->subscription_id >= 0) cancel_subscription (manager->priv->subscription_id); if (manager->priv->printing_printers) g_hash_table_destroy (manager->priv->printing_printers); g_clear_object (&manager->priv->cups_bus_connection); for (tmp = manager->priv->timeouts; tmp; tmp = g_list_next (tmp)) { data = (TimeoutData *) tmp->data; if (data) g_source_remove (data->timeout_id); } g_list_free_full (manager->priv->timeouts, free_timeout_data); for (tmp = manager->priv->active_notifications; tmp; tmp = g_list_next (tmp)) { reason_data = (ReasonData *) tmp->data; if (reason_data) { if (reason_data->notification_close_id > 0 && g_signal_handler_is_connected (reason_data->notification, reason_data->notification_close_id)) { g_signal_handler_disconnect (reason_data->notification, reason_data->notification_close_id); reason_data->notification_close_id = 0; } notify_notification_close (reason_data->notification, NULL); } } g_list_free_full (manager->priv->active_notifications, free_reason_data); scp_handler (manager, FALSE); } static GObject * gsd_print_notifications_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdPrintNotificationsManager *print_notifications_manager; print_notifications_manager = GSD_PRINT_NOTIFICATIONS_MANAGER (G_OBJECT_CLASS (gsd_print_notifications_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (print_notifications_manager); } static void gsd_print_notifications_manager_class_init (GsdPrintNotificationsManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_print_notifications_manager_constructor; object_class->finalize = gsd_print_notifications_manager_finalize; g_type_class_add_private (klass, sizeof (GsdPrintNotificationsManagerPrivate)); } static void gsd_print_notifications_manager_init (GsdPrintNotificationsManager *manager) { manager->priv = GSD_PRINT_NOTIFICATIONS_MANAGER_GET_PRIVATE (manager); } static void gsd_print_notifications_manager_finalize (GObject *object) { GsdPrintNotificationsManager *manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_PRINT_NOTIFICATIONS_MANAGER (object)); manager = GSD_PRINT_NOTIFICATIONS_MANAGER (object); g_return_if_fail (manager->priv != NULL); G_OBJECT_CLASS (gsd_print_notifications_manager_parent_class)->finalize (object); } GsdPrintNotificationsManager * gsd_print_notifications_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_PRINT_NOTIFICATIONS_MANAGER (manager_object); } ./plugins/print-notifications/gsd-print-notifications-manager.h0000644000004100000410000000521712735467744025316 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_PRINT_NOTIFICATIONS_MANAGER_H #define __GSD_PRINT_NOTIFICATIONS_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER (gsd_print_notifications_manager_get_type ()) #define GSD_PRINT_NOTIFICATIONS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER, GsdPrintNotificationsManager)) #define GSD_PRINT_NOTIFICATIONS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER, GsdPrintNotificationsManagerClass)) #define GSD_IS_PRINT_NOTIFICATIONS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER)) #define GSD_IS_PRINT_NOTIFICATIONS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER)) #define GSD_PRINT_NOTIFICATIONS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_PRINT_NOTIFICATIONS_MANAGER, GsdPrintNotificationsManagerClass)) typedef struct GsdPrintNotificationsManagerPrivate GsdPrintNotificationsManagerPrivate; typedef struct { GObject parent; GsdPrintNotificationsManagerPrivate *priv; } GsdPrintNotificationsManager; typedef struct { GObjectClass parent_class; } GsdPrintNotificationsManagerClass; GType gsd_print_notifications_manager_get_type (void); GsdPrintNotificationsManager *gsd_print_notifications_manager_new (void); gboolean gsd_print_notifications_manager_start (GsdPrintNotificationsManager *manager, GError **error); void gsd_print_notifications_manager_stop (GsdPrintNotificationsManager *manager); G_END_DECLS #endif /* __GSD_PRINT_NOTIFICATIONS_MANAGER_H */ ./plugins/print-notifications/print-notifications.gnome-settings-plugin.in0000644000004100000410000000036712735467744027551 0ustar www-datawww-data[GNOME Settings Plugin] Module=print-notifications IAge=0 # Default Priority # Priority=100 _Name=Print-notifications _Description=Print-notifications plugin Authors=Marek Kasik Copyright=Copyright © 2011 Marek Kasik Website= ./plugins/print-notifications/gsd-printer.c0000644000004100000410000014745112735467744021370 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * 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. * */ #include "config.h" #include #include #include #include #include #include #include #include static GDBusNodeInfo *npn_introspection_data = NULL; static GDBusNodeInfo *pdi_introspection_data = NULL; #define SCP_DBUS_NPN_NAME "com.redhat.NewPrinterNotification" #define SCP_DBUS_NPN_PATH "/com/redhat/NewPrinterNotification" #define SCP_DBUS_NPN_INTERFACE "com.redhat.NewPrinterNotification" #define SCP_DBUS_PDI_NAME "com.redhat.PrinterDriversInstaller" #define SCP_DBUS_PDI_PATH "/com/redhat/PrinterDriversInstaller" #define SCP_DBUS_PDI_INTERFACE "com.redhat.PrinterDriversInstaller" #define PACKAGE_KIT_BUS "org.freedesktop.PackageKit" #define PACKAGE_KIT_PATH "/org/freedesktop/PackageKit" #define PACKAGE_KIT_MODIFY_IFACE "org.freedesktop.PackageKit.Modify" #define PACKAGE_KIT_QUERY_IFACE "org.freedesktop.PackageKit.Query" #define SCP_BUS "org.fedoraproject.Config.Printing" #define SCP_PATH "/org/fedoraproject/Config/Printing" #define SCP_IFACE "org.fedoraproject.Config.Printing" #define MECHANISM_BUS "org.opensuse.CupsPkHelper.Mechanism" #define ALLOWED_CHARACTERS "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" #define DBUS_TIMEOUT 60000 #define DBUS_INSTALL_TIMEOUT 3600000 #define GNOME_SESSION_DBUS_NAME "org.gnome.SessionManager" #define GNOME_SESSION_DBUS_PATH "/org/gnome/SessionManager" #define GNOME_SESSION_DBUS_IFACE "org.gnome.SessionManager" #define GNOME_SESSION_CLIENT_PRIVATE_DBUS_IFACE "org.gnome.SessionManager.ClientPrivate" #define GNOME_SESSION_PRESENCE_DBUS_PATH "/org/gnome/SessionManager/Presence" #define GNOME_SESSION_PRESENCE_DBUS_IFACE "org.gnome.SessionManager.Presence" #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) #define HAVE_CUPS_1_6 1 #endif #ifndef HAVE_CUPS_1_6 #define ippGetState(ipp) ipp->state #endif enum { PRESENCE_STATUS_AVAILABLE = 0, PRESENCE_STATUS_INVISIBLE, PRESENCE_STATUS_BUSY, PRESENCE_STATUS_IDLE, PRESENCE_STATUS_UNKNOWN }; static const gchar npn_introspection_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " ""; static const gchar pdi_introspection_xml[] = "" " " " " " " " " " " " " " " ""; static GMainLoop *main_loop; static guint npn_registration_id; static guint pdi_registration_id; static guint npn_owner_id; static guint pdi_owner_id; static GHashTable * get_missing_executables (const gchar *ppd_file_name) { GHashTable *executables = NULL; GDBusProxy *proxy; GVariant *output; GVariant *array; GError *error = NULL; gint i; if (!ppd_file_name) return NULL; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, SCP_BUS, SCP_PATH, SCP_IFACE, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return NULL; } output = g_dbus_proxy_call_sync (proxy, "MissingExecutables", g_variant_new ("(s)", ppd_file_name), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &error); if (output && g_variant_n_children (output) == 1) { array = g_variant_get_child_value (output, 0); if (array) { executables = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (i = 0; i < g_variant_n_children (array); i++) { g_hash_table_insert (executables, g_strdup (g_variant_get_string ( g_variant_get_child_value (array, i), NULL)), NULL); } } } if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); return executables; } static GHashTable * find_packages_for_executables (GHashTable *executables) { GHashTableIter exec_iter; GHashTable *packages = NULL; GDBusProxy *proxy; GVariant *output; gpointer key, value; GError *error = NULL; if (!executables || g_hash_table_size (executables) <= 0) return NULL; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, PACKAGE_KIT_BUS, PACKAGE_KIT_PATH, PACKAGE_KIT_QUERY_IFACE, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return NULL; } packages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); g_hash_table_iter_init (&exec_iter, executables); while (g_hash_table_iter_next (&exec_iter, &key, &value)) { output = g_dbus_proxy_call_sync (proxy, "SearchFile", g_variant_new ("(ss)", (gchar *) key, ""), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &error); if (output) { gboolean installed; gchar *package; g_variant_get (output, "(bs)", &installed, &package); if (!installed) g_hash_table_insert (packages, g_strdup (package), NULL); g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } } g_object_unref (proxy); return packages; } static void install_packages (GHashTable *packages) { GVariantBuilder array_builder; GHashTableIter pkg_iter; GDBusProxy *proxy; GVariant *output; gpointer key, value; GError *error = NULL; if (!packages || g_hash_table_size (packages) <= 0) return; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, PACKAGE_KIT_BUS, PACKAGE_KIT_PATH, PACKAGE_KIT_MODIFY_IFACE, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return; } g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as")); g_hash_table_iter_init (&pkg_iter, packages); while (g_hash_table_iter_next (&pkg_iter, &key, &value)) { g_variant_builder_add (&array_builder, "s", (gchar *) key); } output = g_dbus_proxy_call_sync (proxy, "InstallPackageNames", g_variant_new ("(uass)", 0, &array_builder, "hide-finished"), G_DBUS_CALL_FLAGS_NONE, DBUS_INSTALL_TIMEOUT, NULL, &error); if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); } static gchar * get_best_ppd (gchar *device_id, gchar *device_make_and_model, gchar *device_uri) { GDBusProxy *proxy; GVariant *output; GVariant *array; GVariant *tuple; GError *error = NULL; gchar *ppd_name = NULL; gint i, j; static const char * const match_levels[] = { "exact-cmd", "exact", "close", "generic", "none"}; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, SCP_BUS, SCP_PATH, SCP_IFACE, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return NULL; } output = g_dbus_proxy_call_sync (proxy, "GetBestDrivers", g_variant_new ("(sss)", device_id ? device_id : "", device_make_and_model ? device_make_and_model : "", device_uri ? device_uri : ""), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &error); if (output && g_variant_n_children (output) >= 1) { array = g_variant_get_child_value (output, 0); if (array) for (j = 0; j < G_N_ELEMENTS (match_levels) && ppd_name == NULL; j++) for (i = 0; i < g_variant_n_children (array) && ppd_name == NULL; i++) { tuple = g_variant_get_child_value (array, i); if (tuple && g_variant_n_children (tuple) == 2) { if (g_strcmp0 (g_variant_get_string ( g_variant_get_child_value (tuple, 1), NULL), match_levels[j]) == 0) ppd_name = g_strdup (g_variant_get_string ( g_variant_get_child_value (tuple, 0), NULL)); } } } if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); return ppd_name; } static gchar * get_tag_value (const gchar *tag_string, const gchar *tag_name) { gchar **tag_string_splitted; gchar *tag_value = NULL; gint tag_name_length; gint i; if (!tag_string || !tag_name) return NULL; tag_name_length = strlen (tag_name); tag_string_splitted = g_strsplit (tag_string, ";", 0); if (tag_string_splitted) { for (i = 0; i < g_strv_length (tag_string_splitted); i++) if (g_ascii_strncasecmp (tag_string_splitted[i], tag_name, tag_name_length) == 0) if (strlen (tag_string_splitted[i]) > tag_name_length + 1) tag_value = g_strdup (tag_string_splitted[i] + tag_name_length + 1); g_strfreev (tag_string_splitted); } return tag_value; } static gchar * create_name (gchar *device_id) { cups_dest_t *dests; gboolean already_present = FALSE; gchar *name = NULL; gchar *new_name = NULL; gint num_dests; gint name_index = 2; gint j; g_return_val_if_fail (device_id != NULL, NULL); name = get_tag_value (device_id, "mdl"); if (!name) name = get_tag_value (device_id, "model"); if (name) name = g_strcanon (name, ALLOWED_CHARACTERS, '-'); num_dests = cupsGetDests (&dests); do { if (already_present) { new_name = g_strdup_printf ("%s-%d", name, name_index); name_index++; } else { new_name = g_strdup (name); } already_present = FALSE; for (j = 0; j < num_dests; j++) if (g_strcmp0 (dests[j].name, new_name) == 0) already_present = TRUE; if (already_present) { g_free (new_name); } else { g_free (name); name = new_name; } } while (already_present); cupsFreeDests (num_dests, dests); return name; } static gboolean add_printer (gchar *printer_name, gchar *device_uri, gchar *ppd_name, gchar *info, gchar *location) { cups_dest_t *dests; GDBusProxy *proxy; gboolean success = FALSE; GVariant *output; GError *error = NULL; gint num_dests; gint i; if (!printer_name || !device_uri || !ppd_name) return FALSE; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, MECHANISM_BUS, "/", MECHANISM_BUS, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return FALSE; } output = g_dbus_proxy_call_sync (proxy, "PrinterAdd", g_variant_new ("(sssss)", printer_name, device_uri, ppd_name, info ? info : "", location ? location : ""), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &error); if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); num_dests = cupsGetDests (&dests); for (i = 0; i < num_dests; i++) if (g_strcmp0 (dests[i].name, printer_name) == 0) success = TRUE; cupsFreeDests (num_dests, dests); return success; } static gboolean printer_set_enabled (const gchar *printer_name, gboolean enabled) { GDBusProxy *proxy; gboolean result = TRUE; GVariant *output; GError *error = NULL; if (!printer_name) return FALSE; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, MECHANISM_BUS, "/", MECHANISM_BUS, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return FALSE; } output = g_dbus_proxy_call_sync (proxy, "PrinterSetEnabled", g_variant_new ("(sb)", printer_name, enabled), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &error); if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); result = FALSE; } g_object_unref (proxy); return result; } static gboolean printer_set_accepting_jobs (const gchar *printer_name, gboolean accepting_jobs, const gchar *reason) { GDBusProxy *proxy; gboolean result = TRUE; GVariant *output; GError *error = NULL; if (!printer_name) return FALSE; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, MECHANISM_BUS, "/", MECHANISM_BUS, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return FALSE; } output = g_dbus_proxy_call_sync (proxy, "PrinterSetAcceptJobs", g_variant_new ("(sbs)", printer_name, accepting_jobs, reason ? reason : ""), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &error); if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); result = FALSE; } g_object_unref (proxy); return result; } static ipp_t * execute_maintenance_command (const char *printer_name, const char *command, const char *title) { http_t *http; GError *error = NULL; ipp_t *request = NULL; ipp_t *response = NULL; gchar *file_name = NULL; char *uri; int fd = -1; http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ()); if (!http) return NULL; request = ippNewRequest (IPP_PRINT_JOB); uri = g_strdup_printf ("ipp://localhost/printers/%s", printer_name); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); g_free (uri); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title); ippAddString (request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format", NULL, "application/vnd.cups-command"); fd = g_file_open_tmp ("ccXXXXXX", &file_name, &error); if (fd != -1) { FILE *file; file = fdopen (fd, "w"); fprintf (file, "#CUPS-COMMAND\n"); fprintf (file, "%s\n", command); fclose (file); response = cupsDoFileRequest (http, request, "/", file_name); g_unlink (file_name); } else { g_warning ("%s", error->message); g_error_free (error); } g_free (file_name); httpClose (http); return response; } static char * get_dest_attr (const char *dest_name, const char *attr) { cups_dest_t *dests; int num_dests; cups_dest_t *dest; const char *value; char *ret; if (dest_name == NULL) return NULL; ret = NULL; num_dests = cupsGetDests (&dests); if (num_dests < 1) { g_debug ("Unable to get printer destinations"); return NULL; } dest = cupsGetDest (dest_name, NULL, num_dests, dests); if (dest == NULL) { g_debug ("Unable to find a printer named '%s'", dest_name); goto out; } value = cupsGetOption (attr, dest->num_options, dest->options); if (value == NULL) { g_debug ("Unable to get %s for '%s'", attr, dest_name); goto out; } ret = g_strdup (value); out: cupsFreeDests (num_dests, dests); return ret; } static void printer_autoconfigure (gchar *printer_name) { gchar *commands; gchar *commands_lowercase; ipp_t *response = NULL; if (!printer_name) return; commands = get_dest_attr (printer_name, "printer-commands"); commands_lowercase = g_ascii_strdown (commands, -1); if (g_strrstr (commands_lowercase, "autoconfigure")) { response = execute_maintenance_command (printer_name, "AutoConfigure", ("Automatic configuration")); if (response) { if (ippGetState (response) == IPP_ERROR) g_warning ("An error has occured during automatic configuration of new printer."); ippDelete (response); } } g_free (commands); g_free (commands_lowercase); } /* Return default media size for current locale */ static const gchar * get_paper_size_from_locale () { if (g_str_equal (gtk_paper_size_get_default (), GTK_PAPER_NAME_LETTER)) return "na-letter"; else return "iso-a4"; } static void set_default_paper_size (const gchar *printer_name, const gchar *ppd_file_name) { GDBusProxy *proxy; GVariant *output; GError *error = NULL; GVariantBuilder *builder; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, MECHANISM_BUS, "/", MECHANISM_BUS, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); return; } /* Set default media size according to the locale * FIXME: Handle more than A4 and Letter: * https://bugzilla.gnome.org/show_bug.cgi?id=660769 */ builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); g_variant_builder_add (builder, "s", get_paper_size_from_locale ()); output = g_dbus_proxy_call_sync (proxy, "PrinterAddOption", g_variant_new ("(ssas)", printer_name ? printer_name : "", "media", builder), G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &error); if (output) { g_variant_unref (output); } else { if (!(error->domain == G_DBUS_ERROR && (error->code == G_DBUS_ERROR_SERVICE_UNKNOWN || error->code == G_DBUS_ERROR_UNKNOWN_METHOD))) g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); } /* * Setup new printer and returns TRUE if successful. */ static gboolean setup_printer (gchar *device_id, gchar *device_make_and_model, gchar *device_uri) { gboolean success = FALSE; gchar *ppd_name; gchar *printer_name; ppd_name = get_best_ppd (device_id, device_make_and_model, device_uri); printer_name = create_name (device_id); if (!ppd_name || !printer_name || !device_uri) { g_free (ppd_name); g_free (printer_name); return FALSE; } success = add_printer (printer_name, device_uri, ppd_name, NULL, NULL); /* Set some options of the new printer */ if (success) { const char *ppd_file_name; printer_set_accepting_jobs (printer_name, TRUE, NULL); printer_set_enabled (printer_name, TRUE); printer_autoconfigure (printer_name); ppd_file_name = cupsGetPPD (printer_name); if (ppd_file_name) { GHashTable *executables; GHashTable *packages; set_default_paper_size (printer_name, ppd_file_name); executables = get_missing_executables (ppd_file_name); packages = find_packages_for_executables (executables); install_packages (packages); if (executables) g_hash_table_destroy (executables); if (packages) g_hash_table_destroy (packages); g_unlink (ppd_file_name); } } g_free (printer_name); g_free (ppd_name); return success; } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { gchar *primary_text = NULL; gchar *secondary_text = NULL; gchar *name = NULL; gchar *mfg = NULL; gchar *mdl = NULL; gchar *des = NULL; gchar *cmd = NULL; gchar *device = NULL; gchar *device_id; gchar *make_and_model; gint status = 0; if (g_strcmp0 (method_name, "GetReady") == 0) { /* Translators: We are configuring new printer */ primary_text = g_strdup (_("Configuring new printer")); /* Translators: Just wait */ secondary_text = g_strdup (_("Please wait...")); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "NewPrinter") == 0) { if (g_variant_n_children (parameters) == 6) { g_variant_get (parameters, "(i&s&s&s&s&s)", &status, &name, &mfg, &mdl, &des, &cmd); } if (g_strrstr (name, "/")) { /* name is a URI, no queue was generated, because no suitable * driver was found */ device_id = g_strdup_printf ("MFG:%s;MDL:%s;DES:%s;CMD:%s;", mfg, mdl, des, cmd); make_and_model = g_strdup_printf ("%s %s", mfg, mdl); if (!setup_printer (device_id, make_and_model, name)) { /* Translators: We have no driver installed for this printer */ primary_text = g_strdup (_("Missing printer driver")); if ((mfg && mdl) || des) { if (mfg && mdl) device = g_strdup_printf ("%s %s", mfg, mdl); else device = g_strdup (des); /* Translators: We have no driver installed for the device */ secondary_text = g_strdup_printf (_("No printer driver for %s."), device); g_free (device); } else /* Translators: We have no driver installed for this printer */ secondary_text = g_strdup (_("No driver for this printer.")); } g_free (make_and_model); g_free (device_id); } else { /* name is the name of the queue which hal_lpadmin has set up * automatically. */ const char *ppd_file_name; ppd_file_name = cupsGetPPD (name); if (ppd_file_name) { GHashTable *executables; GHashTable *packages; executables = get_missing_executables (ppd_file_name); packages = find_packages_for_executables (executables); install_packages (packages); if (executables) g_hash_table_destroy (executables); if (packages) g_hash_table_destroy (packages); g_unlink (ppd_file_name); } } g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "InstallDrivers") == 0) { GDBusProxy *proxy; GError *error = NULL; if (g_variant_n_children (parameters) == 3) { g_variant_get (parameters, "(&s&s&s)", &mfg, &mdl, &cmd); } if (mfg && mdl) device = g_strdup_printf ("MFG:%s;MDL:%s;", mfg, mdl); proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, PACKAGE_KIT_BUS, PACKAGE_KIT_PATH, PACKAGE_KIT_MODIFY_IFACE, NULL, &error); if (!proxy) { g_warning ("%s", error->message); g_error_free (error); } if (proxy && device) { GVariantBuilder *builder; GVariant *output; builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); g_variant_builder_add (builder, "s", device); output = g_dbus_proxy_call_sync (proxy, "InstallPrinterDrivers", g_variant_new ("(uass)", 0, builder, "hide-finished"), G_DBUS_CALL_FLAGS_NONE, DBUS_INSTALL_TIMEOUT, NULL, &error); if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); } g_dbus_method_invocation_return_value (invocation, NULL); } if (primary_text) { NotifyNotification *notification; notification = notify_notification_new (primary_text, secondary_text, "printer-symbolic"); notify_notification_set_app_name (notification, _("Printers")); notify_notification_set_hint (notification, "transient", g_variant_new_boolean (TRUE)); notify_notification_show (notification, NULL); g_object_unref (notification); g_free (primary_text); g_free (secondary_text); } } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, NULL }; static void unregister_objects () { GDBusConnection *system_connection; GError *error = NULL; system_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (npn_registration_id > 0) { g_dbus_connection_unregister_object (system_connection, npn_registration_id); npn_registration_id = 0; } if (pdi_registration_id > 0) { g_dbus_connection_unregister_object (system_connection, pdi_registration_id); pdi_registration_id = 0; } } static void unown_names () { if (npn_owner_id > 0) { g_bus_unown_name (npn_owner_id); npn_owner_id = 0; } if (pdi_owner_id > 0) { g_bus_unown_name (pdi_owner_id); pdi_owner_id = 0; } } static void on_npn_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { GError *error = NULL; npn_registration_id = g_dbus_connection_register_object (connection, SCP_DBUS_NPN_PATH, npn_introspection_data->interfaces[0], &interface_vtable, NULL, NULL, &error); if (npn_registration_id == 0) { g_warning ("Failed to register object: %s\n", error->message); g_error_free (error); } } static void on_pdi_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { GError *error = NULL; pdi_registration_id = g_dbus_connection_register_object (connection, SCP_DBUS_PDI_PATH, pdi_introspection_data->interfaces[0], &interface_vtable, NULL, NULL, &error); if (pdi_registration_id == 0) { g_warning ("Failed to register object: %s\n", error->message); g_error_free (error); } } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { unregister_objects (); } static void session_signal_handler (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { guint new_status; g_variant_get (parameters, "(u)", &new_status); if (new_status == PRESENCE_STATUS_IDLE || new_status == PRESENCE_STATUS_AVAILABLE) { unregister_objects (); unown_names (); if (new_status == PRESENCE_STATUS_AVAILABLE) { npn_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, SCP_DBUS_NPN_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE, on_npn_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); pdi_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, SCP_DBUS_PDI_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE, on_pdi_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); } } } static void client_signal_handler (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { GDBusProxy *proxy; GError *error = NULL; GVariant *output; if (g_strcmp0 (signal_name, "QueryEndSession") == 0 || g_strcmp0 (signal_name, "EndSession") == 0) { proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, sender_name, object_path, interface_name, NULL, &error); if (proxy) { output = g_dbus_proxy_call_sync (proxy, "EndSessionResponse", g_variant_new ("(bs)", TRUE, ""), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (output) { g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); } else { g_warning ("%s", error->message); g_error_free (error); } if (g_strcmp0 (signal_name, "EndSession") == 0) { g_main_loop_quit (main_loop); g_debug ("Exiting usd-printer"); } } } static gchar * register_gnome_session_client (const gchar *app_id, const gchar *client_startup_id) { GDBusProxy *proxy; GVariant *output = NULL; GError *error = NULL; const gchar *client_id = NULL; gchar *result = NULL; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, NULL, GNOME_SESSION_DBUS_NAME, GNOME_SESSION_DBUS_PATH, GNOME_SESSION_DBUS_IFACE, NULL, &error); if (proxy) { output = g_dbus_proxy_call_sync (proxy, "RegisterClient", g_variant_new ("(ss)", app_id, client_startup_id), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (output) { g_variant_get (output, "(o)", &client_id); if (client_id) result = g_strdup (client_id); g_variant_unref (output); } else { g_warning ("%s", error->message); g_error_free (error); } g_object_unref (proxy); } else { g_warning ("%s", error->message); g_error_free (error); } return result; } int main (int argc, char *argv[]) { GDBusConnection *connection; gboolean client_signal_subscription_set = FALSE; GError *error = NULL; guint client_signal_subscription_id; guint session_signal_subscription_id; gchar *object_path; bindtextdomain (GETTEXT_PACKAGE, GNOME_SETTINGS_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); setlocale (LC_ALL, ""); npn_registration_id = 0; pdi_registration_id = 0; npn_owner_id = 0; pdi_owner_id = 0; notify_init ("gnome-settings-daemon-printer"); npn_introspection_data = g_dbus_node_info_new_for_xml (npn_introspection_xml, &error); if (npn_introspection_data == NULL) { g_warning ("Error parsing introspection XML: %s\n", error->message); g_error_free (error); goto error; } pdi_introspection_data = g_dbus_node_info_new_for_xml (pdi_introspection_xml, &error); if (pdi_introspection_data == NULL) { g_warning ("Error parsing introspection XML: %s\n", error->message); g_error_free (error); goto error; } connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); session_signal_subscription_id = g_dbus_connection_signal_subscribe (connection, NULL, GNOME_SESSION_PRESENCE_DBUS_IFACE, "StatusChanged", GNOME_SESSION_PRESENCE_DBUS_PATH, NULL, G_DBUS_SIGNAL_FLAGS_NONE, session_signal_handler, NULL, NULL); object_path = register_gnome_session_client ("usd-printer", ""); if (object_path) { client_signal_subscription_id = g_dbus_connection_signal_subscribe (connection, NULL, GNOME_SESSION_CLIENT_PRIVATE_DBUS_IFACE, NULL, object_path, NULL, G_DBUS_SIGNAL_FLAGS_NONE, client_signal_handler, NULL, NULL); client_signal_subscription_set = TRUE; } if (npn_owner_id == 0) npn_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, SCP_DBUS_NPN_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE, on_npn_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); if (pdi_owner_id == 0) pdi_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, SCP_DBUS_PDI_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE, on_pdi_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); unregister_objects (); unown_names (); if (client_signal_subscription_set) g_dbus_connection_signal_unsubscribe (connection, client_signal_subscription_id); g_dbus_connection_signal_unsubscribe (connection, session_signal_subscription_id); g_free (object_path); g_dbus_node_info_unref (npn_introspection_data); g_dbus_node_info_unref (pdi_introspection_data); return 0; error: if (npn_introspection_data) g_dbus_node_info_unref (npn_introspection_data); if (pdi_introspection_data) g_dbus_node_info_unref (pdi_introspection_data); return 1; } ./plugins/print-notifications/gsd-print-notifications-plugin.c0000644000004100000410000000204012735467744025164 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-print-notifications-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdPrintNotifications, gsd_print_notifications) ./plugins/cursor/0000755000004100000410000000000012735467744014264 5ustar www-datawww-data./plugins/cursor/test-cursor.c0000644000004100000410000000031212735467744016716 0ustar www-datawww-data#define NEW gsd_cursor_manager_new #define START gsd_cursor_manager_start #define STOP gsd_cursor_manager_stop #define MANAGER GsdCursorManager #include "gsd-cursor-manager.h" #include "test-plugin.h" ./plugins/cursor/gsd-cursor-plugin.c0000644000004100000410000000202012735467744020006 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-cursor-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdCursor, gsd_cursor) ./plugins/cursor/Makefile.am0000644000004100000410000000332312735467744016321 0ustar www-datawww-dataplugin_name = cursor plugin_LTLIBRARIES = \ libcursor.la libcursor_la_SOURCES = \ gsd-cursor-manager.c \ gsd-cursor-manager.h \ gsd-cursor-plugin.c libcursor_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common/ \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libcursor_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(CURSOR_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) libcursor_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libcursor_la_LIBADD = \ $(top_builddir)/plugins/common/libcommon.la \ $(top_builddir)/gnome-settings-daemon/libunity-settings-daemon.la \ $(CURSOR_LIBS) \ $(SETTINGS_PLUGIN_LIBS) libexec_PROGRAMS = usd-test-cursor usd_test_cursor_SOURCES = \ test-cursor.c \ gsd-cursor-manager.c \ gsd-cursor-manager.h usd_test_cursor_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) usd_test_cursor_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(CURSOR_CFLAGS) \ $(AM_CFLAGS) usd_test_cursor_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(top_builddir)/gnome-settings-daemon/libunity-settings-daemon.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ $(CURSOR_LIBS) \ -lm plugin_in_files = \ cursor.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/cursor/gsd-cursor-manager.c0000644000004100000410000003246112735467744020136 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2011-2013 Bastien Nocera * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-profile.h" #include "gsd-cursor-manager.h" #include "gsd-input-helper.h" #include "gsd-idle-monitor.h" #define XFIXES_CURSOR_HIDING_MAJOR 4 #define GSD_CURSOR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_CURSOR_MANAGER, GsdCursorManagerPrivate)) struct GsdCursorManagerPrivate { guint added_id; guint removed_id; guint changed_id; gboolean cursor_shown; GHashTable *monitors; }; static void gsd_cursor_manager_class_init (GsdCursorManagerClass *klass); static void gsd_cursor_manager_init (GsdCursorManager *cursor_manager); static void gsd_cursor_manager_finalize (GObject *object); G_DEFINE_TYPE (GsdCursorManager, gsd_cursor_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static gboolean add_all_devices (GsdCursorManager *manager, GdkDevice *exception, GError **error); typedef void (*ForeachScreenFunc) (GdkDisplay *display, GdkScreen *screen, GsdCursorManager *manager, gpointer user_data); static void foreach_screen (GsdCursorManager *manager, ForeachScreenFunc func, gpointer user_data) { GdkDisplay *display; guint n_screens; guint i; display = gdk_display_get_default (); n_screens = gdk_display_get_n_screens (display); for (i = 0; i < n_screens; i++) { GdkScreen *screen; screen = gdk_display_get_screen (display, i); (func) (display, screen, manager, user_data); } } static void set_cursor_visibility_foreach (GdkDisplay *display, GdkScreen *screen, GsdCursorManager *manager, gpointer user_data) { Display *xdisplay; gboolean visible = GPOINTER_TO_INT (user_data); xdisplay = GDK_DISPLAY_XDISPLAY (display); if (visible) XFixesShowCursor (xdisplay, GDK_WINDOW_XID (gdk_screen_get_root_window (screen))); else XFixesHideCursor (xdisplay, GDK_WINDOW_XID (gdk_screen_get_root_window (screen))); } static void set_cursor_visibility (GsdCursorManager *manager, gboolean visible) { g_debug ("Attempting to %s the cursor", visible ? "show" : "hide"); if (manager->priv->cursor_shown == visible) return; gdk_error_trap_push (); foreach_screen (manager, set_cursor_visibility_foreach, GINT_TO_POINTER (visible)); if (gdk_error_trap_pop ()) { g_warning ("An error occurred trying to %s the cursor", visible ? "show" : "hide"); } manager->priv->cursor_shown = visible; } static void monitor_became_active (GsdIdleMonitor *monitor, guint watch_id, gpointer user_data) { GdkDevice *device; int device_id; GsdCursorManager *manager = GSD_CURSOR_MANAGER (user_data); GdkDeviceManager *device_manager; /* Oh, so you're active? */ g_object_get (G_OBJECT (monitor), "device_id", &device_id, NULL); device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); device = gdk_x11_device_manager_lookup (device_manager, device_id); g_debug ("Device %d '%s' became active", gdk_x11_device_get_id (device), gdk_device_get_name (device)); set_cursor_visibility (manager, gdk_device_get_source (device) != GDK_SOURCE_TOUCHSCREEN); /* Remove the device from the watch */ g_hash_table_remove (manager->priv->monitors, device); /* Make sure that all the other devices are watched * (but not the one we just stopped monitoring */ add_all_devices (manager, device, NULL); } static gboolean add_device (GdkDeviceManager *device_manager, GdkDevice *device, GsdCursorManager *manager, GError **error) { GsdIdleMonitor *monitor; int device_id; if (g_hash_table_lookup (manager->priv->monitors, device) != NULL) return TRUE; if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_SLAVE) return TRUE; if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) return TRUE; if (strstr (gdk_device_get_name (device), "XTEST") != NULL) return TRUE; /* Create IdleMonitors for each pointer device */ device_id = gdk_x11_device_get_id (device); monitor = gsd_idle_monitor_get_for_device (device_id); if (!monitor) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Per-device idletime monitor not available"); return FALSE; } g_hash_table_insert (manager->priv->monitors, device, g_object_ref (monitor)); gsd_idle_monitor_add_user_active_watch (monitor, monitor_became_active, manager, NULL); return TRUE; } static void device_added_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdCursorManager *manager) { add_device (device_manager, device, manager, NULL); } static void device_removed_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdCursorManager *manager) { g_hash_table_remove (manager->priv->monitors, device); } static void device_changed_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdCursorManager *manager) { if (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_FLOATING) device_removed_cb (device_manager, device, manager); else device_added_cb (device_manager, device, manager); } static gboolean supports_xfixes (void) { gint op_code, event, error; return XQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "XFIXES", &op_code, &event, &error); } static gboolean supports_cursor_xfixes (void) { int major = XFIXES_CURSOR_HIDING_MAJOR; int minor = 0; gdk_error_trap_push (); if (!supports_xfixes ()) { gdk_error_trap_pop_ignored (); return FALSE; } if (!XFixesQueryVersion (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &major, &minor)) { gdk_error_trap_pop_ignored (); return FALSE; } gdk_error_trap_pop_ignored (); if (major >= XFIXES_CURSOR_HIDING_MAJOR) return TRUE; return FALSE; } static gboolean add_all_devices (GsdCursorManager *manager, GdkDevice *exception, GError **error) { GdkDeviceManager *device_manager; GList *devices, *l; gboolean ret = TRUE; device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE); for (l = devices; l != NULL; l = l->next) { GdkDevice *device = l->data; if (device == exception) continue; if (!add_device (device_manager, device, manager, error)) { ret = FALSE; break; } } g_list_free (devices); return ret; } gboolean gsd_cursor_manager_start (GsdCursorManager *manager, GError **error) { GdkDeviceManager *device_manager; g_debug ("Starting cursor manager"); gnome_settings_profile_start (NULL); if (supports_cursor_xfixes () == FALSE) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "XFixes cursor extension not available"); return FALSE; } if (supports_xinput_devices () == FALSE) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "XInput support not available"); return FALSE; } device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); manager->priv->added_id = g_signal_connect (G_OBJECT (device_manager), "device-added", G_CALLBACK (device_added_cb), manager); manager->priv->removed_id = g_signal_connect (G_OBJECT (device_manager), "device-removed", G_CALLBACK (device_removed_cb), manager); manager->priv->changed_id = g_signal_connect (G_OBJECT (device_manager), "device-changed", G_CALLBACK (device_changed_cb), manager); if (!add_all_devices (manager, NULL, error)) { g_debug ("Per-device idletime monitor not available, will not hide the cursor"); gnome_settings_profile_end (NULL); return FALSE; } /* Start by hiding the cursor, and then initialising the default * root window cursor, as the window manager shouldn't do that. */ set_cursor_visibility (manager, FALSE); gnome_settings_profile_end (NULL); return TRUE; } void gsd_cursor_manager_stop (GsdCursorManager *manager) { GdkDeviceManager *device_manager; g_debug ("Stopping cursor manager"); device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); if (manager->priv->added_id > 0) { g_signal_handler_disconnect (G_OBJECT (device_manager), manager->priv->added_id); manager->priv->added_id = 0; } if (manager->priv->removed_id > 0) { g_signal_handler_disconnect (G_OBJECT (device_manager), manager->priv->removed_id); manager->priv->removed_id = 0; } if (manager->priv->changed_id > 0) { g_signal_handler_disconnect (G_OBJECT (device_manager), manager->priv->changed_id); manager->priv->changed_id = 0; } if (manager->priv->cursor_shown == FALSE) set_cursor_visibility (manager, TRUE); } static void gsd_cursor_manager_class_init (GsdCursorManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_cursor_manager_finalize; g_type_class_add_private (klass, sizeof (GsdCursorManagerPrivate)); } static void gsd_cursor_manager_init (GsdCursorManager *manager) { manager->priv = GSD_CURSOR_MANAGER_GET_PRIVATE (manager); manager->priv->cursor_shown = TRUE; manager->priv->monitors = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); } static void gsd_cursor_manager_finalize (GObject *object) { GsdCursorManager *cursor_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_CURSOR_MANAGER (object)); cursor_manager = GSD_CURSOR_MANAGER (object); g_clear_pointer (&cursor_manager->priv->monitors, g_hash_table_destroy); G_OBJECT_CLASS (gsd_cursor_manager_parent_class)->finalize (object); } GsdCursorManager * gsd_cursor_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_CURSOR_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_CURSOR_MANAGER (manager_object); } ./plugins/cursor/gsd-cursor-manager.h0000644000004100000410000000442612735467744020143 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_CURSOR_MANAGER_H #define __GSD_CURSOR_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_CURSOR_MANAGER (gsd_cursor_manager_get_type ()) #define GSD_CURSOR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_CURSOR_MANAGER, GsdCursorManager)) #define GSD_CURSOR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_CURSOR_MANAGER, GsdCursorManagerClass)) #define GSD_IS_CURSOR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_CURSOR_MANAGER)) #define GSD_IS_CURSOR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_CURSOR_MANAGER)) #define GSD_CURSOR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_CURSOR_MANAGER, GsdCursorManagerClass)) typedef struct GsdCursorManagerPrivate GsdCursorManagerPrivate; typedef struct { GObject parent; GsdCursorManagerPrivate *priv; } GsdCursorManager; typedef struct { GObjectClass parent_class; } GsdCursorManagerClass; GType gsd_cursor_manager_get_type (void); GsdCursorManager * gsd_cursor_manager_new (void); gboolean gsd_cursor_manager_start (GsdCursorManager *manager, GError **error); void gsd_cursor_manager_stop (GsdCursorManager *manager); G_END_DECLS #endif /* __GSD_CURSOR_MANAGER_H */ ./plugins/cursor/cursor.gnome-settings-plugin.in0000644000004100000410000000035212735467744022367 0ustar www-datawww-data[GNOME Settings Plugin] Module=cursor IAge=0 # Default Priority # Priority=100 _Name=Cursor _Description=Show/hide cursor on tablet devices Authors=Bastien Nocera Copyright=Copyright © 2011 Red Hat, Inc. Website= ./plugins/media-keys/0000755000004100000410000000000012735467763015000 5ustar www-datawww-data./plugins/media-keys/gvc/0000755000004100000410000000000012735467744015556 5ustar www-datawww-data./plugins/media-keys/gvc/gvc-mixer-ui-device.c0000644000004100000410000006025612735467744021504 0ustar www-datawww-data/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * gvc-mixer-ui-device.c * Copyright (C) Conor Curran 2011 * Copyright (C) 2012 David Henningsson, Canonical Ltd. * * gvc-mixer-ui-device.c 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. * * gvc-mixer-ui-device.c 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, see . */ #include #include "gvc-mixer-ui-device.h" #include "gvc-mixer-card.h" #define GVC_MIXER_UI_DEVICE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDevicePrivate)) struct GvcMixerUIDevicePrivate { gchar *first_line_desc; gchar *second_line_desc; GvcMixerCard *card; gchar *port_name; gint stream_id; guint id; gboolean port_available; /* These two lists contain pointers to GvcMixerCardProfile objects. Those objects are owned by GvcMixerCard. * * TODO: Do we want to add a weak reference to the GvcMixerCard for this reason? */ GList *supported_profiles; /* all profiles supported by this port.*/ GList *profiles; /* profiles to be added to combobox, subset of supported_profiles. */ GvcMixerUIDeviceDirection type; gboolean disable_profile_swapping; gchar *user_preferred_profile; }; enum { PROP_0, PROP_DESC_LINE_1, PROP_DESC_LINE_2, PROP_CARD, PROP_PORT_NAME, PROP_STREAM_ID, PROP_UI_DEVICE_TYPE, PROP_PORT_AVAILABLE, }; static void gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass); static void gvc_mixer_ui_device_init (GvcMixerUIDevice *device); static void gvc_mixer_ui_device_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerUIDevice, gvc_mixer_ui_device, G_TYPE_OBJECT); static guint32 get_next_output_serial (void) { static guint32 output_serial = 1; guint32 serial; serial = output_serial++; if ((gint32)output_serial < 0) output_serial = 1; return serial; } static void gvc_mixer_ui_device_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object); switch (property_id) { case PROP_DESC_LINE_1: g_value_set_string (value, self->priv->first_line_desc); break; case PROP_DESC_LINE_2: g_value_set_string (value, self->priv->second_line_desc); break; case PROP_CARD: g_value_set_pointer (value, self->priv->card); break; case PROP_PORT_NAME: g_value_set_string (value, self->priv->port_name); break; case PROP_STREAM_ID: g_value_set_int (value, self->priv->stream_id); break; case PROP_UI_DEVICE_TYPE: g_value_set_uint (value, (guint)self->priv->type); break; case PROP_PORT_AVAILABLE: g_value_set_boolean (value, self->priv->port_available); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gvc_mixer_ui_device_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GvcMixerUIDevice *self = GVC_MIXER_UI_DEVICE (object); switch (property_id) { case PROP_DESC_LINE_1: g_free (self->priv->first_line_desc); self->priv->first_line_desc = g_value_dup_string (value); g_debug ("gvc-mixer-output-set-property - 1st line: %s\n", self->priv->first_line_desc); break; case PROP_DESC_LINE_2: g_free (self->priv->second_line_desc); self->priv->second_line_desc = g_value_dup_string (value); g_debug ("gvc-mixer-output-set-property - 2nd line: %s\n", self->priv->second_line_desc); break; case PROP_CARD: self->priv->card = g_value_get_pointer (value); g_debug ("gvc-mixer-output-set-property - card: %p\n", self->priv->card); break; case PROP_PORT_NAME: g_free (self->priv->port_name); self->priv->port_name = g_value_dup_string (value); g_debug ("gvc-mixer-output-set-property - card port name: %s\n", self->priv->port_name); break; case PROP_STREAM_ID: self->priv->stream_id = g_value_get_int (value); g_debug ("gvc-mixer-output-set-property - sink/source id: %i\n", self->priv->stream_id); break; case PROP_UI_DEVICE_TYPE: self->priv->type = (GvcMixerUIDeviceDirection) g_value_get_uint (value); break; case PROP_PORT_AVAILABLE: self->priv->port_available = g_value_get_boolean (value); g_debug ("gvc-mixer-output-set-property - port available %i, value passed in %i \n", self->priv->port_available, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static GObject * gvc_mixer_ui_device_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerUIDevice *self; object = G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_UI_DEVICE (object); self->priv->id = get_next_output_serial (); self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID; return object; } static void gvc_mixer_ui_device_init (GvcMixerUIDevice *device) { device->priv = GVC_MIXER_UI_DEVICE_GET_PRIVATE (device); } static void gvc_mixer_ui_device_dispose (GObject *object) { GvcMixerUIDevice *device; g_return_if_fail (object != NULL); g_return_if_fail (GVC_MIXER_UI_DEVICE (object)); device = GVC_MIXER_UI_DEVICE (object); g_clear_pointer (&device->priv->port_name, g_free); g_clear_pointer (&device->priv->first_line_desc, g_free); g_clear_pointer (&device->priv->second_line_desc, g_free); g_clear_pointer (&device->priv->profiles, g_list_free); g_clear_pointer (&device->priv->supported_profiles, g_list_free); g_clear_pointer (&device->priv->user_preferred_profile, g_free); G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->dispose (object); } static void gvc_mixer_ui_device_finalize (GObject *object) { G_OBJECT_CLASS (gvc_mixer_ui_device_parent_class)->finalize (object); } static void gvc_mixer_ui_device_class_init (GvcMixerUIDeviceClass *klass) { GObjectClass* object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->constructor = gvc_mixer_ui_device_constructor; object_class->dispose = gvc_mixer_ui_device_dispose; object_class->finalize = gvc_mixer_ui_device_finalize; object_class->set_property = gvc_mixer_ui_device_set_property; object_class->get_property = gvc_mixer_ui_device_get_property; pspec = g_param_spec_string ("description", "Description construct prop", "Set first line description", "no-name-set", G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_DESC_LINE_1, pspec); pspec = g_param_spec_string ("origin", "origin construct prop", "Set second line description name", "no-name-set", G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_DESC_LINE_2, pspec); pspec = g_param_spec_pointer ("card", "Card from pulse", "Set/Get card", G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_CARD, pspec); pspec = g_param_spec_string ("port-name", "port-name construct prop", "Set port-name", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PORT_NAME, pspec); pspec = g_param_spec_int ("stream-id", "stream id assigned by gvc-stream", "Set/Get stream id", -1, G_MAXINT, GVC_MIXER_UI_DEVICE_INVALID, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_STREAM_ID, pspec); pspec = g_param_spec_uint ("type", "ui-device type", "determine whether its an input and output", 0, 1, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_UI_DEVICE_TYPE, pspec); pspec = g_param_spec_boolean ("port-available", "available", "determine whether this port is available", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_PORT_AVAILABLE, pspec); g_type_class_add_private (klass, sizeof (GvcMixerUIDevicePrivate)); } /* Removes the part of the string that starts with skip_prefix * ie. corresponding to the other direction. * Normally either "input:" or "output:" * * Example: if given the input string "output:hdmi-stereo+input:analog-stereo" and * skip_prefix "input:", the resulting string is "output:hdmi-stereo". * * The returned string must be freed with g_free(). */ static gchar * get_profile_canonical_name (const gchar *profile_name, const gchar *skip_prefix) { gchar *result = NULL; gchar **s; int i; /* optimisation for the simple case. */ if (strstr (profile_name, skip_prefix) == NULL) return g_strdup (profile_name); s = g_strsplit (profile_name, "+", 0); for (i = 0; i < g_strv_length (s); i++) { if (g_str_has_prefix (s[i], skip_prefix)) continue; if (result == NULL) result = g_strdup (s[i]); else { gchar *c = g_strdup_printf("%s+%s", result, s[i]); g_free(result); result = c; } } g_strfreev(s); if (!result) return g_strdup("off"); return result; } const gchar * gvc_mixer_ui_device_get_matching_profile (GvcMixerUIDevice *device, const gchar *profile) { gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:"; gchar *target_cname = get_profile_canonical_name (profile, skip_prefix); GList *l; gchar *result = NULL; for (l = device->priv->profiles; l != NULL; l = l->next) { gchar *canonical_name; GvcMixerCardProfile* p = l->data; canonical_name = get_profile_canonical_name (p->profile, skip_prefix); if (strcmp (canonical_name, target_cname) == 0) result = p->profile; g_free (canonical_name); } g_free (target_cname); g_debug ("Matching profile for '%s' is '%s'", profile, result ? result : "(null)"); return result; } static void add_canonical_names_of_profiles (GvcMixerUIDevice *device, const GList *in_profiles, GHashTable *added_profiles, const gchar *skip_prefix, gboolean only_canonical) { const GList *l; for (l = in_profiles; l != NULL; l = l->next) { gchar *canonical_name; GvcMixerCardProfile* p = l->data; canonical_name = get_profile_canonical_name (p->profile, skip_prefix); g_debug ("The canonical name for '%s' is '%s'", p->profile, canonical_name); /* Have we already added the canonical version of this profile? */ if (g_hash_table_contains (added_profiles, canonical_name)) { g_free (canonical_name); continue; } if (only_canonical && strcmp (p->profile, canonical_name) != 0) { g_free (canonical_name); continue; } g_free (canonical_name); g_debug ("Adding profile to combobox: '%s' - '%s'", p->profile, p->human_profile); g_hash_table_insert (added_profiles, g_strdup (p->profile), p); device->priv->profiles = g_list_append (device->priv->profiles, p); } } /** * gvc_mixer_ui_device_set_profiles: * @in_profiles: (element-type Gvc.MixerCardProfile): a list of GvcMixerCardProfile * * Assigns value to * - device->priv->profiles (profiles to be added to combobox) * - device->priv->supported_profiles (all profiles of this port) * - device->priv->disable_profile_swapping (whether to show the combobox) * * This method attempts to reduce the list of profiles visible to the user by figuring out * from the context of that device (whether it's an input or an output) what profiles * actually provide an alternative. * * It does this by the following. * - It ignores off profiles. * - It takes the canonical name of the profile. That name is what you get when you * ignore the other direction. * - In the first iteration, it only adds the names of canonical profiles - i e * when the other side is turned off. * - Normally the first iteration covers all cases, but sometimes (e g bluetooth) * it doesn't, so add other profiles whose canonical name isn't already added * in a second iteration. */ void gvc_mixer_ui_device_set_profiles (GvcMixerUIDevice *device, const GList *in_profiles) { GHashTable *added_profiles; gchar *skip_prefix = device->priv->type == UIDeviceInput ? "output:" : "input:"; g_debug ("Set profiles for '%s'", gvc_mixer_ui_device_get_description(device)); if (in_profiles == NULL) return; device->priv->supported_profiles = g_list_copy ((GList*) in_profiles); added_profiles = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); /* Run two iterations: First, add profiles which are canonical themselves, * Second, add profiles for which the canonical name is not added already. */ add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, TRUE); add_canonical_names_of_profiles(device, in_profiles, added_profiles, skip_prefix, FALSE); /* TODO: Consider adding the "Off" profile here */ device->priv->disable_profile_swapping = g_hash_table_size (added_profiles) <= 1; g_hash_table_destroy (added_profiles); } /** * gvc_mixer_ui_device_get_best_profile: * @selected: The selected profile or its canonical name or %NULL for any profile * @current: The currently selected profile * * Returns: (transfer none): a profile name, valid as long as the UI device profiles are. */ const gchar * gvc_mixer_ui_device_get_best_profile (GvcMixerUIDevice *device, const gchar *selected, const gchar *current) { GList *candidates, *l; const gchar *result; gchar *skip_prefix; gchar *canonical_name_selected; if (device->priv->type == UIDeviceInput) skip_prefix = "output:"; else skip_prefix = "input:"; /* First make a list of profiles acceptable to switch to */ canonical_name_selected = NULL; if (selected) canonical_name_selected = get_profile_canonical_name (selected, skip_prefix); candidates = NULL; for (l = device->priv->supported_profiles; l != NULL; l = l->next) { gchar *canonical_name; GvcMixerCardProfile* p = l->data; canonical_name = get_profile_canonical_name (p->profile, skip_prefix); if (!canonical_name_selected || strcmp (canonical_name, canonical_name_selected) == 0) { candidates = g_list_append (candidates, p); g_debug ("Candidate for profile switching: '%s'", p->profile); } } if (!candidates) { g_warning ("No suitable profile candidates for '%s'", selected ? selected : "(null)"); g_free (canonical_name_selected); return current; } /* 1) Maybe we can skip profile switching altogether? */ result = NULL; for (l = candidates; (result == NULL) && (l != NULL); l = l->next) { GvcMixerCardProfile* p = l->data; if (strcmp (current, p->profile) == 0) result = p->profile; } /* 2) Try to keep the other side unchanged if possible */ if (result == NULL) { guint prio = 0; gchar *skip_prefix_reverse = device->priv->type == UIDeviceInput ? "input:" : "output:"; gchar *current_reverse = get_profile_canonical_name (current, skip_prefix_reverse); for (l = candidates; l != NULL; l = l->next) { gchar *p_reverse; GvcMixerCardProfile* p = l->data; p_reverse = get_profile_canonical_name (p->profile, skip_prefix_reverse); g_debug ("Comparing '%s' (from '%s') with '%s', prio %d", p_reverse, p->profile, current_reverse, p->priority); if (strcmp (p_reverse, current_reverse) == 0 && (!result || p->priority > prio)) { result = p->profile; prio = p->priority; } g_free (p_reverse); } g_free (current_reverse); } /* 3) All right, let's just pick the profile with highest priority. * TODO: We could consider asking a GUI question if this stops streams * in the other direction */ if (result == NULL) { guint prio = 0; for (l = candidates; l != NULL; l = l->next) { GvcMixerCardProfile* p = l->data; if ((p->priority > prio) || !result) { result = p->profile; prio = p->priority; } } } g_list_free (candidates); g_free (canonical_name_selected); return result; } const gchar * gvc_mixer_ui_device_get_active_profile (GvcMixerUIDevice* device) { GvcMixerCardProfile *profile; g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); if (device->priv->card == NULL) { g_warning ("Device did not have an appropriate card"); return NULL; } profile = gvc_mixer_card_get_profile (device->priv->card); return gvc_mixer_ui_device_get_matching_profile (device, profile->profile); } gboolean gvc_mixer_ui_device_should_profiles_be_hidden (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE); return device->priv->disable_profile_swapping; } /** * gvc_mixer_ui_device_get_profiles: * @device: * * Returns: (transfer none) (element-type Gvc.MixerCardProfile): */ GList* gvc_mixer_ui_device_get_profiles (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->profiles; } /** * gvc_mixer_ui_device_get_supported_profiles: * @device: * * Returns: (transfer none) (element-type Gvc.MixerCardProfile): */ GList* gvc_mixer_ui_device_get_supported_profiles (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->supported_profiles; } guint gvc_mixer_ui_device_get_id (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0); return device->priv->id; } gint gvc_mixer_ui_device_get_stream_id (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), 0); return device->priv->stream_id; } void gvc_mixer_ui_device_invalidate_stream (GvcMixerUIDevice *self) { g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (self)); self->priv->stream_id = GVC_MIXER_UI_DEVICE_INVALID; } const gchar * gvc_mixer_ui_device_get_description (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->first_line_desc; } const gchar * gvc_mixer_ui_device_get_origin (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->second_line_desc; } const gchar* gvc_mixer_ui_device_get_user_preferred_profile (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->user_preferred_profile; } const gchar * gvc_mixer_ui_device_get_top_priority_profile (GvcMixerUIDevice *device) { GList *last; GvcMixerCardProfile *profile; g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); last = g_list_last (device->priv->supported_profiles); profile = last->data; return profile->profile; } void gvc_mixer_ui_device_set_user_preferred_profile (GvcMixerUIDevice *device, const gchar *profile) { g_return_if_fail (GVC_IS_MIXER_UI_DEVICE (device)); g_free (device->priv->user_preferred_profile); device->priv->user_preferred_profile = g_strdup (profile); } const gchar * gvc_mixer_ui_device_get_port (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); return device->priv->port_name; } gboolean gvc_mixer_ui_device_has_ports (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE); return (device->priv->port_name != NULL); } gboolean gvc_mixer_ui_device_is_output (GvcMixerUIDevice *device) { g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), FALSE); return (device->priv->type == UIDeviceOutput); } ./plugins/media-keys/gvc/gvc-mixer-ui-device.h0000644000004100000410000001012712735467744021501 0ustar www-datawww-data/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * Copyright (C) Conor Curran 2011 * * This 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. * * gvc-mixer-ui-device.h 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, see . */ #ifndef _GVC_MIXER_UI_DEVICE_H_ #define _GVC_MIXER_UI_DEVICE_H_ #include G_BEGIN_DECLS #define GVC_TYPE_MIXER_UI_DEVICE (gvc_mixer_ui_device_get_type ()) #define GVC_MIXER_UI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDevice)) #define GVC_MIXER_UI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass)) #define GVC_IS_MIXER_UI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GVC_TYPE_MIXER_UI_DEVICE)) #define GVC_IS_MIXER_UI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GVC_TYPE_MIXER_UI_DEVICE)) #define GVC_MIXER_UI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GVC_TYPE_MIXER_UI_DEVICE, GvcMixerUIDeviceClass)) #define GVC_MIXER_UI_DEVICE_INVALID -1 typedef struct GvcMixerUIDevicePrivate GvcMixerUIDevicePrivate; typedef struct { GObjectClass parent_class; } GvcMixerUIDeviceClass; typedef struct { GObject parent_instance; GvcMixerUIDevicePrivate *priv; } GvcMixerUIDevice; typedef enum { UIDeviceInput, UIDeviceOutput, } GvcMixerUIDeviceDirection; GType gvc_mixer_ui_device_get_type (void) G_GNUC_CONST; guint gvc_mixer_ui_device_get_id (GvcMixerUIDevice *device); gint gvc_mixer_ui_device_get_stream_id (GvcMixerUIDevice *device); const gchar * gvc_mixer_ui_device_get_description (GvcMixerUIDevice *device); const gchar * gvc_mixer_ui_device_get_origin (GvcMixerUIDevice *device); const gchar * gvc_mixer_ui_device_get_port (GvcMixerUIDevice *device); const gchar * gvc_mixer_ui_device_get_best_profile (GvcMixerUIDevice *device, const gchar *selected, const gchar *current); const gchar * gvc_mixer_ui_device_get_active_profile (GvcMixerUIDevice* device); const gchar * gvc_mixer_ui_device_get_matching_profile (GvcMixerUIDevice *device, const gchar *profile); const gchar * gvc_mixer_ui_device_get_user_preferred_profile (GvcMixerUIDevice *device); const gchar * gvc_mixer_ui_device_get_top_priority_profile (GvcMixerUIDevice *device); GList * gvc_mixer_ui_device_get_profiles (GvcMixerUIDevice *device); GList * gvc_mixer_ui_device_get_supported_profiles (GvcMixerUIDevice *device); gboolean gvc_mixer_ui_device_should_profiles_be_hidden (GvcMixerUIDevice *device); void gvc_mixer_ui_device_set_profiles (GvcMixerUIDevice *device, const GList *in_profiles); void gvc_mixer_ui_device_set_user_preferred_profile (GvcMixerUIDevice *device, const gchar *profile); void gvc_mixer_ui_device_invalidate_stream (GvcMixerUIDevice *device); gboolean gvc_mixer_ui_device_has_ports (GvcMixerUIDevice *device); gboolean gvc_mixer_ui_device_is_output (GvcMixerUIDevice *device); G_END_DECLS #endif /* _GVC_MIXER_UI_DEVICE_H_ */ ./plugins/media-keys/gvc/gvc-channel-map-private.h0000644000004100000410000000305612735467744022343 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_CHANNEL_MAP_PRIVATE_H #define __GVC_CHANNEL_MAP_PRIVATE_H #include #include G_BEGIN_DECLS GvcChannelMap * gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *map); const pa_channel_map * gvc_channel_map_get_pa_channel_map (const GvcChannelMap *map); void gvc_channel_map_volume_changed (GvcChannelMap *map, const pa_cvolume *cv, gboolean set); const pa_cvolume * gvc_channel_map_get_cvolume (const GvcChannelMap *map); G_END_DECLS #endif /* __GVC_CHANNEL_MAP_PRIVATE_H */ ./plugins/media-keys/gvc/gvc-mixer-card-private.h0000644000004100000410000000237512735467744022216 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008-2009 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 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. * */ #ifndef __GVC_MIXER_CARD_PRIVATE_H #define __GVC_MIXER_CARD_PRIVATE_H #include #include "gvc-mixer-card.h" G_BEGIN_DECLS GvcMixerCard * gvc_mixer_card_new (pa_context *context, guint index); pa_context * gvc_mixer_card_get_pa_context (GvcMixerCard *card); G_END_DECLS #endif /* __GVC_MIXER_CARD_PRIVATE_H */ ./plugins/media-keys/gvc/gvc-mixer-card.c0000644000004100000410000004463012735467744020541 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * Copyright (C) 2009 Bastien Nocera * Copyright (C) Conor Curran 2011 * * 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 "config.h" #include #include #include #include #include #include #include "gvc-mixer-card.h" #include "gvc-mixer-card-private.h" #define GVC_MIXER_CARD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CARD, GvcMixerCardPrivate)) static guint32 card_serial = 1; struct GvcMixerCardPrivate { pa_context *pa_context; guint id; guint index; char *name; char *icon_name; char *profile; char *target_profile; char *human_profile; GList *profiles; pa_operation *profile_op; GList *ports; }; enum { PROP_0, PROP_ID, PROP_PA_CONTEXT, PROP_INDEX, PROP_NAME, PROP_ICON_NAME, PROP_PROFILE, PROP_HUMAN_PROFILE, }; static void gvc_mixer_card_class_init (GvcMixerCardClass *klass); static void gvc_mixer_card_init (GvcMixerCard *mixer_card); static void gvc_mixer_card_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerCard, gvc_mixer_card, G_TYPE_OBJECT) static guint32 get_next_card_serial (void) { guint32 serial; serial = card_serial++; if ((gint32)card_serial < 0) { card_serial = 1; } return serial; } pa_context * gvc_mixer_card_get_pa_context (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0); return card->priv->pa_context; } guint gvc_mixer_card_get_index (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0); return card->priv->index; } guint gvc_mixer_card_get_id (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), 0); return card->priv->id; } const char * gvc_mixer_card_get_name (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->name; } gboolean gvc_mixer_card_set_name (GvcMixerCard *card, const char *name) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_free (card->priv->name); card->priv->name = g_strdup (name); g_object_notify (G_OBJECT (card), "name"); return TRUE; } const char * gvc_mixer_card_get_icon_name (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->icon_name; } gboolean gvc_mixer_card_set_icon_name (GvcMixerCard *card, const char *icon_name) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_free (card->priv->icon_name); card->priv->icon_name = g_strdup (icon_name); g_object_notify (G_OBJECT (card), "icon-name"); return TRUE; } /** * gvc_mixer_card_get_profile: (skip) * @card: * * Returns: */ GvcMixerCardProfile * gvc_mixer_card_get_profile (GvcMixerCard *card) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); g_return_val_if_fail (card->priv->profiles != NULL, NULL); for (l = card->priv->profiles; l != NULL; l = l->next) { GvcMixerCardProfile *p = l->data; if (g_str_equal (card->priv->profile, p->profile)) { return p; } } g_assert_not_reached (); return NULL; } gboolean gvc_mixer_card_set_profile (GvcMixerCard *card, const char *profile) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->profiles != NULL, FALSE); g_free (card->priv->profile); card->priv->profile = g_strdup (profile); g_free (card->priv->human_profile); card->priv->human_profile = NULL; for (l = card->priv->profiles; l != NULL; l = l->next) { GvcMixerCardProfile *p = l->data; if (g_str_equal (card->priv->profile, p->profile)) { card->priv->human_profile = g_strdup (p->human_profile); break; } } g_object_notify (G_OBJECT (card), "profile"); return TRUE; } static void _pa_context_set_card_profile_by_index_cb (pa_context *context, int success, void *userdata) { GvcMixerCard *card = GVC_MIXER_CARD (userdata); g_assert (card->priv->target_profile); if (success > 0) { gvc_mixer_card_set_profile (card, card->priv->target_profile); } else { g_debug ("Failed to switch profile on '%s' from '%s' to '%s'", card->priv->name, card->priv->profile, card->priv->target_profile); } g_free (card->priv->target_profile); card->priv->target_profile = NULL; pa_operation_unref (card->priv->profile_op); card->priv->profile_op = NULL; } gboolean gvc_mixer_card_change_profile (GvcMixerCard *card, const char *profile) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->profiles != NULL, FALSE); /* Same profile, or already requested? */ if (g_strcmp0 (card->priv->profile, profile) == 0) return TRUE; if (g_strcmp0 (profile, card->priv->target_profile) == 0) return TRUE; if (card->priv->profile_op != NULL) { pa_operation_cancel (card->priv->profile_op); pa_operation_unref (card->priv->profile_op); card->priv->profile_op = NULL; } if (card->priv->profile != NULL) { g_free (card->priv->target_profile); card->priv->target_profile = g_strdup (profile); card->priv->profile_op = pa_context_set_card_profile_by_index (card->priv->pa_context, card->priv->index, card->priv->target_profile, _pa_context_set_card_profile_by_index_cb, card); if (card->priv->profile_op == NULL) { g_warning ("pa_context_set_card_profile_by_index() failed"); return FALSE; } } else { g_assert (card->priv->human_profile == NULL); card->priv->profile = g_strdup (profile); } return TRUE; } /** * gvc_mixer_card_get_profiles: * * Return value: (transfer none) (element-type GvcMixerCardProfile): */ const GList * gvc_mixer_card_get_profiles (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->profiles; } /** * gvc_mixer_card_get_ports: * * Return value: (transfer none) (element-type GvcMixerCardPort): */ const GList * gvc_mixer_card_get_ports (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); return card->priv->ports; } /** * gvc_mixer_card_profile_compare: * * Return value: 1 if @a has a higher priority, -1 if @b has a higher * priority, 0 if @a and @b have the same priority. */ int gvc_mixer_card_profile_compare (GvcMixerCardProfile *a, GvcMixerCardProfile *b) { if (a->priority == b->priority) return 0; if (a->priority > b->priority) return 1; return -1; } /** * gvc_mixer_card_set_profiles: * @profiles: (transfer full) (element-type GvcMixerCardProfile): */ gboolean gvc_mixer_card_set_profiles (GvcMixerCard *card, GList *profiles) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->profiles == NULL, FALSE); card->priv->profiles = g_list_sort (profiles, (GCompareFunc) gvc_mixer_card_profile_compare); return TRUE; } /** * gvc_mixer_card_get_gicon: * @card: * * Return value: (transfer full): */ GIcon * gvc_mixer_card_get_gicon (GvcMixerCard *card) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), NULL); if (card->priv->icon_name == NULL) return NULL; return g_themed_icon_new_with_default_fallbacks (card->priv->icon_name); } /** * gvc_mixer_card_set_ports: * @ports: (transfer full) (element-type GvcMixerCardPort): */ gboolean gvc_mixer_card_set_ports (GvcMixerCard *card, GList *ports) { g_return_val_if_fail (GVC_IS_MIXER_CARD (card), FALSE); g_return_val_if_fail (card->priv->ports == NULL, FALSE); card->priv->ports = ports; return TRUE; } static void gvc_mixer_card_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerCard *self = GVC_MIXER_CARD (object); switch (prop_id) { case PROP_PA_CONTEXT: self->priv->pa_context = g_value_get_pointer (value); break; case PROP_INDEX: self->priv->index = g_value_get_ulong (value); break; case PROP_ID: self->priv->id = g_value_get_ulong (value); break; case PROP_NAME: gvc_mixer_card_set_name (self, g_value_get_string (value)); break; case PROP_ICON_NAME: gvc_mixer_card_set_icon_name (self, g_value_get_string (value)); break; case PROP_PROFILE: gvc_mixer_card_set_profile (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_card_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerCard *self = GVC_MIXER_CARD (object); switch (prop_id) { case PROP_PA_CONTEXT: g_value_set_pointer (value, self->priv->pa_context); break; case PROP_INDEX: g_value_set_ulong (value, self->priv->index); break; case PROP_ID: g_value_set_ulong (value, self->priv->id); break; case PROP_NAME: g_value_set_string (value, self->priv->name); break; case PROP_ICON_NAME: g_value_set_string (value, self->priv->icon_name); break; case PROP_PROFILE: g_value_set_string (value, self->priv->profile); break; case PROP_HUMAN_PROFILE: g_value_set_string (value, self->priv->human_profile); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gvc_mixer_card_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerCard *self; object = G_OBJECT_CLASS (gvc_mixer_card_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_CARD (object); self->priv->id = get_next_card_serial (); return object; } static void gvc_mixer_card_class_init (GvcMixerCardClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructor = gvc_mixer_card_constructor; gobject_class->finalize = gvc_mixer_card_finalize; gobject_class->set_property = gvc_mixer_card_set_property; gobject_class->get_property = gvc_mixer_card_get_property; g_object_class_install_property (gobject_class, PROP_INDEX, g_param_spec_ulong ("index", "Index", "The index for this card", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_ID, g_param_spec_ulong ("id", "id", "The id for this card", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_PA_CONTEXT, g_param_spec_pointer ("pa-context", "PulseAudio context", "The PulseAudio context for this card", G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "Name", "Name to display for this card", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_ICON_NAME, g_param_spec_string ("icon-name", "Icon Name", "Name of icon to display for this card", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_PROFILE, g_param_spec_string ("profile", "Profile", "Name of current profile for this card", NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_HUMAN_PROFILE, g_param_spec_string ("human-profile", "Profile (Human readable)", "Name of current profile for this card in human readable form", NULL, G_PARAM_READABLE)); g_type_class_add_private (klass, sizeof (GvcMixerCardPrivate)); } static void gvc_mixer_card_init (GvcMixerCard *card) { card->priv = GVC_MIXER_CARD_GET_PRIVATE (card); } GvcMixerCard * gvc_mixer_card_new (pa_context *context, guint index) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_CARD, "index", index, "pa-context", context, NULL); return GVC_MIXER_CARD (object); } static void free_profile (GvcMixerCardProfile *p) { g_free (p->profile); g_free (p->human_profile); g_free (p->status); g_free (p); } static void gvc_mixer_card_finalize (GObject *object) { GvcMixerCard *mixer_card; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_CARD (object)); mixer_card = GVC_MIXER_CARD (object); g_return_if_fail (mixer_card->priv != NULL); g_free (mixer_card->priv->name); mixer_card->priv->name = NULL; g_free (mixer_card->priv->icon_name); mixer_card->priv->icon_name = NULL; g_free (mixer_card->priv->target_profile); mixer_card->priv->target_profile = NULL; g_free (mixer_card->priv->profile); mixer_card->priv->profile = NULL; g_free (mixer_card->priv->human_profile); mixer_card->priv->human_profile = NULL; g_list_foreach (mixer_card->priv->profiles, (GFunc) free_profile, NULL); g_list_free (mixer_card->priv->profiles); mixer_card->priv->profiles = NULL; G_OBJECT_CLASS (gvc_mixer_card_parent_class)->finalize (object); } ./plugins/media-keys/gvc/gvc-channel-map.c0000644000004100000410000001661412735467744020672 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include "gvc-channel-map.h" #include "gvc-channel-map-private.h" #define GVC_CHANNEL_MAP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapPrivate)) struct GvcChannelMapPrivate { pa_channel_map pa_map; gboolean pa_volume_is_set; pa_cvolume pa_volume; gdouble extern_volume[NUM_TYPES]; /* volume, balance, fade, lfe */ gboolean can_balance; gboolean can_fade; }; enum { VOLUME_CHANGED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; static void gvc_channel_map_class_init (GvcChannelMapClass *klass); static void gvc_channel_map_init (GvcChannelMap *channel_map); static void gvc_channel_map_finalize (GObject *object); G_DEFINE_TYPE (GvcChannelMap, gvc_channel_map, G_TYPE_OBJECT) guint gvc_channel_map_get_num_channels (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), 0); if (!pa_channel_map_valid(&map->priv->pa_map)) return 0; return map->priv->pa_map.channels; } const gdouble * gvc_channel_map_get_volume (GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; map->priv->extern_volume[VOLUME] = (gdouble) pa_cvolume_max (&map->priv->pa_volume); if (gvc_channel_map_can_balance (map)) map->priv->extern_volume[BALANCE] = (gdouble) pa_cvolume_get_balance (&map->priv->pa_volume, &map->priv->pa_map); else map->priv->extern_volume[BALANCE] = 0; if (gvc_channel_map_can_fade (map)) map->priv->extern_volume[FADE] = (gdouble) pa_cvolume_get_fade (&map->priv->pa_volume, &map->priv->pa_map); else map->priv->extern_volume[FADE] = 0; if (gvc_channel_map_has_lfe (map)) map->priv->extern_volume[LFE] = (gdouble) pa_cvolume_get_position (&map->priv->pa_volume, &map->priv->pa_map, PA_CHANNEL_POSITION_LFE); else map->priv->extern_volume[LFE] = 0; return map->priv->extern_volume; } gboolean gvc_channel_map_can_balance (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE); return map->priv->can_balance; } gboolean gvc_channel_map_can_fade (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE); return map->priv->can_fade; } const char * gvc_channel_map_get_mapping (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; return pa_channel_map_to_pretty_name (&map->priv->pa_map); } /** * gvc_channel_map_has_position: (skip) * @map: * @position: * * Returns: */ gboolean gvc_channel_map_has_position (const GvcChannelMap *map, pa_channel_position_t position) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), FALSE); return pa_channel_map_has_position (&(map->priv->pa_map), position); } const pa_channel_map * gvc_channel_map_get_pa_channel_map (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; return &map->priv->pa_map; } const pa_cvolume * gvc_channel_map_get_cvolume (const GvcChannelMap *map) { g_return_val_if_fail (GVC_IS_CHANNEL_MAP (map), NULL); if (!pa_channel_map_valid(&map->priv->pa_map)) return NULL; return &map->priv->pa_volume; } static void gvc_channel_map_class_init (GvcChannelMapClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = gvc_channel_map_finalize; signals [VOLUME_CHANGED] = g_signal_new ("volume-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcChannelMapClass, volume_changed), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); g_type_class_add_private (klass, sizeof (GvcChannelMapPrivate)); } void gvc_channel_map_volume_changed (GvcChannelMap *map, const pa_cvolume *cv, gboolean set) { g_return_if_fail (GVC_IS_CHANNEL_MAP (map)); g_return_if_fail (cv != NULL); g_return_if_fail (pa_cvolume_compatible_with_channel_map(cv, &map->priv->pa_map)); if (pa_cvolume_equal(cv, &map->priv->pa_volume)) return; map->priv->pa_volume = *cv; if (map->priv->pa_volume_is_set == FALSE) { map->priv->pa_volume_is_set = TRUE; return; } g_signal_emit (map, signals[VOLUME_CHANGED], 0, set); } static void gvc_channel_map_init (GvcChannelMap *map) { map->priv = GVC_CHANNEL_MAP_GET_PRIVATE (map); map->priv->pa_volume_is_set = FALSE; } static void gvc_channel_map_finalize (GObject *object) { GvcChannelMap *channel_map; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_CHANNEL_MAP (object)); channel_map = GVC_CHANNEL_MAP (object); g_return_if_fail (channel_map->priv != NULL); G_OBJECT_CLASS (gvc_channel_map_parent_class)->finalize (object); } GvcChannelMap * gvc_channel_map_new (void) { GObject *map; map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL); return GVC_CHANNEL_MAP (map); } static void set_from_pa_map (GvcChannelMap *map, const pa_channel_map *pa_map) { g_assert (pa_channel_map_valid(pa_map)); map->priv->can_balance = pa_channel_map_can_balance (pa_map); map->priv->can_fade = pa_channel_map_can_fade (pa_map); map->priv->pa_map = *pa_map; pa_cvolume_set(&map->priv->pa_volume, pa_map->channels, PA_VOLUME_NORM); } GvcChannelMap * gvc_channel_map_new_from_pa_channel_map (const pa_channel_map *pa_map) { GObject *map; map = g_object_new (GVC_TYPE_CHANNEL_MAP, NULL); set_from_pa_map (GVC_CHANNEL_MAP (map), pa_map); return GVC_CHANNEL_MAP (map); } ./plugins/media-keys/gvc/gvc-mixer-control.c0000644000004100000410000037413512735467744021316 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2006-2008 Lennart Poettering * Copyright (C) 2008 Sjoerd Simons * Copyright (C) 2008 William Jon McCann * Copyright (C) 2012 Conor Curran * * 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 "config.h" #include #include #include #include #include #include #include #include #include "gvc-mixer-control.h" #include "gvc-mixer-sink.h" #include "gvc-mixer-source.h" #include "gvc-mixer-sink-input.h" #include "gvc-mixer-source-output.h" #include "gvc-mixer-event-role.h" #include "gvc-mixer-card.h" #include "gvc-mixer-card-private.h" #include "gvc-channel-map-private.h" #include "gvc-mixer-control-private.h" #include "gvc-mixer-ui-device.h" #define GVC_MIXER_CONTROL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlPrivate)) #define RECONNECT_DELAY 5 enum { PROP_0, PROP_NAME }; struct GvcMixerControlPrivate { pa_glib_mainloop *pa_mainloop; pa_mainloop_api *pa_api; pa_context *pa_context; int n_outstanding; guint reconnect_id; char *name; gboolean default_sink_is_set; guint default_sink_id; char *default_sink_name; gboolean default_source_is_set; guint default_source_id; char *default_source_name; gboolean event_sink_input_is_set; guint event_sink_input_id; GHashTable *all_streams; GHashTable *sinks; /* fixed outputs */ GHashTable *sources; /* fixed inputs */ GHashTable *sink_inputs; /* routable output streams */ GHashTable *source_outputs; /* routable input streams */ GHashTable *clients; GHashTable *cards; GvcMixerStream *new_default_sink_stream; /* new default sink stream, used in gvc_mixer_control_set_default_sink () */ GvcMixerStream *new_default_source_stream; /* new default source stream, used in gvc_mixer_control_set_default_source () */ GHashTable *ui_outputs; /* UI visible outputs */ GHashTable *ui_inputs; /* UI visible inputs */ /* When we change profile on a device that is not the server default sink, * it will jump back to the default sink set by the server to prevent the * audio setup from being 'outputless'. * * All well and good but then when we get the new stream created for the * new profile how do we know that this is the intended default or selected * device the user wishes to use. */ guint profile_swapping_device_id; GvcMixerControlState state; }; enum { STATE_CHANGED, STREAM_ADDED, STREAM_REMOVED, CARD_ADDED, CARD_REMOVED, DEFAULT_SINK_CHANGED, DEFAULT_SOURCE_CHANGED, ACTIVE_OUTPUT_UPDATE, ACTIVE_INPUT_UPDATE, OUTPUT_ADDED, INPUT_ADDED, OUTPUT_REMOVED, INPUT_REMOVED, CARD_INFO, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; static void gvc_mixer_control_class_init (GvcMixerControlClass *klass); static void gvc_mixer_control_init (GvcMixerControl *mixer_control); static void gvc_mixer_control_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerControl, gvc_mixer_control, G_TYPE_OBJECT) pa_context * gvc_mixer_control_get_pa_context (GvcMixerControl *control) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return control->priv->pa_context; } /** * gvc_mixer_control_get_event_sink_input: * @control: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_event_sink_input (GvcMixerControl *control) { GvcMixerStream *stream; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->event_sink_input_id)); return stream; } static void gvc_mixer_control_stream_restore_cb (pa_context *c, GvcMixerStream *new_stream, const pa_ext_stream_restore_info *info, GvcMixerControl *control) { pa_operation *o; pa_ext_stream_restore_info new_info; if (new_stream == NULL) return; new_info.name = info->name; new_info.channel_map = info->channel_map; new_info.volume = info->volume; new_info.mute = info->mute; new_info.device = gvc_mixer_stream_get_name (new_stream); o = pa_ext_stream_restore_write (control->priv->pa_context, PA_UPDATE_REPLACE, &new_info, 1, TRUE, NULL, NULL); if (o == NULL) { g_warning ("pa_ext_stream_restore_write() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return; } g_debug ("Changed default device for %s to %s", info->name, new_info.device); pa_operation_unref (o); } static void gvc_mixer_control_stream_restore_sink_cb (pa_context *c, const pa_ext_stream_restore_info *info, int eol, void *userdata) { GvcMixerControl *control = (GvcMixerControl *) userdata; if (eol || info == NULL || !g_str_has_prefix(info->name, "sink-input-by")) return; gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_sink_stream, info, control); } static void gvc_mixer_control_stream_restore_source_cb (pa_context *c, const pa_ext_stream_restore_info *info, int eol, void *userdata) { GvcMixerControl *control = (GvcMixerControl *) userdata; if (eol || info == NULL || !g_str_has_prefix(info->name, "source-output-by")) return; gvc_mixer_control_stream_restore_cb (c, control->priv->new_default_source_stream, info, control); } /** * gvc_mixer_control_lookup_device_from_stream: * @control: * @stream: * * Returns: (transfer none): a #GvcUIDevice or %NULL */ GvcMixerUIDevice * gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control, GvcMixerStream *stream) { GList *devices, *d; gboolean is_network_stream; const GList *ports; GvcMixerUIDevice *ret; if (GVC_IS_MIXER_SOURCE (stream)) devices = g_hash_table_get_values (control->priv->ui_inputs); else devices = g_hash_table_get_values (control->priv->ui_outputs); ret = NULL; ports = gvc_mixer_stream_get_ports (stream); is_network_stream = (ports == NULL); for (d = devices; d != NULL; d = d->next) { GvcMixerUIDevice *device = d->data; gint stream_id = G_MAXINT; g_object_get (G_OBJECT (device), "stream-id", &stream_id, NULL); if (is_network_stream && stream_id == gvc_mixer_stream_get_id (stream)) { g_debug ("lookup device from stream - %s - it is a network_stream ", gvc_mixer_ui_device_get_description (device)); ret = device; break; } else if (!is_network_stream) { const GvcMixerStreamPort *port; port = gvc_mixer_stream_get_port (stream); if (stream_id == gvc_mixer_stream_get_id (stream) && g_strcmp0 (gvc_mixer_ui_device_get_port (device), port->port) == 0) { g_debug ("lookup-device-from-stream found device: device description '%s', device port = '%s', device stream id %i AND stream port = '%s' stream id '%u' and stream description '%s'", gvc_mixer_ui_device_get_description (device), gvc_mixer_ui_device_get_port (device), stream_id, port->port, gvc_mixer_stream_get_id (stream), gvc_mixer_stream_get_description (stream)); ret = device; break; } } } g_debug ("gvc_mixer_control_lookup_device_from_stream - Could not find a device for stream '%s'",gvc_mixer_stream_get_description (stream)); g_list_free (devices); return ret; } gboolean gvc_mixer_control_set_default_sink (GvcMixerControl *control, GvcMixerStream *stream) { pa_operation *o; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_debug ("about to set default sink on server"); o = pa_context_set_default_sink (control->priv->pa_context, gvc_mixer_stream_get_name (stream), NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_default_sink() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return FALSE; } pa_operation_unref (o); control->priv->new_default_sink_stream = stream; g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_sink_stream); o = pa_ext_stream_restore_read (control->priv->pa_context, gvc_mixer_control_stream_restore_sink_cb, control); if (o == NULL) { g_warning ("pa_ext_stream_restore_read() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return FALSE; } pa_operation_unref (o); return TRUE; } gboolean gvc_mixer_control_set_default_source (GvcMixerControl *control, GvcMixerStream *stream) { GvcMixerUIDevice* input; pa_operation *o; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); o = pa_context_set_default_source (control->priv->pa_context, gvc_mixer_stream_get_name (stream), NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_default_source() failed"); return FALSE; } pa_operation_unref (o); control->priv->new_default_source_stream = stream; g_object_add_weak_pointer (G_OBJECT (stream), (gpointer *) &control->priv->new_default_source_stream); o = pa_ext_stream_restore_read (control->priv->pa_context, gvc_mixer_control_stream_restore_source_cb, control); if (o == NULL) { g_warning ("pa_ext_stream_restore_read() failed: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); return FALSE; } pa_operation_unref (o); /* source change successful, update the UI. */ input = gvc_mixer_control_lookup_device_from_stream (control, stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_INPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (input)); return TRUE; } /** * gvc_mixer_control_get_default_sink: * @control: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_default_sink (GvcMixerControl *control) { GvcMixerStream *stream; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); if (control->priv->default_sink_is_set) { stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->default_sink_id)); } else { stream = NULL; } return stream; } /** * gvc_mixer_control_get_default_source: * @control: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_default_source (GvcMixerControl *control) { GvcMixerStream *stream; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); if (control->priv->default_source_is_set) { stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->default_source_id)); } else { stream = NULL; } return stream; } static gpointer gvc_mixer_control_lookup_id (GHashTable *hash_table, guint id) { return g_hash_table_lookup (hash_table, GUINT_TO_POINTER (id)); } /** * gvc_mixer_control_lookup_stream_id: * @control: * @id: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_lookup_stream_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->all_streams, id); } /** * gvc_mixer_control_lookup_card_id: * @control: * @id: * * Returns: (transfer none): */ GvcMixerCard * gvc_mixer_control_lookup_card_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->cards, id); } /** * gvc_mixer_control_lookup_output_id: * @control: * @id: * * Returns: (transfer none): */ GvcMixerUIDevice * gvc_mixer_control_lookup_output_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->ui_outputs, id); } /** * gvc_mixer_control_lookup_input_id: * @control: * @id: * * Returns: (transfer none): */ GvcMixerUIDevice * gvc_mixer_control_lookup_input_id (GvcMixerControl *control, guint id) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); return gvc_mixer_control_lookup_id (control->priv->ui_inputs, id); } /** * gvc_mixer_control_get_stream_from_device: * @control: * @device: * * Returns: (transfer none): */ GvcMixerStream * gvc_mixer_control_get_stream_from_device (GvcMixerControl *control, GvcMixerUIDevice *device) { gint stream_id; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); g_return_val_if_fail (GVC_IS_MIXER_UI_DEVICE (device), NULL); stream_id = gvc_mixer_ui_device_get_stream_id (device); if (stream_id == GVC_MIXER_UI_DEVICE_INVALID) { g_debug ("gvc_mixer_control_get_stream_from_device - device has a null stream"); return NULL; } return gvc_mixer_control_lookup_stream_id (control, stream_id); } /** * gvc_mixer_control_change_profile_on_selected_device: * @control: * @device: * @profile: Can be null if any profile present on this port is okay * * Returns: This method will attempt to swap the profile on the card of * the device with given profile name. If successfull it will set the * preferred profile on that device so as we know the next time the user * moves to that device it should have this profile active. */ gboolean gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl *control, GvcMixerUIDevice *device, const gchar *profile) { const gchar *best_profile; GvcMixerCardProfile *current_profile; GvcMixerCard *card; g_object_get (G_OBJECT (device), "card", &card, NULL); current_profile = gvc_mixer_card_get_profile (card); if (current_profile) best_profile = gvc_mixer_ui_device_get_best_profile (device, profile, current_profile->profile); else best_profile = profile; g_assert (best_profile); g_debug ("Selected '%s', moving to profile '%s' on card '%s' on stream id %i", profile ? profile : "(any)", best_profile, gvc_mixer_card_get_name (card), gvc_mixer_ui_device_get_stream_id (device)); g_debug ("default sink name = %s and default sink id %u", control->priv->default_sink_name, control->priv->default_sink_id); control->priv->profile_swapping_device_id = gvc_mixer_ui_device_get_id (device); if (gvc_mixer_card_change_profile (card, best_profile)) { gvc_mixer_ui_device_set_user_preferred_profile (device, best_profile); return TRUE; } return FALSE; } /** * gvc_mixer_control_change_output: * @control: * @output: * This method is called from the UI when the user selects a previously unselected device. * - Firstly it queries the stream from the device. * - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources) * In the scenario of a NULL stream on the device * - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device. * - It then caches this device in control->priv->cached_desired_output_id so that when the update_sink triggered * from when we attempt to change profile we will know exactly what device to highlight on that stream. * - It attempts to swap the profile on the card from that device and returns. * - Next, it handles network or bluetooth streams that only require their stream to be made the default. * - Next it deals with port changes so if the stream's active port is not the same as the port on the device * it will attempt to change the port on that stream to be same as the device. If this fails it will return. * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active output device. */ void gvc_mixer_control_change_output (GvcMixerControl *control, GvcMixerUIDevice* output) { GvcMixerStream *stream; GvcMixerStream *default_stream; const GvcMixerStreamPort *active_port; const gchar *output_port; g_debug ("control change output"); stream = gvc_mixer_control_get_stream_from_device (control, output); if (stream == NULL) { gvc_mixer_control_change_profile_on_selected_device (control, output, NULL); return; } /* Handle a network sink as a portless or cardless device */ if (!gvc_mixer_ui_device_has_ports (output)) { g_debug ("Did we try to move to a software/bluetooth sink ?"); if (gvc_mixer_control_set_default_sink (control, stream)) { /* sink change was successful, update the UI.*/ g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } else { g_warning ("Failed to set default sink with stream from output %s", gvc_mixer_ui_device_get_description (output)); } return; } active_port = gvc_mixer_stream_get_port (stream); output_port = gvc_mixer_ui_device_get_port (output); /* First ensure the correct port is active on the sink */ if (g_strcmp0 (active_port->port, output_port) != 0) { g_debug ("Port change, switch to = %s", output_port); if (gvc_mixer_stream_change_port (stream, output_port) == FALSE) { g_warning ("Could not change port !"); return; } } default_stream = gvc_mixer_control_get_default_sink (control); /* Finally if we are not on the correct stream, swap over. */ if (stream != default_stream) { GvcMixerUIDevice* output; g_debug ("Attempting to swap over to stream %s ", gvc_mixer_stream_get_description (stream)); if (gvc_mixer_control_set_default_sink (control, stream)) { output = gvc_mixer_control_lookup_device_from_stream (control, stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } else { /* If the move failed for some reason reset the UI. */ output = gvc_mixer_control_lookup_device_from_stream (control, default_stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } } } /** * gvc_mixer_control_change_input: * @control: * @input: * This method is called from the UI when the user selects a previously unselected device. * - Firstly it queries the stream from the device. * - It assumes that if the stream is null that it cannot be a bluetooth or network stream (they never show unless they have valid sinks and sources) * In the scenario of a NULL stream on the device * - It fetches the device's preferred profile or if NUll the profile with the highest priority on that device. * - It then caches this device in control->priv->cached_desired_input_id so that when the update_source triggered * from when we attempt to change profile we will know exactly what device to highlight on that stream. * - It attempts to swap the profile on the card from that device and returns. * - Next, it handles network or bluetooth streams that only require their stream to be made the default. * - Next it deals with port changes so if the stream's active port is not the same as the port on the device * it will attempt to change the port on that stream to be same as the device. If this fails it will return. * - Finally it will set this new stream to be the default stream and emit a signal for the UI confirming the active input device. */ void gvc_mixer_control_change_input (GvcMixerControl *control, GvcMixerUIDevice* input) { GvcMixerStream *stream; GvcMixerStream *default_stream; const GvcMixerStreamPort *active_port; const gchar *input_port; stream = gvc_mixer_control_get_stream_from_device (control, input); if (stream == NULL) { gvc_mixer_control_change_profile_on_selected_device (control, input, NULL); return; } /* Handle a network sink as a portless/cardless device */ if (!gvc_mixer_ui_device_has_ports (input)) { g_debug ("Did we try to move to a software/bluetooth source ?"); if (! gvc_mixer_control_set_default_source (control, stream)) { g_warning ("Failed to set default source with stream from input %s", gvc_mixer_ui_device_get_description (input)); } return; } active_port = gvc_mixer_stream_get_port (stream); input_port = gvc_mixer_ui_device_get_port (input); /* First ensure the correct port is active on the sink */ if (g_strcmp0 (active_port->port, input_port) != 0) { g_debug ("Port change, switch to = %s", input_port); if (gvc_mixer_stream_change_port (stream, input_port) == FALSE) { g_warning ("Could not change port!"); return; } } default_stream = gvc_mixer_control_get_default_source (control); /* Finally if we are not on the correct stream, swap over. */ if (stream != default_stream) { g_debug ("change-input - attempting to swap over to stream %s", gvc_mixer_stream_get_description (stream)); gvc_mixer_control_set_default_source (control, stream); } } static void listify_hash_values_hfunc (gpointer key, gpointer value, gpointer user_data) { GSList **list = user_data; *list = g_slist_prepend (*list, value); } static int gvc_name_collate (const char *namea, const char *nameb) { if (nameb == NULL && namea == NULL) return 0; if (nameb == NULL) return 1; if (namea == NULL) return -1; return g_utf8_collate (namea, nameb); } static int gvc_card_collate (GvcMixerCard *a, GvcMixerCard *b) { const char *namea; const char *nameb; g_return_val_if_fail (a == NULL || GVC_IS_MIXER_CARD (a), 0); g_return_val_if_fail (b == NULL || GVC_IS_MIXER_CARD (b), 0); namea = gvc_mixer_card_get_name (a); nameb = gvc_mixer_card_get_name (b); return gvc_name_collate (namea, nameb); } /** * gvc_mixer_control_get_cards: * @control: * * Returns: (transfer container) (element-type Gvc.MixerCard): */ GSList * gvc_mixer_control_get_cards (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->cards, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_card_collate); } static int gvc_stream_collate (GvcMixerStream *a, GvcMixerStream *b) { const char *namea; const char *nameb; g_return_val_if_fail (a == NULL || GVC_IS_MIXER_STREAM (a), 0); g_return_val_if_fail (b == NULL || GVC_IS_MIXER_STREAM (b), 0); namea = gvc_mixer_stream_get_name (a); nameb = gvc_mixer_stream_get_name (b); return gvc_name_collate (namea, nameb); } /** * gvc_mixer_control_get_streams: * @control: * * Returns: (transfer container) (element-type Gvc.MixerStream): */ GSList * gvc_mixer_control_get_streams (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->all_streams, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_sinks: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSink): */ GSList * gvc_mixer_control_get_sinks (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->sinks, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_sources: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSource): */ GSList * gvc_mixer_control_get_sources (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->sources, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_sink_inputs: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSinkInput): */ GSList * gvc_mixer_control_get_sink_inputs (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->sink_inputs, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } /** * gvc_mixer_control_get_source_outputs: * @control: * * Returns: (transfer container) (element-type Gvc.MixerSourceOutput): */ GSList * gvc_mixer_control_get_source_outputs (GvcMixerControl *control) { GSList *retval; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), NULL); retval = NULL; g_hash_table_foreach (control->priv->source_outputs, listify_hash_values_hfunc, &retval); return g_slist_sort (retval, (GCompareFunc) gvc_stream_collate); } static void dec_outstanding (GvcMixerControl *control) { if (control->priv->n_outstanding <= 0) { return; } if (--control->priv->n_outstanding <= 0) { control->priv->state = GVC_STATE_READY; g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_READY); } } GvcMixerControlState gvc_mixer_control_get_state (GvcMixerControl *control) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); return control->priv->state; } static void on_default_source_port_notify (GObject *object, GParamSpec *pspec, GvcMixerControl *control) { char *port; GvcMixerUIDevice *input; g_object_get (object, "port", &port, NULL); input = gvc_mixer_control_lookup_device_from_stream (control, GVC_MIXER_STREAM (object)); g_debug ("on_default_source_port_notify - moved to port '%s' which SHOULD ?? correspond to output '%s'", port, gvc_mixer_ui_device_get_description (input)); g_signal_emit (G_OBJECT (control), signals[ACTIVE_INPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (input)); g_free (port); } static void _set_default_source (GvcMixerControl *control, GvcMixerStream *stream) { guint new_id; if (stream == NULL) { control->priv->default_source_id = 0; control->priv->default_source_is_set = FALSE; g_signal_emit (control, signals[DEFAULT_SOURCE_CHANGED], 0, PA_INVALID_INDEX); return; } new_id = gvc_mixer_stream_get_id (stream); if (control->priv->default_source_id != new_id) { GvcMixerUIDevice *input; control->priv->default_source_id = new_id; control->priv->default_source_is_set = TRUE; g_signal_emit (control, signals[DEFAULT_SOURCE_CHANGED], 0, new_id); if (control->priv->default_source_is_set) { g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_source (control), on_default_source_port_notify, control); } g_signal_connect (stream, "notify::port", G_CALLBACK (on_default_source_port_notify), control); input = gvc_mixer_control_lookup_device_from_stream (control, stream); g_signal_emit (G_OBJECT (control), signals[ACTIVE_INPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (input)); } } static void on_default_sink_port_notify (GObject *object, GParamSpec *pspec, GvcMixerControl *control) { char *port; GvcMixerUIDevice *output; g_object_get (object, "port", &port, NULL); output = gvc_mixer_control_lookup_device_from_stream (control, GVC_MIXER_STREAM (object)); if (output != NULL) { g_debug ("on_default_sink_port_notify - moved to port %s - which SHOULD correspond to output %s", port, gvc_mixer_ui_device_get_description (output)); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } g_free (port); } static void _set_default_sink (GvcMixerControl *control, GvcMixerStream *stream) { guint new_id; if (stream == NULL) { /* Don't tell front-ends about an unset default * sink if it's already unset */ if (control->priv->default_sink_is_set == FALSE) return; control->priv->default_sink_id = 0; control->priv->default_sink_is_set = FALSE; g_signal_emit (control, signals[DEFAULT_SINK_CHANGED], 0, PA_INVALID_INDEX); return; } new_id = gvc_mixer_stream_get_id (stream); if (control->priv->default_sink_id != new_id) { GvcMixerUIDevice *output; if (control->priv->default_sink_is_set) { g_signal_handlers_disconnect_by_func (gvc_mixer_control_get_default_sink (control), on_default_sink_port_notify, control); } control->priv->default_sink_id = new_id; control->priv->default_sink_is_set = TRUE; g_signal_emit (control, signals[DEFAULT_SINK_CHANGED], 0, new_id); g_signal_connect (stream, "notify::port", G_CALLBACK (on_default_sink_port_notify), control); output = gvc_mixer_control_lookup_device_from_stream (control, stream); g_debug ("active_sink change"); g_signal_emit (G_OBJECT (control), signals[ACTIVE_OUTPUT_UPDATE], 0, gvc_mixer_ui_device_get_id (output)); } } static gboolean _stream_has_name (gpointer key, GvcMixerStream *stream, const char *name) { const char *t_name; t_name = gvc_mixer_stream_get_name (stream); if (t_name != NULL && name != NULL && strcmp (t_name, name) == 0) { return TRUE; } return FALSE; } static GvcMixerStream * find_stream_for_name (GvcMixerControl *control, const char *name) { GvcMixerStream *stream; stream = g_hash_table_find (control->priv->all_streams, (GHRFunc)_stream_has_name, (char *)name); return stream; } static void update_default_source_from_name (GvcMixerControl *control, const char *name) { gboolean changed = FALSE; if ((control->priv->default_source_name == NULL && name != NULL) || (control->priv->default_source_name != NULL && name == NULL) || (name != NULL && strcmp (control->priv->default_source_name, name) != 0)) { changed = TRUE; } if (changed) { GvcMixerStream *stream; g_free (control->priv->default_source_name); control->priv->default_source_name = g_strdup (name); stream = find_stream_for_name (control, name); _set_default_source (control, stream); } } static void update_default_sink_from_name (GvcMixerControl *control, const char *name) { gboolean changed = FALSE; if ((control->priv->default_sink_name == NULL && name != NULL) || (control->priv->default_sink_name != NULL && name == NULL) || (name != NULL && strcmp (control->priv->default_sink_name, name) != 0)) { changed = TRUE; } if (changed) { GvcMixerStream *stream; g_free (control->priv->default_sink_name); control->priv->default_sink_name = g_strdup (name); stream = find_stream_for_name (control, name); _set_default_sink (control, stream); } } static void update_server (GvcMixerControl *control, const pa_server_info *info) { if (info->default_source_name != NULL) { update_default_source_from_name (control, info->default_source_name); } if (info->default_sink_name != NULL) { g_debug ("update server"); update_default_sink_from_name (control, info->default_sink_name); } } static void remove_stream (GvcMixerControl *control, GvcMixerStream *stream) { guint id; g_object_ref (stream); id = gvc_mixer_stream_get_id (stream); if (id == control->priv->default_sink_id) { _set_default_sink (control, NULL); } else if (id == control->priv->default_source_id) { _set_default_source (control, NULL); } g_hash_table_remove (control->priv->all_streams, GUINT_TO_POINTER (id)); g_signal_emit (G_OBJECT (control), signals[STREAM_REMOVED], 0, gvc_mixer_stream_get_id (stream)); g_object_unref (stream); } static void add_stream (GvcMixerControl *control, GvcMixerStream *stream) { g_hash_table_insert (control->priv->all_streams, GUINT_TO_POINTER (gvc_mixer_stream_get_id (stream)), stream); g_signal_emit (G_OBJECT (control), signals[STREAM_ADDED], 0, gvc_mixer_stream_get_id (stream)); } /* This method will match individual stream ports against its corresponding device * It does this by: * - iterates through our devices and finds the one where the card-id on the device is the same as the card-id on the stream * and the port-name on the device is the same as the streamport-name. * This should always find a match and is used exclusively by sync_devices(). */ static gboolean match_stream_with_devices (GvcMixerControl *control, GvcMixerStreamPort *stream_port, GvcMixerStream *stream) { GList *devices, *d; guint stream_card_id; guint stream_id; gboolean in_possession = FALSE; stream_id = gvc_mixer_stream_get_id (stream); stream_card_id = gvc_mixer_stream_get_card_index (stream); devices = g_hash_table_get_values (GVC_IS_MIXER_SOURCE (stream) ? control->priv->ui_inputs : control->priv->ui_outputs); for (d = devices; d != NULL; d = d->next) { GvcMixerUIDevice *device; gint device_stream_id; gchar *device_port_name; gchar *origin; gchar *description; GvcMixerCard *card; gint card_id; device = d->data; g_object_get (G_OBJECT (device), "stream-id", &device_stream_id, "card", &card, "origin", &origin, "description", &description, "port-name", &device_port_name, NULL); card_id = gvc_mixer_card_get_index (card); g_debug ("Attempt to match_stream update_with_existing_outputs - Try description : '%s', origin : '%s', device port name : '%s', card : %p, AGAINST stream port: '%s', sink card id %i", description, origin, device_port_name, card, stream_port->port, stream_card_id); if (stream_card_id == card_id && g_strcmp0 (device_port_name, stream_port->port) == 0) { g_debug ("Match device with stream: We have a match with description: '%s', origin: '%s', cached already with device id %u, so set stream id to %i", description, origin, gvc_mixer_ui_device_get_id (device), stream_id); g_object_set (G_OBJECT (device), "stream-id", (gint)stream_id, NULL); in_possession = TRUE; } g_free (device_port_name); g_free (origin); g_free (description); if (in_possession == TRUE) break; } g_list_free (devices); return in_possession; } /* * This method attempts to match a sink or source with its relevant UI device. * GvcMixerStream can represent both a sink or source. * Using static card port introspection implies that we know beforehand what * outputs and inputs are available to the user. * But that does not mean that all of these inputs and outputs are available to be used. * For instance we might be able to see that there is a HDMI port available but if * we are on the default analog stereo output profile there is no valid sink for * that HDMI device. We first need to change profile and when update_sink() is called * only then can we match the new hdmi sink with its corresponding device. * * Firstly it checks to see if the incoming stream has no ports. * - If a stream has no ports but has a valid card ID (bluetooth), it will attempt * to match the device with the stream using the card id. * - If a stream has no ports and no valid card id, it goes ahead and makes a new * device (software/network devices are only detectable at the sink/source level) * If the stream has ports it will match each port against the stream using match_stream_with_devices(). * * This method should always find a match. */ static void sync_devices (GvcMixerControl *control, GvcMixerStream* stream) { /* Go through ports to see what outputs can be created. */ const GList *stream_ports; const GList *n = NULL; gboolean is_output = !GVC_IS_MIXER_SOURCE (stream); gint stream_port_count = 0; stream_ports = gvc_mixer_stream_get_ports (stream); if (stream_ports == NULL) { GvcMixerUIDevice *device; /* Bluetooth, no ports but a valid card */ if (gvc_mixer_stream_get_card_index (stream) != PA_INVALID_INDEX) { GList *devices, *d; gboolean in_possession = FALSE; devices = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs); for (d = devices; d != NULL; d = d->next) { GvcMixerCard *card; gint card_id; device = d->data; g_object_get (G_OBJECT (device), "card", &card, NULL); card_id = gvc_mixer_card_get_index (card); g_debug ("sync devices, device description - '%s', device card id - %i, stream description - %s, stream card id - %i", gvc_mixer_ui_device_get_description (device), card_id, gvc_mixer_stream_get_description (stream), gvc_mixer_stream_get_card_index (stream)); if (card_id == gvc_mixer_stream_get_card_index (stream)) { in_possession = TRUE; break; } } g_list_free (devices); if (!in_possession) { g_warning ("Couldn't match the portless stream (with card) - '%s' is it an input ? -> %i, streams card id -> %i", gvc_mixer_stream_get_description (stream), GVC_IS_MIXER_SOURCE (stream), gvc_mixer_stream_get_card_index (stream)); return; } g_object_set (G_OBJECT (device), "stream-id", (gint)gvc_mixer_stream_get_id (stream), "description", gvc_mixer_stream_get_description (stream), "origin", "", /*Leave it empty for these special cases*/ "port-name", NULL, "port-available", TRUE, NULL); } else { /* Network sink/source has no ports and no card. */ GObject *object; object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "stream-id", (gint)gvc_mixer_stream_get_id (stream), "description", gvc_mixer_stream_get_description (stream), "origin", "", /* Leave it empty for these special cases */ "port-name", NULL, "port-available", TRUE, NULL); device = GVC_MIXER_UI_DEVICE (object); g_hash_table_insert (is_output ? control->priv->ui_outputs : control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device)), g_object_ref (device)); } g_signal_emit (G_OBJECT (control), signals[is_output ? OUTPUT_ADDED : INPUT_ADDED], 0, gvc_mixer_ui_device_get_id (device)); return; } /* Go ahead and make sure to match each port against a previously created device */ for (n = stream_ports; n != NULL; n = n->next) { GvcMixerStreamPort *stream_port; stream_port = n->data; stream_port_count ++; if (match_stream_with_devices (control, stream_port, stream)) continue; g_warning ("Sync_devices: Failed to match stream id: %u, description: '%s', origin: '%s'", gvc_mixer_stream_get_id (stream), stream_port->human_port, gvc_mixer_stream_get_description (stream)); } } static void set_icon_name_from_proplist (GvcMixerStream *stream, pa_proplist *l, const char *default_icon_name) { const char *t; if ((t = pa_proplist_gets (l, PA_PROP_DEVICE_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_WINDOW_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ICON_NAME))) { goto finish; } if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) { if (strcmp (t, "video") == 0 || strcmp (t, "phone") == 0) { goto finish; } if (strcmp (t, "music") == 0) { t = "audio"; goto finish; } if (strcmp (t, "game") == 0) { t = "applications-games"; goto finish; } if (strcmp (t, "event") == 0) { t = "dialog-information"; goto finish; } } t = default_icon_name; finish: gvc_mixer_stream_set_icon_name (stream, t); } /* * Called when anything changes with a sink. */ static void update_sink (GvcMixerControl *control, const pa_sink_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; GvcChannelMap *map; char map_buff[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_channel_map_snprint (map_buff, PA_CHANNEL_MAP_SNPRINT_MAX, &info->channel_map); #if 1 g_debug ("Updating sink: index=%u name='%s' description='%s' map='%s'", info->index, info->name, info->description, map_buff); #endif map = NULL; is_new = FALSE; stream = g_hash_table_lookup (control->priv->sinks, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GList *list = NULL; guint i; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_sink_new (control->priv->pa_context, info->index, map); for (i = 0; i < info->n_ports; i++) { GvcMixerStreamPort *port; port = g_slice_new0 (GvcMixerStreamPort); port->port = g_strdup (info->ports[i]->name); port->human_port = g_strdup (info->ports[i]->description); port->priority = info->ports[i]->priority; port->available = info->ports[i]->available != PA_PORT_AVAILABLE_NO; list = g_list_prepend (list, port); } gvc_mixer_stream_set_ports (stream, list); g_object_unref (map); is_new = TRUE; } else if (gvc_mixer_stream_is_running (stream)) { /* Ignore events if volume changes are outstanding */ g_debug ("Ignoring event, volume changes are outstanding"); return; } max_volume = pa_cvolume_max (&info->volume); gvc_mixer_stream_set_name (stream, info->name); gvc_mixer_stream_set_card_index (stream, info->card); gvc_mixer_stream_set_description (stream, info->description); set_icon_name_from_proplist (stream, info->proplist, "audio-card"); gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR)); gvc_mixer_stream_set_sysfs_path (stream, pa_proplist_gets (info->proplist, "sysfs.path")); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SINK_DECIBEL_VOLUME)); gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume); /* Messy I know but to set the port everytime regardless of whether it has changed will cost us a * port change notify signal which causes the frontend to resync. * Only update the UI when something has changed. */ if (info->active_port != NULL) { if (is_new) gvc_mixer_stream_set_port (stream, info->active_port->name); else { const GvcMixerStreamPort *active_port; active_port = gvc_mixer_stream_get_port (stream); if (active_port == NULL || g_strcmp0 (active_port->port, info->active_port->name) != 0) { g_debug ("update sink - apparently a port update"); gvc_mixer_stream_set_port (stream, info->active_port->name); } } } if (is_new) { g_debug ("update sink - is new"); g_hash_table_insert (control->priv->sinks, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); /* Always sink on a new stream to able to assign the right stream id * to the appropriate outputs (multiple potential outputs per stream). */ sync_devices (control, stream); } /* * When we change profile on a device that is not the server default sink, * it will jump back to the default sink set by the server to prevent the audio setup from being 'outputless'. * All well and good but then when we get the new stream created for the new profile how do we know * that this is the intended default or selected device the user wishes to use. * This is messy but it's the only reliable way that it can be done without ripping the whole thing apart. */ if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) { GvcMixerUIDevice *dev = NULL; dev = gvc_mixer_control_lookup_output_id (control, control->priv->profile_swapping_device_id); if (dev != NULL) { /* now check to make sure this new stream is the same stream just matched and set on the device object */ if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) { g_debug ("Looks like we profile swapped on a non server default sink"); gvc_mixer_control_set_default_sink (control, stream); } } control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID; } if (control->priv->default_sink_name != NULL && info->name != NULL && strcmp (control->priv->default_sink_name, info->name) == 0) { _set_default_sink (control, stream); } if (map == NULL) map = (GvcChannelMap *) gvc_mixer_stream_get_channel_map (stream); gvc_channel_map_volume_changed (map, &info->volume, FALSE); } static void update_source (GvcMixerControl *control, const pa_source_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; #if 1 g_debug ("Updating source: index=%u name='%s' description='%s'", info->index, info->name, info->description); #endif /* completely ignore monitors, they're not real sources */ if (info->monitor_of_sink != PA_INVALID_INDEX) { return; } is_new = FALSE; stream = g_hash_table_lookup (control->priv->sources, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GList *list = NULL; guint i; GvcChannelMap *map; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_source_new (control->priv->pa_context, info->index, map); for (i = 0; i < info->n_ports; i++) { GvcMixerStreamPort *port; port = g_slice_new0 (GvcMixerStreamPort); port->port = g_strdup (info->ports[i]->name); port->human_port = g_strdup (info->ports[i]->description); port->priority = info->ports[i]->priority; list = g_list_prepend (list, port); } gvc_mixer_stream_set_ports (stream, list); g_object_unref (map); is_new = TRUE; } else if (gvc_mixer_stream_is_running (stream)) { /* Ignore events if volume changes are outstanding */ g_debug ("Ignoring event, volume changes are outstanding"); return; } max_volume = pa_cvolume_max (&info->volume); gvc_mixer_stream_set_name (stream, info->name); gvc_mixer_stream_set_card_index (stream, info->card); gvc_mixer_stream_set_description (stream, info->description); set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone"); gvc_mixer_stream_set_form_factor (stream, pa_proplist_gets (info->proplist, PA_PROP_DEVICE_FORM_FACTOR)); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); gvc_mixer_stream_set_can_decibel (stream, !!(info->flags & PA_SOURCE_DECIBEL_VOLUME)); gvc_mixer_stream_set_base_volume (stream, (guint32) info->base_volume); g_debug ("update source"); if (info->active_port != NULL) { if (is_new) gvc_mixer_stream_set_port (stream, info->active_port->name); else { const GvcMixerStreamPort *active_port; active_port = gvc_mixer_stream_get_port (stream); if (active_port == NULL || g_strcmp0 (active_port->port, info->active_port->name) != 0) { g_debug ("update source - apparently a port update"); gvc_mixer_stream_set_port (stream, info->active_port->name); } } } if (is_new) { g_hash_table_insert (control->priv->sources, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); sync_devices (control, stream); } if (control->priv->profile_swapping_device_id != GVC_MIXER_UI_DEVICE_INVALID) { GvcMixerUIDevice *dev = NULL; dev = gvc_mixer_control_lookup_input_id (control, control->priv->profile_swapping_device_id); if (dev != NULL) { /* now check to make sure this new stream is the same stream just matched and set on the device object */ if (gvc_mixer_ui_device_get_stream_id (dev) == gvc_mixer_stream_get_id (stream)) { g_debug ("Looks like we profile swapped on a non server default sink"); gvc_mixer_control_set_default_source (control, stream); } } control->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID; } if (control->priv->default_source_name != NULL && info->name != NULL && strcmp (control->priv->default_source_name, info->name) == 0) { _set_default_source (control, stream); } } static void set_is_event_stream_from_proplist (GvcMixerStream *stream, pa_proplist *l) { const char *t; gboolean is_event_stream; is_event_stream = FALSE; if ((t = pa_proplist_gets (l, PA_PROP_MEDIA_ROLE))) { if (g_str_equal (t, "event")) is_event_stream = TRUE; } gvc_mixer_stream_set_is_event_stream (stream, is_event_stream); } static void set_application_id_from_proplist (GvcMixerStream *stream, pa_proplist *l) { const char *t; if ((t = pa_proplist_gets (l, PA_PROP_APPLICATION_ID))) { gvc_mixer_stream_set_application_id (stream, t); } } static void update_sink_input (GvcMixerControl *control, const pa_sink_input_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; const char *name; #if 0 g_debug ("Updating sink input: index=%u name='%s' client=%u sink=%u", info->index, info->name, info->client, info->sink); #endif is_new = FALSE; stream = g_hash_table_lookup (control->priv->sink_inputs, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GvcChannelMap *map; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_sink_input_new (control->priv->pa_context, info->index, map); g_object_unref (map); is_new = TRUE; } else if (gvc_mixer_stream_is_running (stream)) { /* Ignore events if volume changes are outstanding */ g_debug ("Ignoring event, volume changes are outstanding"); return; } max_volume = pa_cvolume_max (&info->volume); name = (const char *)g_hash_table_lookup (control->priv->clients, GUINT_TO_POINTER (info->client)); gvc_mixer_stream_set_name (stream, name); gvc_mixer_stream_set_description (stream, info->name); set_application_id_from_proplist (stream, info->proplist); set_is_event_stream_from_proplist (stream, info->proplist); set_icon_name_from_proplist (stream, info->proplist, "applications-multimedia"); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); gvc_mixer_stream_set_is_virtual (stream, info->client == PA_INVALID_INDEX); if (is_new) { g_hash_table_insert (control->priv->sink_inputs, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); } } static void update_source_output (GvcMixerControl *control, const pa_source_output_info *info) { GvcMixerStream *stream; gboolean is_new; const char *name; #if 1 g_debug ("Updating source output: index=%u name='%s' client=%u source=%u", info->index, info->name, info->client, info->source); #endif is_new = FALSE; stream = g_hash_table_lookup (control->priv->source_outputs, GUINT_TO_POINTER (info->index)); if (stream == NULL) { GvcChannelMap *map; map = gvc_channel_map_new_from_pa_channel_map (&info->channel_map); stream = gvc_mixer_source_output_new (control->priv->pa_context, info->index, map); g_object_unref (map); is_new = TRUE; } name = (const char *)g_hash_table_lookup (control->priv->clients, GUINT_TO_POINTER (info->client)); gvc_mixer_stream_set_name (stream, name); gvc_mixer_stream_set_description (stream, info->name); set_application_id_from_proplist (stream, info->proplist); set_is_event_stream_from_proplist (stream, info->proplist); set_icon_name_from_proplist (stream, info->proplist, "audio-input-microphone"); if (is_new) { g_hash_table_insert (control->priv->source_outputs, GUINT_TO_POINTER (info->index), g_object_ref (stream)); add_stream (control, stream); } } static void update_client (GvcMixerControl *control, const pa_client_info *info) { #if 1 g_debug ("Updating client: index=%u name='%s'", info->index, info->name); #endif g_hash_table_insert (control->priv->clients, GUINT_TO_POINTER (info->index), g_strdup (info->name)); } static char * card_num_streams_to_status (guint sinks, guint sources) { char *sinks_str; char *sources_str; char *ret; if (sinks == 0 && sources == 0) { /* translators: * The device has been disabled */ return g_strdup (_("Disabled")); } if (sinks == 0) { sinks_str = NULL; } else { /* translators: * The number of sound outputs on a particular device */ sinks_str = g_strdup_printf (ngettext ("%u Output", "%u Outputs", sinks), sinks); } if (sources == 0) { sources_str = NULL; } else { /* translators: * The number of sound inputs on a particular device */ sources_str = g_strdup_printf (ngettext ("%u Input", "%u Inputs", sources), sources); } if (sources_str == NULL) return sinks_str; if (sinks_str == NULL) return sources_str; ret = g_strdup_printf ("%s / %s", sinks_str, sources_str); g_free (sinks_str); g_free (sources_str); return ret; } /* * A utility method to gather which card profiles are relevant to the port . */ static GList * determine_profiles_for_port (pa_card_port_info *port, GList* card_profiles) { gint i; GList *supported_profiles = NULL; GList *p; for (i = 0; i < port->n_profiles; i++) { for (p = card_profiles; p != NULL; p = p->next) { GvcMixerCardProfile *prof; prof = p->data; if (g_strcmp0 (port->profiles[i]->name, prof->profile) == 0) supported_profiles = g_list_append (supported_profiles, prof); } } g_debug ("%i profiles supported on port %s", g_list_length (supported_profiles), port->description); return g_list_sort (supported_profiles, (GCompareFunc) gvc_mixer_card_profile_compare); } static gboolean is_card_port_an_output (GvcMixerCardPort* port) { return port->direction == PA_DIRECTION_OUTPUT ? TRUE : FALSE; } /* * This method will create a ui device for the given port. */ static void create_ui_device_from_port (GvcMixerControl* control, GvcMixerCardPort* port, GvcMixerCard* card) { GvcMixerUIDeviceDirection direction; GObject *object; GvcMixerUIDevice *uidevice; gboolean available = port->available != PA_PORT_AVAILABLE_NO; direction = (is_card_port_an_output (port) == TRUE) ? UIDeviceOutput : UIDeviceInput; object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "type", (uint)direction, "card", card, "port-name", port->port, "description", port->human_port, "origin", gvc_mixer_card_get_name (card), "port-available", available, NULL); uidevice = GVC_MIXER_UI_DEVICE (object); gvc_mixer_ui_device_set_profiles (uidevice, port->profiles); g_hash_table_insert (is_card_port_an_output (port) ? control->priv->ui_outputs : control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (uidevice)), g_object_ref (uidevice)); if (available) { g_signal_emit (G_OBJECT (control), signals[is_card_port_an_output (port) ? OUTPUT_ADDED : INPUT_ADDED], 0, gvc_mixer_ui_device_get_id (uidevice)); } g_debug ("create_ui_device_from_port, direction %u, description '%s', origin '%s', port available %i", direction, port->human_port, gvc_mixer_card_get_name (card), available); } /* * This method will match up GvcMixerCardPorts with existing devices. * A match is achieved if the device's card-id and the port's card-id are the same * && the device's port-name and the card-port's port member are the same. * A signal is then sent adding or removing that device from the UI depending on the availability of the port. */ static void match_card_port_with_existing_device (GvcMixerControl *control, GvcMixerCardPort *card_port, GvcMixerCard *card, gboolean available) { GList *d; GList *devices; GvcMixerUIDevice *device; gboolean is_output = is_card_port_an_output (card_port); devices = g_hash_table_get_values (is_output ? control->priv->ui_outputs : control->priv->ui_inputs); for (d = devices; d != NULL; d = d->next) { GvcMixerCard *device_card; gchar *device_port_name; device = d->data; g_object_get (G_OBJECT (device), "card", &device_card, "port-name", &device_port_name, NULL); if (g_strcmp0 (card_port->port, device_port_name) == 0 && device_card == card) { g_debug ("Found the relevant device %s, update its port availability flag to %i, is_output %i", device_port_name, available, is_output); g_object_set (G_OBJECT (device), "port-available", available, NULL); g_signal_emit (G_OBJECT (control), is_output ? signals[available ? OUTPUT_ADDED : OUTPUT_REMOVED] : signals[available ? INPUT_ADDED : INPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); } g_free (device_port_name); } g_list_free (devices); } static void create_ui_device_from_card (GvcMixerControl *control, GvcMixerCard *card) { GObject *object; GvcMixerUIDevice *in; GvcMixerUIDevice *out; const GList *profiles; /* For now just create two devices and presume this device is multi directional * Ensure to remove both on card removal (available to false by default) */ profiles = gvc_mixer_card_get_profiles (card); g_debug ("Portless card just registered - %i", gvc_mixer_card_get_index (card)); object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "type", UIDeviceInput, "description", gvc_mixer_card_get_name (card), "origin", "", /* Leave it empty for these special cases */ "port-name", NULL, "port-available", FALSE, "card", card, NULL); in = GVC_MIXER_UI_DEVICE (object); gvc_mixer_ui_device_set_profiles (in, profiles); g_hash_table_insert (control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (in)), g_object_ref (in)); object = g_object_new (GVC_TYPE_MIXER_UI_DEVICE, "type", UIDeviceOutput, "description", gvc_mixer_card_get_name (card), "origin", "", /* Leave it empty for these special cases */ "port-name", NULL, "port-available", FALSE, "card", card, NULL); out = GVC_MIXER_UI_DEVICE (object); gvc_mixer_ui_device_set_profiles (out, profiles); g_hash_table_insert (control->priv->ui_outputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (out)), g_object_ref (out)); } /* * At this point we can determine all devices available to us (besides network 'ports') * This is done by the following: * * - gvc_mixer_card and gvc_mixer_card_ports are created and relevant setters are called. * - First it checks to see if it's a portless card. Bluetooth devices are portless AFAIHS. * If so it creates two devices, an input and an output. * - If it's a 'normal' card with ports it will create a new ui-device or * synchronise port availability with the existing device cached for that port on this card. */ static void update_card (GvcMixerControl *control, const pa_card_info *info) { const GList *card_ports = NULL; const GList *m = NULL; GvcMixerCard *card; gboolean is_new = FALSE; #if 1 guint i; const char *key; void *state; g_debug ("Udpating card %s (index: %u driver: %s):", info->name, info->index, info->driver); for (i = 0; i < info->n_profiles; i++) { struct pa_card_profile_info pi = info->profiles[i]; gboolean is_default; is_default = (g_strcmp0 (pi.name, info->active_profile->name) == 0); g_debug ("\tProfile '%s': %d sources %d sinks%s", pi.name, pi.n_sources, pi.n_sinks, is_default ? " (Current)" : ""); } state = NULL; key = pa_proplist_iterate (info->proplist, &state); while (key != NULL) { g_debug ("\tProperty: '%s' = '%s'", key, pa_proplist_gets (info->proplist, key)); key = pa_proplist_iterate (info->proplist, &state); } #endif card = g_hash_table_lookup (control->priv->cards, GUINT_TO_POINTER (info->index)); if (card == NULL) { GList *profile_list = NULL; GList *port_list = NULL; for (i = 0; i < info->n_profiles; i++) { GvcMixerCardProfile *profile; struct pa_card_profile_info pi = info->profiles[i]; profile = g_new0 (GvcMixerCardProfile, 1); profile->profile = g_strdup (pi.name); profile->human_profile = g_strdup (pi.description); profile->status = card_num_streams_to_status (pi.n_sinks, pi.n_sources); profile->n_sinks = pi.n_sinks; profile->n_sources = pi.n_sources; profile->priority = pi.priority; profile_list = g_list_prepend (profile_list, profile); } card = gvc_mixer_card_new (control->priv->pa_context, info->index); gvc_mixer_card_set_profiles (card, profile_list); for (i = 0; i < info->n_ports; i++) { GvcMixerCardPort *port; port = g_new0 (GvcMixerCardPort, 1); port->port = g_strdup (info->ports[i]->name); port->human_port = g_strdup (info->ports[i]->description); port->priority = info->ports[i]->priority; port->available = info->ports[i]->available; port->direction = info->ports[i]->direction; port->profiles = determine_profiles_for_port (info->ports[i], profile_list); port_list = g_list_prepend (port_list, port); } gvc_mixer_card_set_ports (card, port_list); is_new = TRUE; } gvc_mixer_card_set_name (card, pa_proplist_gets (info->proplist, "device.description")); gvc_mixer_card_set_icon_name (card, pa_proplist_gets (info->proplist, "device.icon_name")); gvc_mixer_card_set_profile (card, info->active_profile->name); if (is_new) { g_hash_table_insert (control->priv->cards, GUINT_TO_POINTER (info->index), g_object_ref (card)); } card_ports = gvc_mixer_card_get_ports (card); if (card_ports == NULL && is_new) { g_debug ("Portless card just registered - %s", gvc_mixer_card_get_name (card)); create_ui_device_from_card (control, card); } for (m = card_ports; m != NULL; m = m->next) { GvcMixerCardPort *card_port; card_port = m->data; if (is_new) create_ui_device_from_port (control, card_port, card); else { for (i = 0; i < info->n_ports; i++) { if (g_strcmp0 (card_port->port, info->ports[i]->name) == 0) { if (card_port->available != info->ports[i]->available) { card_port->available = info->ports[i]->available; g_debug ("sync port availability on card %i, card port name '%s', new available value %i", gvc_mixer_card_get_index (card), card_port->port, card_port->available); match_card_port_with_existing_device (control, card_port, card, card_port->available != PA_PORT_AVAILABLE_NO); } } } } } g_signal_emit (G_OBJECT (control), signals[CARD_INFO], 0, info); g_signal_emit (G_OBJECT (control), signals[CARD_ADDED], 0, info->index); } static void _pa_context_get_sink_info_cb (pa_context *context, const pa_sink_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Sink callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_sink (control, i); } static void _pa_context_get_source_info_cb (pa_context *context, const pa_source_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Source callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_source (control, i); } static void _pa_context_get_sink_input_info_cb (pa_context *context, const pa_sink_input_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Sink input callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_sink_input (control, i); } static void _pa_context_get_source_output_info_cb (pa_context *context, const pa_source_output_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Source output callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_source_output (control, i); } static void _pa_context_get_client_info_cb (pa_context *context, const pa_client_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) { return; } g_warning ("Client callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_client (control, i); } static void _pa_context_get_card_info_by_index_cb (pa_context *context, const pa_card_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { if (pa_context_errno (context) == PA_ERR_NOENTITY) return; g_warning ("Card callback failure"); return; } if (eol > 0) { dec_outstanding (control); return; } update_card (control, i); } static void _pa_context_get_server_info_cb (pa_context *context, const pa_server_info *i, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (i == NULL) { g_warning ("Server info callback failure"); return; } g_debug ("get server info"); update_server (control, i); dec_outstanding (control); } static void remove_event_role_stream (GvcMixerControl *control) { g_debug ("Removing event role"); } static void update_event_role_stream (GvcMixerControl *control, const pa_ext_stream_restore_info *info) { GvcMixerStream *stream; gboolean is_new; pa_volume_t max_volume; if (strcmp (info->name, "sink-input-by-media-role:event") != 0) { return; } #if 0 g_debug ("Updating event role: name='%s' device='%s'", info->name, info->device); #endif is_new = FALSE; if (!control->priv->event_sink_input_is_set) { pa_channel_map pa_map; GvcChannelMap *map; pa_map.channels = 1; pa_map.map[0] = PA_CHANNEL_POSITION_MONO; map = gvc_channel_map_new_from_pa_channel_map (&pa_map); stream = gvc_mixer_event_role_new (control->priv->pa_context, info->device, map); control->priv->event_sink_input_id = gvc_mixer_stream_get_id (stream); control->priv->event_sink_input_is_set = TRUE; is_new = TRUE; } else { stream = g_hash_table_lookup (control->priv->all_streams, GUINT_TO_POINTER (control->priv->event_sink_input_id)); } max_volume = pa_cvolume_max (&info->volume); gvc_mixer_stream_set_name (stream, _("System Sounds")); gvc_mixer_stream_set_icon_name (stream, "multimedia-volume-control"); gvc_mixer_stream_set_volume (stream, (guint)max_volume); gvc_mixer_stream_set_is_muted (stream, info->mute); if (is_new) { add_stream (control, stream); } } static void _pa_ext_stream_restore_read_cb (pa_context *context, const pa_ext_stream_restore_info *i, int eol, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); if (eol < 0) { g_debug ("Failed to initialized stream_restore extension: %s", pa_strerror (pa_context_errno (context))); remove_event_role_stream (control); return; } if (eol > 0) { dec_outstanding (control); /* If we don't have an event stream to restore, then * set one up with a default 100% volume */ if (!control->priv->event_sink_input_is_set) { pa_ext_stream_restore_info info; memset (&info, 0, sizeof(info)); info.name = "sink-input-by-media-role:event"; info.volume.channels = 1; info.volume.values[0] = PA_VOLUME_NORM; update_event_role_stream (control, &info); } return; } update_event_role_stream (control, i); } static void _pa_ext_stream_restore_subscribe_cb (pa_context *context, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); pa_operation *o; o = pa_ext_stream_restore_read (context, _pa_ext_stream_restore_read_cb, control); if (o == NULL) { g_warning ("pa_ext_stream_restore_read() failed"); return; } pa_operation_unref (o); } static void req_update_server_info (GvcMixerControl *control, int index) { pa_operation *o; o = pa_context_get_server_info (control->priv->pa_context, _pa_context_get_server_info_cb, control); if (o == NULL) { g_warning ("pa_context_get_server_info() failed"); return; } pa_operation_unref (o); } static void req_update_client_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_client_info_list (control->priv->pa_context, _pa_context_get_client_info_cb, control); } else { o = pa_context_get_client_info (control->priv->pa_context, index, _pa_context_get_client_info_cb, control); } if (o == NULL) { g_warning ("pa_context_client_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_card (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_card_info_list (control->priv->pa_context, _pa_context_get_card_info_by_index_cb, control); } else { o = pa_context_get_card_info_by_index (control->priv->pa_context, index, _pa_context_get_card_info_by_index_cb, control); } if (o == NULL) { g_warning ("pa_context_get_card_info_by_index() failed"); return; } pa_operation_unref (o); } static void req_update_sink_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_sink_info_list (control->priv->pa_context, _pa_context_get_sink_info_cb, control); } else { o = pa_context_get_sink_info_by_index (control->priv->pa_context, index, _pa_context_get_sink_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_sink_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_source_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_source_info_list (control->priv->pa_context, _pa_context_get_source_info_cb, control); } else { o = pa_context_get_source_info_by_index(control->priv->pa_context, index, _pa_context_get_source_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_source_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_sink_input_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_sink_input_info_list (control->priv->pa_context, _pa_context_get_sink_input_info_cb, control); } else { o = pa_context_get_sink_input_info (control->priv->pa_context, index, _pa_context_get_sink_input_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_sink_input_info_list() failed"); return; } pa_operation_unref (o); } static void req_update_source_output_info (GvcMixerControl *control, int index) { pa_operation *o; if (index < 0) { o = pa_context_get_source_output_info_list (control->priv->pa_context, _pa_context_get_source_output_info_cb, control); } else { o = pa_context_get_source_output_info (control->priv->pa_context, index, _pa_context_get_source_output_info_cb, control); } if (o == NULL) { g_warning ("pa_context_get_source_output_info_list() failed"); return; } pa_operation_unref (o); } static void remove_client (GvcMixerControl *control, guint index) { g_hash_table_remove (control->priv->clients, GUINT_TO_POINTER (index)); } static void remove_card (GvcMixerControl *control, guint index) { GList *devices, *d; devices = g_list_concat (g_hash_table_get_values (control->priv->ui_inputs), g_hash_table_get_values (control->priv->ui_outputs)); for (d = devices; d != NULL; d = d->next) { GvcMixerCard *card; GvcMixerUIDevice *device = d->data; g_object_get (G_OBJECT (device), "card", &card, NULL); if (gvc_mixer_card_get_index (card) == index) { g_signal_emit (G_OBJECT (control), signals[gvc_mixer_ui_device_is_output (device) ? OUTPUT_REMOVED : INPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); g_debug ("Card removal remove device %s", gvc_mixer_ui_device_get_description (device)); g_hash_table_remove (gvc_mixer_ui_device_is_output (device) ? control->priv->ui_outputs : control->priv->ui_inputs, GUINT_TO_POINTER (gvc_mixer_ui_device_get_id (device))); } } g_list_free (devices); g_hash_table_remove (control->priv->cards, GUINT_TO_POINTER (index)); g_signal_emit (G_OBJECT (control), signals[CARD_REMOVED], 0, index); } static void remove_sink (GvcMixerControl *control, guint index) { GvcMixerStream *stream; GvcMixerUIDevice *device; g_debug ("Removing sink: index=%u", index); stream = g_hash_table_lookup (control->priv->sinks, GUINT_TO_POINTER (index)); if (stream == NULL) return; device = gvc_mixer_control_lookup_device_from_stream (control, stream); if (device != NULL) { gvc_mixer_ui_device_invalidate_stream (device); if (!gvc_mixer_ui_device_has_ports (device)) { g_signal_emit (G_OBJECT (control), signals[OUTPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); } else { GList *devices, *d; devices = g_hash_table_get_values (control->priv->ui_outputs); for (d = devices; d != NULL; d = d->next) { gint stream_id = GVC_MIXER_UI_DEVICE_INVALID; device = d->data; g_object_get (G_OBJECT (device), "stream-id", &stream_id, NULL); if (stream_id == gvc_mixer_stream_get_id (stream)) gvc_mixer_ui_device_invalidate_stream (device); } g_list_free (devices); } } g_hash_table_remove (control->priv->sinks, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void remove_source (GvcMixerControl *control, guint index) { GvcMixerStream *stream; GvcMixerUIDevice *device; g_debug ("Removing source: index=%u", index); stream = g_hash_table_lookup (control->priv->sources, GUINT_TO_POINTER (index)); if (stream == NULL) return; device = gvc_mixer_control_lookup_device_from_stream (control, stream); if (device != NULL) { gvc_mixer_ui_device_invalidate_stream (device); if (!gvc_mixer_ui_device_has_ports (device)) { g_signal_emit (G_OBJECT (control), signals[INPUT_REMOVED], 0, gvc_mixer_ui_device_get_id (device)); } else { GList *devices, *d; devices = g_hash_table_get_values (control->priv->ui_inputs); for (d = devices; d != NULL; d = d->next) { gint stream_id = GVC_MIXER_UI_DEVICE_INVALID; device = d->data; g_object_get (G_OBJECT (device), "stream-id", &stream_id, NULL); if (stream_id == gvc_mixer_stream_get_id (stream)) gvc_mixer_ui_device_invalidate_stream (device); } g_list_free (devices); } } g_hash_table_remove (control->priv->sources, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void remove_sink_input (GvcMixerControl *control, guint index) { GvcMixerStream *stream; g_debug ("Removing sink input: index=%u", index); stream = g_hash_table_lookup (control->priv->sink_inputs, GUINT_TO_POINTER (index)); if (stream == NULL) { return; } g_hash_table_remove (control->priv->sink_inputs, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void remove_source_output (GvcMixerControl *control, guint index) { GvcMixerStream *stream; g_debug ("Removing source output: index=%u", index); stream = g_hash_table_lookup (control->priv->source_outputs, GUINT_TO_POINTER (index)); if (stream == NULL) { return; } g_hash_table_remove (control->priv->source_outputs, GUINT_TO_POINTER (index)); remove_stream (control, stream); } static void _pa_context_subscribe_cb (pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { case PA_SUBSCRIPTION_EVENT_SINK: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_sink (control, index); } else { req_update_sink_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SOURCE: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_source (control, index); } else { req_update_source_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_sink_input (control, index); } else { req_update_sink_input_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_source_output (control, index); } else { req_update_source_output_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_CLIENT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_client (control, index); } else { req_update_client_info (control, index); } break; case PA_SUBSCRIPTION_EVENT_SERVER: req_update_server_info (control, index); break; case PA_SUBSCRIPTION_EVENT_CARD: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { remove_card (control, index); } else { req_update_card (control, index); } break; } } static void gvc_mixer_control_ready (GvcMixerControl *control) { pa_operation *o; pa_context_set_subscribe_callback (control->priv->pa_context, _pa_context_subscribe_cb, control); o = pa_context_subscribe (control->priv->pa_context, (pa_subscription_mask_t) (PA_SUBSCRIPTION_MASK_SINK| PA_SUBSCRIPTION_MASK_SOURCE| PA_SUBSCRIPTION_MASK_SINK_INPUT| PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT| PA_SUBSCRIPTION_MASK_CLIENT| PA_SUBSCRIPTION_MASK_SERVER| PA_SUBSCRIPTION_MASK_CARD), NULL, NULL); if (o == NULL) { g_warning ("pa_context_subscribe() failed"); return; } pa_operation_unref (o); req_update_server_info (control, -1); req_update_card (control, -1); req_update_client_info (control, -1); req_update_sink_info (control, -1); req_update_source_info (control, -1); req_update_sink_input_info (control, -1); req_update_source_output_info (control, -1); control->priv->n_outstanding = 6; /* This call is not always supported */ o = pa_ext_stream_restore_read (control->priv->pa_context, _pa_ext_stream_restore_read_cb, control); if (o != NULL) { pa_operation_unref (o); control->priv->n_outstanding++; pa_ext_stream_restore_set_subscribe_cb (control->priv->pa_context, _pa_ext_stream_restore_subscribe_cb, control); o = pa_ext_stream_restore_subscribe (control->priv->pa_context, 1, NULL, NULL); if (o != NULL) { pa_operation_unref (o); } } else { g_debug ("Failed to initialized stream_restore extension: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); } } static void gvc_mixer_new_pa_context (GvcMixerControl *self) { pa_proplist *proplist; g_return_if_fail (self); g_return_if_fail (!self->priv->pa_context); proplist = pa_proplist_new (); pa_proplist_sets (proplist, PA_PROP_APPLICATION_NAME, self->priv->name); pa_proplist_sets (proplist, PA_PROP_APPLICATION_ID, "org.gnome.VolumeControl"); pa_proplist_sets (proplist, PA_PROP_APPLICATION_ICON_NAME, "multimedia-volume-control"); pa_proplist_sets (proplist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION); self->priv->pa_context = pa_context_new_with_proplist (self->priv->pa_api, NULL, proplist); pa_proplist_free (proplist); g_assert (self->priv->pa_context); } static void remove_all_streams (GvcMixerControl *control, GHashTable *hash_table) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, hash_table); while (g_hash_table_iter_next (&iter, &key, &value)) { remove_stream (control, value); g_hash_table_iter_remove (&iter); } } static gboolean idle_reconnect (gpointer data) { GvcMixerControl *control = GVC_MIXER_CONTROL (data); GHashTableIter iter; gpointer key, value; g_return_val_if_fail (control, FALSE); if (control->priv->pa_context) { pa_context_unref (control->priv->pa_context); control->priv->pa_context = NULL; gvc_mixer_new_pa_context (control); } remove_all_streams (control, control->priv->sinks); remove_all_streams (control, control->priv->sources); remove_all_streams (control, control->priv->sink_inputs); remove_all_streams (control, control->priv->source_outputs); g_hash_table_iter_init (&iter, control->priv->clients); while (g_hash_table_iter_next (&iter, &key, &value)) g_hash_table_iter_remove (&iter); gvc_mixer_control_open (control); /* cannot fail */ control->priv->reconnect_id = 0; return FALSE; } static void _pa_context_state_cb (pa_context *context, void *userdata) { GvcMixerControl *control = GVC_MIXER_CONTROL (userdata); switch (pa_context_get_state (context)) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: gvc_mixer_control_ready (control); break; case PA_CONTEXT_FAILED: control->priv->state = GVC_STATE_FAILED; g_signal_emit (control, signals[STATE_CHANGED], 0, GVC_STATE_FAILED); if (control->priv->reconnect_id == 0) control->priv->reconnect_id = g_timeout_add_seconds (RECONNECT_DELAY, idle_reconnect, control); break; case PA_CONTEXT_TERMINATED: default: /* FIXME: */ break; } } gboolean gvc_mixer_control_open (GvcMixerControl *control) { int res; g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (control->priv->pa_context != NULL, FALSE); g_return_val_if_fail (pa_context_get_state (control->priv->pa_context) == PA_CONTEXT_UNCONNECTED, FALSE); pa_context_set_state_callback (control->priv->pa_context, _pa_context_state_cb, control); control->priv->state = GVC_STATE_CONNECTING; g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CONNECTING); res = pa_context_connect (control->priv->pa_context, NULL, (pa_context_flags_t) PA_CONTEXT_NOFAIL, NULL); if (res < 0) { g_warning ("Failed to connect context: %s", pa_strerror (pa_context_errno (control->priv->pa_context))); } return res; } gboolean gvc_mixer_control_close (GvcMixerControl *control) { g_return_val_if_fail (GVC_IS_MIXER_CONTROL (control), FALSE); g_return_val_if_fail (control->priv->pa_context != NULL, FALSE); pa_context_disconnect (control->priv->pa_context); control->priv->state = GVC_STATE_CLOSED; g_signal_emit (G_OBJECT (control), signals[STATE_CHANGED], 0, GVC_STATE_CLOSED); return TRUE; } static void gvc_mixer_control_dispose (GObject *object) { GvcMixerControl *control = GVC_MIXER_CONTROL (object); if (control->priv->reconnect_id != 0) { g_source_remove (control->priv->reconnect_id); control->priv->reconnect_id = 0; } if (control->priv->pa_context != NULL) { pa_context_unref (control->priv->pa_context); control->priv->pa_context = NULL; } if (control->priv->default_source_name != NULL) { g_free (control->priv->default_source_name); control->priv->default_source_name = NULL; } if (control->priv->default_sink_name != NULL) { g_free (control->priv->default_sink_name); control->priv->default_sink_name = NULL; } if (control->priv->pa_mainloop != NULL) { pa_glib_mainloop_free (control->priv->pa_mainloop); control->priv->pa_mainloop = NULL; } if (control->priv->all_streams != NULL) { g_hash_table_destroy (control->priv->all_streams); control->priv->all_streams = NULL; } if (control->priv->sinks != NULL) { g_hash_table_destroy (control->priv->sinks); control->priv->sinks = NULL; } if (control->priv->sources != NULL) { g_hash_table_destroy (control->priv->sources); control->priv->sources = NULL; } if (control->priv->sink_inputs != NULL) { g_hash_table_destroy (control->priv->sink_inputs); control->priv->sink_inputs = NULL; } if (control->priv->source_outputs != NULL) { g_hash_table_destroy (control->priv->source_outputs); control->priv->source_outputs = NULL; } if (control->priv->clients != NULL) { g_hash_table_destroy (control->priv->clients); control->priv->clients = NULL; } if (control->priv->cards != NULL) { g_hash_table_destroy (control->priv->cards); control->priv->cards = NULL; } if (control->priv->ui_outputs != NULL) { g_hash_table_destroy (control->priv->ui_outputs); control->priv->ui_outputs = NULL; } if (control->priv->ui_inputs != NULL) { g_hash_table_destroy (control->priv->ui_inputs); control->priv->ui_inputs = NULL; } G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object); } static void gvc_mixer_control_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerControl *self = GVC_MIXER_CONTROL (object); switch (prop_id) { case PROP_NAME: g_free (self->priv->name); self->priv->name = g_value_dup_string (value); g_object_notify (G_OBJECT (self), "name"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_control_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerControl *self = GVC_MIXER_CONTROL (object); switch (prop_id) { case PROP_NAME: g_value_set_string (value, self->priv->name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gvc_mixer_control_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerControl *self; object = G_OBJECT_CLASS (gvc_mixer_control_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_CONTROL (object); gvc_mixer_new_pa_context (self); self->priv->profile_swapping_device_id = GVC_MIXER_UI_DEVICE_INVALID; return object; } static void gvc_mixer_control_class_init (GvcMixerControlClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gvc_mixer_control_constructor; object_class->dispose = gvc_mixer_control_dispose; object_class->finalize = gvc_mixer_control_finalize; object_class->set_property = gvc_mixer_control_set_property; object_class->get_property = gvc_mixer_control_get_property; g_object_class_install_property (object_class, PROP_NAME, g_param_spec_string ("name", "Name", "Name to display for this mixer control", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); signals [STATE_CHANGED] = g_signal_new ("state-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, state_changed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [STREAM_ADDED] = g_signal_new ("stream-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, stream_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [STREAM_REMOVED] = g_signal_new ("stream-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, stream_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [CARD_INFO] = g_signal_new ("card-info", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); signals [CARD_ADDED] = g_signal_new ("card-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, card_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [CARD_REMOVED] = g_signal_new ("card-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, card_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [DEFAULT_SINK_CHANGED] = g_signal_new ("default-sink-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, default_sink_changed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [DEFAULT_SOURCE_CHANGED] = g_signal_new ("default-source-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, default_source_changed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [ACTIVE_OUTPUT_UPDATE] = g_signal_new ("active-output-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, active_output_update), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [ACTIVE_INPUT_UPDATE] = g_signal_new ("active-input-update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, active_input_update), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [OUTPUT_ADDED] = g_signal_new ("output-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, output_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [INPUT_ADDED] = g_signal_new ("input-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, input_added), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [OUTPUT_REMOVED] = g_signal_new ("output-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, output_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals [INPUT_REMOVED] = g_signal_new ("input-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GvcMixerControlClass, input_removed), NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); g_type_class_add_private (klass, sizeof (GvcMixerControlPrivate)); } static void gvc_mixer_control_init (GvcMixerControl *control) { control->priv = GVC_MIXER_CONTROL_GET_PRIVATE (control); control->priv->pa_mainloop = pa_glib_mainloop_new (g_main_context_default ()); g_assert (control->priv->pa_mainloop); control->priv->pa_api = pa_glib_mainloop_get_api (control->priv->pa_mainloop); g_assert (control->priv->pa_api); control->priv->all_streams = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->sinks = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->sources = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->sink_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->source_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->cards = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->ui_outputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->ui_inputs = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_object_unref); control->priv->clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free); control->priv->state = GVC_STATE_CLOSED; } static void gvc_mixer_control_finalize (GObject *object) { GvcMixerControl *mixer_control; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_CONTROL (object)); mixer_control = GVC_MIXER_CONTROL (object); g_free (mixer_control->priv->name); mixer_control->priv->name = NULL; g_return_if_fail (mixer_control->priv != NULL); G_OBJECT_CLASS (gvc_mixer_control_parent_class)->finalize (object); } GvcMixerControl * gvc_mixer_control_new (const char *name) { GObject *control; control = g_object_new (GVC_TYPE_MIXER_CONTROL, "name", name, NULL); return GVC_MIXER_CONTROL (control); } gdouble gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control) { return (gdouble) PA_VOLUME_NORM; } gdouble gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control) { return (gdouble) PA_VOLUME_UI_MAX; } ./plugins/media-keys/gvc/gvc-mixer-sink-input.h0000644000004100000410000000433312735467744021732 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_SINK_INPUT_H #define __GVC_MIXER_SINK_INPUT_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SINK_INPUT (gvc_mixer_sink_input_get_type ()) #define GVC_MIXER_SINK_INPUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInput)) #define GVC_MIXER_SINK_INPUT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass)) #define GVC_IS_MIXER_SINK_INPUT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK_INPUT)) #define GVC_IS_MIXER_SINK_INPUT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK_INPUT)) #define GVC_MIXER_SINK_INPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputClass)) typedef struct GvcMixerSinkInputPrivate GvcMixerSinkInputPrivate; typedef struct { GvcMixerStream parent; GvcMixerSinkInputPrivate *priv; } GvcMixerSinkInput; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSinkInputClass; GType gvc_mixer_sink_input_get_type (void); GvcMixerStream * gvc_mixer_sink_input_new (pa_context *context, guint index, GvcChannelMap *channel_map); G_END_DECLS #endif /* __GVC_MIXER_SINK_INPUT_H */ ./plugins/media-keys/gvc/Makefile.am0000644000004100000410000000266312735467744017621 0ustar www-datawww-data noinst_LTLIBRARIES = libgvc.la INTROSPECTION_SCANNER_ARGS = --warn-all libgvc_la_CPPFLAGS = \ $(WARN_CFLAGS) \ $(GVC_CFLAGS) \ -I$(srcdir) \ -DWITH_INTROSPECTION \ -DG_LOG_DOMAIN="\"Gvc\"" libgvc_la_gir_sources = \ gvc-mixer-card.h \ gvc-mixer-card.c \ gvc-mixer-stream.h \ gvc-mixer-stream.c \ gvc-channel-map.h \ gvc-channel-map.c \ gvc-mixer-ui-device.h \ gvc-mixer-ui-device.c \ gvc-mixer-sink.h \ gvc-mixer-sink.c \ gvc-mixer-source.h \ gvc-mixer-source.c \ gvc-mixer-sink-input.h \ gvc-mixer-sink-input.c \ gvc-mixer-source-output.h \ gvc-mixer-source-output.c \ gvc-mixer-event-role.h \ gvc-mixer-event-role.c \ gvc-mixer-control.h \ gvc-mixer-control.c \ $(NULL) libgvc_la_SOURCES = \ $(libgvc_la_gir_sources) \ gvc-mixer-card-private.h \ gvc-mixer-stream-private.h \ gvc-channel-map-private.h \ gvc-mixer-control-private.h \ gvc-pulseaudio-fake.h \ $(NULL) libgvc_la_LIBADD = \ $(GVC_LIBS) \ $(NULL) if HAVE_INTROSPECTION include $(INTROSPECTION_MAKEFILE) Gvc-1.0.gir: libgvc.la Gvc_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 Gvc_1_0_gir_CFLAGS = $(INCLUDES) -I$(srcdir) -DWITH_INTROSPECTION Gvc_1_0_gir_LIBS = libgvc.la Gvc_1_0_gir_FILES = $(addprefix $(srcdir)/,$(libgvc_la_gir_sources)) INTROSPECTION_GIRS = Gvc-1.0.gir typelibdir = $(pkglibdir) typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES = Gvc-1.0.gir $(typelib_DATA) endif ./plugins/media-keys/gvc/gvc-mixer-source-output.h0000644000004100000410000000445712735467744022476 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_SOURCE_OUTPUT_H #define __GVC_MIXER_SOURCE_OUTPUT_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SOURCE_OUTPUT (gvc_mixer_source_output_get_type ()) #define GVC_MIXER_SOURCE_OUTPUT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutput)) #define GVC_MIXER_SOURCE_OUTPUT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass)) #define GVC_IS_MIXER_SOURCE_OUTPUT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT)) #define GVC_IS_MIXER_SOURCE_OUTPUT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE_OUTPUT)) #define GVC_MIXER_SOURCE_OUTPUT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputClass)) typedef struct GvcMixerSourceOutputPrivate GvcMixerSourceOutputPrivate; typedef struct { GvcMixerStream parent; GvcMixerSourceOutputPrivate *priv; } GvcMixerSourceOutput; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSourceOutputClass; GType gvc_mixer_source_output_get_type (void); GvcMixerStream * gvc_mixer_source_output_new (pa_context *context, guint index, GvcChannelMap *channel_map); G_END_DECLS #endif /* __GVC_MIXER_SOURCE_OUTPUT_H */ ./plugins/media-keys/gvc/gvc-mixer-event-role.c0000644000004100000410000001701112735467744021701 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include "gvc-mixer-event-role.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_EVENT_ROLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRolePrivate)) struct GvcMixerEventRolePrivate { char *device; }; enum { PROP_0, PROP_DEVICE }; static void gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass); static void gvc_mixer_event_role_init (GvcMixerEventRole *mixer_event_role); static void gvc_mixer_event_role_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerEventRole, gvc_mixer_event_role, GVC_TYPE_MIXER_STREAM) static gboolean update_settings (GvcMixerEventRole *role, gboolean is_muted, gpointer *op) { pa_operation *o; const GvcChannelMap *map; pa_context *context; pa_ext_stream_restore_info info; map = gvc_mixer_stream_get_channel_map (GVC_MIXER_STREAM(role)); info.volume = *gvc_channel_map_get_cvolume(map); info.name = "sink-input-by-media-role:event"; info.channel_map = *gvc_channel_map_get_pa_channel_map(map); info.device = role->priv->device; info.mute = is_muted; context = gvc_mixer_stream_get_pa_context (GVC_MIXER_STREAM (role)); o = pa_ext_stream_restore_write (context, PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL); if (o == NULL) { g_warning ("pa_ext_stream_restore_write() failed"); return FALSE; } if (op != NULL) *op = o; return TRUE; } static gboolean gvc_mixer_event_role_push_volume (GvcMixerStream *stream, gpointer *op) { return update_settings (GVC_MIXER_EVENT_ROLE (stream), gvc_mixer_stream_get_is_muted (stream), op); } static gboolean gvc_mixer_event_role_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { /* Apply change straight away so that we don't get a race with * gvc_mixer_event_role_push_volume(). * See https://bugs.freedesktop.org/show_bug.cgi?id=51413 */ gvc_mixer_stream_set_is_muted (stream, is_muted); return update_settings (GVC_MIXER_EVENT_ROLE (stream), is_muted, NULL); } static gboolean gvc_mixer_event_role_set_device (GvcMixerEventRole *role, const char *device) { g_return_val_if_fail (GVC_IS_MIXER_EVENT_ROLE (role), FALSE); g_free (role->priv->device); role->priv->device = g_strdup (device); g_object_notify (G_OBJECT (role), "device"); return TRUE; } static void gvc_mixer_event_role_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object); switch (prop_id) { case PROP_DEVICE: gvc_mixer_event_role_set_device (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_event_role_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerEventRole *self = GVC_MIXER_EVENT_ROLE (object); switch (prop_id) { case PROP_DEVICE: g_value_set_string (value, self->priv->device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_event_role_class_init (GvcMixerEventRoleClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_event_role_finalize; object_class->set_property = gvc_mixer_event_role_set_property; object_class->get_property = gvc_mixer_event_role_get_property; stream_class->push_volume = gvc_mixer_event_role_push_volume; stream_class->change_is_muted = gvc_mixer_event_role_change_is_muted; g_object_class_install_property (object_class, PROP_DEVICE, g_param_spec_string ("device", "Device", "Device", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_type_class_add_private (klass, sizeof (GvcMixerEventRolePrivate)); } static void gvc_mixer_event_role_init (GvcMixerEventRole *event_role) { event_role->priv = GVC_MIXER_EVENT_ROLE_GET_PRIVATE (event_role); } static void gvc_mixer_event_role_finalize (GObject *object) { GvcMixerEventRole *mixer_event_role; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_EVENT_ROLE (object)); mixer_event_role = GVC_MIXER_EVENT_ROLE (object); g_return_if_fail (mixer_event_role->priv != NULL); g_free (mixer_event_role->priv->device); G_OBJECT_CLASS (gvc_mixer_event_role_parent_class)->finalize (object); } /** * gvc_mixer_event_role_new: (skip) * @context: * @device: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_event_role_new (pa_context *context, const char *device, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_EVENT_ROLE, "pa-context", context, "index", 0, "device", device, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } ./plugins/media-keys/gvc/gvc-mixer-stream.h0000644000004100000410000001607512735467744021132 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_STREAM_H #define __GVC_MIXER_STREAM_H #include #include "gvc-pulseaudio-fake.h" #include "gvc-channel-map.h" #include G_BEGIN_DECLS #define GVC_TYPE_MIXER_STREAM (gvc_mixer_stream_get_type ()) #define GVC_MIXER_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStream)) #define GVC_MIXER_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass)) #define GVC_IS_MIXER_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_STREAM)) #define GVC_IS_MIXER_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_STREAM)) #define GVC_MIXER_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamClass)) typedef struct GvcMixerStreamPrivate GvcMixerStreamPrivate; typedef struct { GObject parent; GvcMixerStreamPrivate *priv; } GvcMixerStream; typedef struct { GObjectClass parent_class; /* vtable */ gboolean (*push_volume) (GvcMixerStream *stream, gpointer *operation); gboolean (*change_is_muted) (GvcMixerStream *stream, gboolean is_muted); gboolean (*change_port) (GvcMixerStream *stream, const char *port); } GvcMixerStreamClass; typedef struct { char *port; char *human_port; guint priority; gboolean available; } GvcMixerStreamPort; GType gvc_mixer_stream_port_get_type (void) G_GNUC_CONST; GType gvc_mixer_stream_get_type (void) G_GNUC_CONST; guint gvc_mixer_stream_get_index (GvcMixerStream *stream); guint gvc_mixer_stream_get_id (GvcMixerStream *stream); const GvcChannelMap *gvc_mixer_stream_get_channel_map(GvcMixerStream *stream); const GvcMixerStreamPort *gvc_mixer_stream_get_port (GvcMixerStream *stream); const GList * gvc_mixer_stream_get_ports (GvcMixerStream *stream); gboolean gvc_mixer_stream_change_port (GvcMixerStream *stream, const char *port); pa_volume_t gvc_mixer_stream_get_volume (GvcMixerStream *stream); gdouble gvc_mixer_stream_get_decibel (GvcMixerStream *stream); gboolean gvc_mixer_stream_push_volume (GvcMixerStream *stream); pa_volume_t gvc_mixer_stream_get_base_volume (GvcMixerStream *stream); gboolean gvc_mixer_stream_get_is_muted (GvcMixerStream *stream); gboolean gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream); gboolean gvc_mixer_stream_change_is_muted (GvcMixerStream *stream, gboolean is_muted); gboolean gvc_mixer_stream_is_running (GvcMixerStream *stream); const char * gvc_mixer_stream_get_name (GvcMixerStream *stream); const char * gvc_mixer_stream_get_icon_name (GvcMixerStream *stream); const char * gvc_mixer_stream_get_form_factor (GvcMixerStream *stream); const char * gvc_mixer_stream_get_sysfs_path (GvcMixerStream *stream); GIcon * gvc_mixer_stream_get_gicon (GvcMixerStream *stream); const char * gvc_mixer_stream_get_description (GvcMixerStream *stream); const char * gvc_mixer_stream_get_application_id (GvcMixerStream *stream); gboolean gvc_mixer_stream_is_event_stream (GvcMixerStream *stream); gboolean gvc_mixer_stream_is_virtual (GvcMixerStream *stream); gint gvc_mixer_stream_get_card_index (GvcMixerStream *stream); /* private */ gboolean gvc_mixer_stream_set_volume (GvcMixerStream *stream, pa_volume_t volume); gboolean gvc_mixer_stream_set_decibel (GvcMixerStream *stream, gdouble db); gboolean gvc_mixer_stream_set_is_muted (GvcMixerStream *stream, gboolean is_muted); gboolean gvc_mixer_stream_set_can_decibel (GvcMixerStream *stream, gboolean can_decibel); gboolean gvc_mixer_stream_set_name (GvcMixerStream *stream, const char *name); gboolean gvc_mixer_stream_set_description (GvcMixerStream *stream, const char *description); gboolean gvc_mixer_stream_set_icon_name (GvcMixerStream *stream, const char *name); gboolean gvc_mixer_stream_set_form_factor (GvcMixerStream *stream, const char *form_factor); gboolean gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream, const char *sysfs_path); gboolean gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream, gboolean is_event_stream); gboolean gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream, gboolean is_event_stream); gboolean gvc_mixer_stream_set_application_id (GvcMixerStream *stream, const char *application_id); gboolean gvc_mixer_stream_set_base_volume (GvcMixerStream *stream, pa_volume_t base_volume); gboolean gvc_mixer_stream_set_port (GvcMixerStream *stream, const char *port); gboolean gvc_mixer_stream_set_ports (GvcMixerStream *stream, GList *ports); gboolean gvc_mixer_stream_set_card_index (GvcMixerStream *stream, gint card_index); G_END_DECLS #endif /* __GVC_MIXER_STREAM_H */ ./plugins/media-keys/gvc/gvc-mixer-control.h0000644000004100000410000001637212735467744021317 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_CONTROL_H #define __GVC_MIXER_CONTROL_H #include #include "gvc-mixer-stream.h" #include "gvc-mixer-card.h" #include "gvc-mixer-ui-device.h" G_BEGIN_DECLS typedef enum { GVC_STATE_CLOSED, GVC_STATE_READY, GVC_STATE_CONNECTING, GVC_STATE_FAILED } GvcMixerControlState; #define GVC_TYPE_MIXER_CONTROL (gvc_mixer_control_get_type ()) #define GVC_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControl)) #define GVC_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass)) #define GVC_IS_MIXER_CONTROL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CONTROL)) #define GVC_IS_MIXER_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CONTROL)) #define GVC_MIXER_CONTROL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CONTROL, GvcMixerControlClass)) typedef struct GvcMixerControlPrivate GvcMixerControlPrivate; typedef struct { GObject parent; GvcMixerControlPrivate *priv; } GvcMixerControl; typedef struct { GObjectClass parent_class; void (*state_changed) (GvcMixerControl *control, GvcMixerControlState new_state); void (*stream_added) (GvcMixerControl *control, guint id); void (*stream_removed) (GvcMixerControl *control, guint id); void (*card_added) (GvcMixerControl *control, guint id); void (*card_removed) (GvcMixerControl *control, guint id); void (*default_sink_changed) (GvcMixerControl *control, guint id); void (*default_source_changed) (GvcMixerControl *control, guint id); void (*active_output_update) (GvcMixerControl *control, guint id); void (*active_input_update) (GvcMixerControl *control, guint id); void (*output_added) (GvcMixerControl *control, guint id); void (*input_added) (GvcMixerControl *control, guint id); void (*output_removed) (GvcMixerControl *control, guint id); void (*input_removed) (GvcMixerControl *control, guint id); } GvcMixerControlClass; GType gvc_mixer_control_get_type (void); GvcMixerControl * gvc_mixer_control_new (const char *name); gboolean gvc_mixer_control_open (GvcMixerControl *control); gboolean gvc_mixer_control_close (GvcMixerControl *control); GSList * gvc_mixer_control_get_cards (GvcMixerControl *control); GSList * gvc_mixer_control_get_streams (GvcMixerControl *control); GSList * gvc_mixer_control_get_sinks (GvcMixerControl *control); GSList * gvc_mixer_control_get_sources (GvcMixerControl *control); GSList * gvc_mixer_control_get_sink_inputs (GvcMixerControl *control); GSList * gvc_mixer_control_get_source_outputs (GvcMixerControl *control); GvcMixerStream * gvc_mixer_control_lookup_stream_id (GvcMixerControl *control, guint id); GvcMixerCard * gvc_mixer_control_lookup_card_id (GvcMixerControl *control, guint id); GvcMixerUIDevice * gvc_mixer_control_lookup_output_id (GvcMixerControl *control, guint id); GvcMixerUIDevice * gvc_mixer_control_lookup_input_id (GvcMixerControl *control, guint id); GvcMixerUIDevice * gvc_mixer_control_lookup_device_from_stream (GvcMixerControl *control, GvcMixerStream *stream); GvcMixerStream * gvc_mixer_control_get_default_sink (GvcMixerControl *control); GvcMixerStream * gvc_mixer_control_get_default_source (GvcMixerControl *control); GvcMixerStream * gvc_mixer_control_get_event_sink_input (GvcMixerControl *control); gboolean gvc_mixer_control_set_default_sink (GvcMixerControl *control, GvcMixerStream *stream); gboolean gvc_mixer_control_set_default_source (GvcMixerControl *control, GvcMixerStream *stream); gdouble gvc_mixer_control_get_vol_max_norm (GvcMixerControl *control); gdouble gvc_mixer_control_get_vol_max_amplified (GvcMixerControl *control); void gvc_mixer_control_change_output (GvcMixerControl *control, GvcMixerUIDevice* output); void gvc_mixer_control_change_input (GvcMixerControl *control, GvcMixerUIDevice* input); GvcMixerStream* gvc_mixer_control_get_stream_from_device (GvcMixerControl *control, GvcMixerUIDevice *device); gboolean gvc_mixer_control_change_profile_on_selected_device (GvcMixerControl *control, GvcMixerUIDevice *device, const gchar* profile); GvcMixerControlState gvc_mixer_control_get_state (GvcMixerControl *control); G_END_DECLS #endif /* __GVC_MIXER_CONTROL_H */ ./plugins/media-keys/gvc/gvc-channel-map.h0000644000004100000410000000557012735467744020676 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_CHANNEL_MAP_H #define __GVC_CHANNEL_MAP_H #include #include G_BEGIN_DECLS #define GVC_TYPE_CHANNEL_MAP (gvc_channel_map_get_type ()) #define GVC_CHANNEL_MAP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMap)) #define GVC_CHANNEL_MAP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass)) #define GVC_IS_CHANNEL_MAP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_CHANNEL_MAP)) #define GVC_IS_CHANNEL_MAP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_CHANNEL_MAP)) #define GVC_CHANNEL_MAP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_CHANNEL_MAP, GvcChannelMapClass)) typedef struct GvcChannelMapPrivate GvcChannelMapPrivate; typedef struct { GObject parent; GvcChannelMapPrivate *priv; } GvcChannelMap; typedef struct { GObjectClass parent_class; void (*volume_changed) (GvcChannelMap *channel_map, gboolean set); } GvcChannelMapClass; enum { VOLUME, BALANCE, FADE, LFE, NUM_TYPES }; GType gvc_channel_map_get_type (void); GvcChannelMap * gvc_channel_map_new (void); guint gvc_channel_map_get_num_channels (const GvcChannelMap *map); const gdouble * gvc_channel_map_get_volume (GvcChannelMap *map); gboolean gvc_channel_map_can_balance (const GvcChannelMap *map); gboolean gvc_channel_map_can_fade (const GvcChannelMap *map); gboolean gvc_channel_map_has_position (const GvcChannelMap *map, pa_channel_position_t position); #define gvc_channel_map_has_lfe(x) gvc_channel_map_has_position (x, PA_CHANNEL_POSITION_LFE) const char * gvc_channel_map_get_mapping (const GvcChannelMap *map); G_END_DECLS #endif /* __GVC_CHANNEL_MAP_H */ ./plugins/media-keys/gvc/gvc-pulseaudio-fake.h0000644000004100000410000000216712735467744021570 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_PULSEAUDIO_FAKE_H #define __GVC_PULSEAUDIO_FAKE_H #ifdef WITH_INTROSPECTION #ifndef PA_API_VERSION #define pa_channel_position_t int #define pa_volume_t guint32 #define pa_context gpointer #endif /* PA_API_VERSION */ #endif /* WITH_INTROSPECTION */ #endif /* __GVC_PULSEAUDIO_FAKE_H */ ./plugins/media-keys/gvc/gvc-mixer-sink.c0000644000004100000410000001334012735467744020566 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include "gvc-mixer-sink.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_SINK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkPrivate)) struct GvcMixerSinkPrivate { gpointer dummy; }; static void gvc_mixer_sink_class_init (GvcMixerSinkClass *klass); static void gvc_mixer_sink_init (GvcMixerSink *mixer_sink); static void gvc_mixer_sink_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSink, gvc_mixer_sink, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_sink_push_volume (GvcMixerStream *stream, gpointer *op) { pa_operation *o; guint index; const GvcChannelMap *map; pa_context *context; const pa_cvolume *cv; index = gvc_mixer_stream_get_index (stream); map = gvc_mixer_stream_get_channel_map (stream); /* set the volume */ cv = gvc_channel_map_get_cvolume(map); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_volume_by_index (context, index, cv, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } *op = o; return TRUE; } static gboolean gvc_mixer_sink_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_mute_by_index (context, index, is_muted, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static gboolean gvc_mixer_sink_change_port (GvcMixerStream *stream, const char *port) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_port_by_index (context, index, port, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_port_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static void gvc_mixer_sink_class_init (GvcMixerSinkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_sink_finalize; stream_class->push_volume = gvc_mixer_sink_push_volume; stream_class->change_port = gvc_mixer_sink_change_port; stream_class->change_is_muted = gvc_mixer_sink_change_is_muted; g_type_class_add_private (klass, sizeof (GvcMixerSinkPrivate)); } static void gvc_mixer_sink_init (GvcMixerSink *sink) { sink->priv = GVC_MIXER_SINK_GET_PRIVATE (sink); } static void gvc_mixer_sink_finalize (GObject *object) { GvcMixerSink *mixer_sink; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SINK (object)); mixer_sink = GVC_MIXER_SINK (object); g_return_if_fail (mixer_sink->priv != NULL); G_OBJECT_CLASS (gvc_mixer_sink_parent_class)->finalize (object); } /** * gvc_mixer_sink_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_sink_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SINK, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } ./plugins/media-keys/gvc/gvc-mixer-stream-private.h0000644000004100000410000000214012735467744022566 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_STREAM_PRIVATE_H #define __GVC_MIXER_STREAM_PRIVATE_H #include #include "gvc-channel-map.h" G_BEGIN_DECLS pa_context * gvc_mixer_stream_get_pa_context (GvcMixerStream *stream); G_END_DECLS #endif /* __GVC_MIXER_STREAM_PRIVATE_H */ ./plugins/media-keys/gvc/gvc-mixer-control-private.h0000644000004100000410000000224412735467744022760 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_CONTROL_PRIVATE_H #define __GVC_MIXER_CONTROL_PRIVATE_H #include #include #include "gvc-mixer-stream.h" #include "gvc-mixer-card.h" G_BEGIN_DECLS pa_context * gvc_mixer_control_get_pa_context (GvcMixerControl *control); G_END_DECLS #endif /* __GVC_MIXER_CONTROL_PRIVATE_H */ ./plugins/media-keys/gvc/gvc-mixer-sink.h0000644000004100000410000000412412735467744020573 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_SINK_H #define __GVC_MIXER_SINK_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SINK (gvc_mixer_sink_get_type ()) #define GVC_MIXER_SINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SINK, GvcMixerSink)) #define GVC_MIXER_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass)) #define GVC_IS_MIXER_SINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SINK)) #define GVC_IS_MIXER_SINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SINK)) #define GVC_MIXER_SINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SINK, GvcMixerSinkClass)) typedef struct GvcMixerSinkPrivate GvcMixerSinkPrivate; typedef struct { GvcMixerStream parent; GvcMixerSinkPrivate *priv; } GvcMixerSink; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSinkClass; GType gvc_mixer_sink_get_type (void); GvcMixerStream * gvc_mixer_sink_new (pa_context *context, guint index, GvcChannelMap *channel_map); G_END_DECLS #endif /* __GVC_MIXER_SINK_H */ ./plugins/media-keys/gvc/gvc-mixer-source-output.c0000644000004100000410000000717412735467744022470 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include "gvc-mixer-source-output.h" #define GVC_MIXER_SOURCE_OUTPUT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SOURCE_OUTPUT, GvcMixerSourceOutputPrivate)) struct GvcMixerSourceOutputPrivate { gpointer dummy; }; static void gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass); static void gvc_mixer_source_output_init (GvcMixerSourceOutput *mixer_source_output); static void gvc_mixer_source_output_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSourceOutput, gvc_mixer_source_output, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_source_output_push_volume (GvcMixerStream *stream, gpointer *op) { /* FIXME: */ *op = NULL; return TRUE; } static gboolean gvc_mixer_source_output_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { /* FIXME: */ return TRUE; } static void gvc_mixer_source_output_class_init (GvcMixerSourceOutputClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_source_output_finalize; stream_class->push_volume = gvc_mixer_source_output_push_volume; stream_class->change_is_muted = gvc_mixer_source_output_change_is_muted; g_type_class_add_private (klass, sizeof (GvcMixerSourceOutputPrivate)); } static void gvc_mixer_source_output_init (GvcMixerSourceOutput *source_output) { source_output->priv = GVC_MIXER_SOURCE_OUTPUT_GET_PRIVATE (source_output); } static void gvc_mixer_source_output_finalize (GObject *object) { GvcMixerSourceOutput *mixer_source_output; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SOURCE_OUTPUT (object)); mixer_source_output = GVC_MIXER_SOURCE_OUTPUT (object); g_return_if_fail (mixer_source_output->priv != NULL); G_OBJECT_CLASS (gvc_mixer_source_output_parent_class)->finalize (object); } /** * gvc_mixer_source_output_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_source_output_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SOURCE_OUTPUT, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } ./plugins/media-keys/gvc/gvc-mixer-stream.c0000644000004100000410000011071312735467744021117 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include "gvc-mixer-stream.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_STREAM, GvcMixerStreamPrivate)) static guint32 stream_serial = 1; struct GvcMixerStreamPrivate { pa_context *pa_context; guint id; guint index; gint card_index; GvcChannelMap *channel_map; char *name; char *description; char *application_id; char *icon_name; char *form_factor; char *sysfs_path; gboolean is_muted; gboolean can_decibel; gboolean is_event_stream; gboolean is_virtual; pa_volume_t base_volume; pa_operation *change_volume_op; char *port; char *human_port; GList *ports; }; enum { PROP_0, PROP_ID, PROP_PA_CONTEXT, PROP_CHANNEL_MAP, PROP_INDEX, PROP_NAME, PROP_DESCRIPTION, PROP_APPLICATION_ID, PROP_ICON_NAME, PROP_FORM_FACTOR, PROP_SYSFS_PATH, PROP_VOLUME, PROP_DECIBEL, PROP_IS_MUTED, PROP_CAN_DECIBEL, PROP_IS_EVENT_STREAM, PROP_IS_VIRTUAL, PROP_CARD_INDEX, PROP_PORT, }; static void gvc_mixer_stream_class_init (GvcMixerStreamClass *klass); static void gvc_mixer_stream_init (GvcMixerStream *mixer_stream); static void gvc_mixer_stream_finalize (GObject *object); G_DEFINE_ABSTRACT_TYPE (GvcMixerStream, gvc_mixer_stream, G_TYPE_OBJECT) static void free_port (GvcMixerStreamPort *p) { g_free (p->port); g_free (p->human_port); g_slice_free (GvcMixerStreamPort, p); } static GvcMixerStreamPort * dup_port (GvcMixerStreamPort *p) { GvcMixerStreamPort *m; m = g_slice_new (GvcMixerStreamPort); *m = *p; m->port = g_strdup (p->port); m->human_port = g_strdup (p->human_port); return m; } G_DEFINE_BOXED_TYPE (GvcMixerStreamPort, gvc_mixer_stream_port, dup_port, free_port) static guint32 get_next_stream_serial (void) { guint32 serial; serial = stream_serial++; if ((gint32)stream_serial < 0) { stream_serial = 1; } return serial; } pa_context * gvc_mixer_stream_get_pa_context (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->pa_context; } guint gvc_mixer_stream_get_index (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->index; } guint gvc_mixer_stream_get_id (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->id; } const GvcChannelMap * gvc_mixer_stream_get_channel_map (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->channel_map; } /** * gvc_mixer_stream_get_volume: * @stream: * * Returns: (type guint32) (transfer none): */ pa_volume_t gvc_mixer_stream_get_volume (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]; } gdouble gvc_mixer_stream_get_decibel (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return pa_sw_volume_to_dB( (pa_volume_t) gvc_channel_map_get_volume(stream->priv->channel_map)[VOLUME]); } /** * gvc_mixer_stream_set_volume: * @stream: * @volume: (type guint32): * * Returns: */ gboolean gvc_mixer_stream_set_volume (GvcMixerStream *stream, pa_volume_t volume) { pa_cvolume cv; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map); pa_cvolume_scale(&cv, volume); if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) { gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE); g_object_notify (G_OBJECT (stream), "volume"); return TRUE; } return FALSE; } gboolean gvc_mixer_stream_set_decibel (GvcMixerStream *stream, gdouble db) { pa_cvolume cv; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); cv = *gvc_channel_map_get_cvolume(stream->priv->channel_map); pa_cvolume_scale(&cv, pa_sw_volume_from_dB(db)); if (!pa_cvolume_equal(gvc_channel_map_get_cvolume(stream->priv->channel_map), &cv)) { gvc_channel_map_volume_changed(stream->priv->channel_map, &cv, FALSE); g_object_notify (G_OBJECT (stream), "volume"); } return TRUE; } gboolean gvc_mixer_stream_get_is_muted (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->is_muted; } gboolean gvc_mixer_stream_get_can_decibel (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->can_decibel; } gboolean gvc_mixer_stream_set_is_muted (GvcMixerStream *stream, gboolean is_muted) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (is_muted != stream->priv->is_muted) { stream->priv->is_muted = is_muted; g_object_notify (G_OBJECT (stream), "is-muted"); } return TRUE; } gboolean gvc_mixer_stream_set_can_decibel (GvcMixerStream *stream, gboolean can_decibel) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (can_decibel != stream->priv->can_decibel) { stream->priv->can_decibel = can_decibel; g_object_notify (G_OBJECT (stream), "can-decibel"); } return TRUE; } const char * gvc_mixer_stream_get_name (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->name; } const char * gvc_mixer_stream_get_description (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->description; } gboolean gvc_mixer_stream_set_name (GvcMixerStream *stream, const char *name) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->name); stream->priv->name = g_strdup (name); g_object_notify (G_OBJECT (stream), "name"); return TRUE; } gboolean gvc_mixer_stream_set_description (GvcMixerStream *stream, const char *description) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->description); stream->priv->description = g_strdup (description); g_object_notify (G_OBJECT (stream), "description"); return TRUE; } gboolean gvc_mixer_stream_is_event_stream (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->is_event_stream; } gboolean gvc_mixer_stream_set_is_event_stream (GvcMixerStream *stream, gboolean is_event_stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->is_event_stream = is_event_stream; g_object_notify (G_OBJECT (stream), "is-event-stream"); return TRUE; } gboolean gvc_mixer_stream_is_virtual (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return stream->priv->is_virtual; } gboolean gvc_mixer_stream_set_is_virtual (GvcMixerStream *stream, gboolean is_virtual) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->is_virtual = is_virtual; g_object_notify (G_OBJECT (stream), "is-virtual"); return TRUE; } const char * gvc_mixer_stream_get_application_id (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->application_id; } gboolean gvc_mixer_stream_set_application_id (GvcMixerStream *stream, const char *application_id) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->application_id); stream->priv->application_id = g_strdup (application_id); g_object_notify (G_OBJECT (stream), "application-id"); return TRUE; } static void on_channel_map_volume_changed (GvcChannelMap *channel_map, gboolean set, GvcMixerStream *stream) { if (set == TRUE) gvc_mixer_stream_push_volume (stream); g_object_notify (G_OBJECT (stream), "volume"); } static gboolean gvc_mixer_stream_set_channel_map (GvcMixerStream *stream, GvcChannelMap *channel_map) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (channel_map != NULL) { g_object_ref (channel_map); } if (stream->priv->channel_map != NULL) { g_signal_handlers_disconnect_by_func (stream->priv->channel_map, on_channel_map_volume_changed, stream); g_object_unref (stream->priv->channel_map); } stream->priv->channel_map = channel_map; if (stream->priv->channel_map != NULL) { g_signal_connect (stream->priv->channel_map, "volume-changed", G_CALLBACK (on_channel_map_volume_changed), stream); g_object_notify (G_OBJECT (stream), "channel-map"); } return TRUE; } const char * gvc_mixer_stream_get_icon_name (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->icon_name; } const char * gvc_mixer_stream_get_form_factor (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->form_factor; } const char * gvc_mixer_stream_get_sysfs_path (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->sysfs_path; } /** * gvc_mixer_stream_get_gicon: * @stream: a #GvcMixerStream * * Returns: (transfer full): a new #GIcon */ GIcon * gvc_mixer_stream_get_gicon (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); if (stream->priv->icon_name == NULL) return NULL; return g_themed_icon_new_with_default_fallbacks (stream->priv->icon_name); } gboolean gvc_mixer_stream_set_icon_name (GvcMixerStream *stream, const char *icon_name) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->icon_name); stream->priv->icon_name = g_strdup (icon_name); g_object_notify (G_OBJECT (stream), "icon-name"); return TRUE; } gboolean gvc_mixer_stream_set_form_factor (GvcMixerStream *stream, const char *form_factor) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->form_factor); stream->priv->form_factor = g_strdup (form_factor); g_object_notify (G_OBJECT (stream), "form-factor"); return TRUE; } gboolean gvc_mixer_stream_set_sysfs_path (GvcMixerStream *stream, const char *sysfs_path) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_free (stream->priv->sysfs_path); stream->priv->sysfs_path = g_strdup (sysfs_path); g_object_notify (G_OBJECT (stream), "sysfs-path"); return TRUE; } /** * gvc_mixer_stream_get_base_volume: * @stream: * * Returns: (type guint32) (transfer none): */ pa_volume_t gvc_mixer_stream_get_base_volume (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), 0); return stream->priv->base_volume; } /** * gvc_mixer_stream_set_base_volume: * @stream: * @base_volume: (type guint32): * * Returns: */ gboolean gvc_mixer_stream_set_base_volume (GvcMixerStream *stream, pa_volume_t base_volume) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->base_volume = base_volume; return TRUE; } const GvcMixerStreamPort * gvc_mixer_stream_get_port (GvcMixerStream *stream) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); g_return_val_if_fail (stream->priv->ports != NULL, NULL); for (l = stream->priv->ports; l != NULL; l = l->next) { GvcMixerStreamPort *p = l->data; if (g_strcmp0 (stream->priv->port, p->port) == 0) { return p; } } g_assert_not_reached (); return NULL; } gboolean gvc_mixer_stream_set_port (GvcMixerStream *stream, const char *port) { GList *l; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_return_val_if_fail (stream->priv->ports != NULL, FALSE); g_free (stream->priv->port); stream->priv->port = g_strdup (port); g_free (stream->priv->human_port); stream->priv->human_port = NULL; for (l = stream->priv->ports; l != NULL; l = l->next) { GvcMixerStreamPort *p = l->data; if (g_str_equal (stream->priv->port, p->port)) { stream->priv->human_port = g_strdup (p->human_port); break; } } g_object_notify (G_OBJECT (stream), "port"); return TRUE; } gboolean gvc_mixer_stream_change_port (GvcMixerStream *stream, const char *port) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); return GVC_MIXER_STREAM_GET_CLASS (stream)->change_port (stream, port); } /** * gvc_mixer_stream_get_ports: * * Return value: (transfer none) (element-type GvcMixerStreamPort): */ const GList * gvc_mixer_stream_get_ports (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), NULL); return stream->priv->ports; } static int sort_ports (GvcMixerStreamPort *a, GvcMixerStreamPort *b) { if (a->priority == b->priority) return 0; if (a->priority > b->priority) return 1; return -1; } /** * gvc_mixer_stream_set_ports: * @ports: (transfer full) (element-type GvcMixerStreamPort): */ gboolean gvc_mixer_stream_set_ports (GvcMixerStream *stream, GList *ports) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); g_return_val_if_fail (stream->priv->ports == NULL, FALSE); stream->priv->ports = g_list_sort (ports, (GCompareFunc) sort_ports); return TRUE; } gint gvc_mixer_stream_get_card_index (GvcMixerStream *stream) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), PA_INVALID_INDEX); return stream->priv->card_index; } gboolean gvc_mixer_stream_set_card_index (GvcMixerStream *stream, gint card_index) { g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); stream->priv->card_index = card_index; g_object_notify (G_OBJECT (stream), "card-index"); return TRUE; } static void gvc_mixer_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GvcMixerStream *self = GVC_MIXER_STREAM (object); switch (prop_id) { case PROP_PA_CONTEXT: self->priv->pa_context = g_value_get_pointer (value); break; case PROP_INDEX: self->priv->index = g_value_get_ulong (value); break; case PROP_ID: self->priv->id = g_value_get_ulong (value); break; case PROP_CHANNEL_MAP: gvc_mixer_stream_set_channel_map (self, g_value_get_object (value)); break; case PROP_NAME: gvc_mixer_stream_set_name (self, g_value_get_string (value)); break; case PROP_DESCRIPTION: gvc_mixer_stream_set_description (self, g_value_get_string (value)); break; case PROP_APPLICATION_ID: gvc_mixer_stream_set_application_id (self, g_value_get_string (value)); break; case PROP_ICON_NAME: gvc_mixer_stream_set_icon_name (self, g_value_get_string (value)); break; case PROP_FORM_FACTOR: gvc_mixer_stream_set_form_factor (self, g_value_get_string (value)); break; case PROP_SYSFS_PATH: gvc_mixer_stream_set_sysfs_path (self, g_value_get_string (value)); break; case PROP_VOLUME: gvc_mixer_stream_set_volume (self, g_value_get_ulong (value)); break; case PROP_DECIBEL: gvc_mixer_stream_set_decibel (self, g_value_get_double (value)); break; case PROP_IS_MUTED: gvc_mixer_stream_set_is_muted (self, g_value_get_boolean (value)); break; case PROP_IS_EVENT_STREAM: gvc_mixer_stream_set_is_event_stream (self, g_value_get_boolean (value)); break; case PROP_IS_VIRTUAL: gvc_mixer_stream_set_is_virtual (self, g_value_get_boolean (value)); break; case PROP_CAN_DECIBEL: gvc_mixer_stream_set_can_decibel (self, g_value_get_boolean (value)); break; case PROP_PORT: gvc_mixer_stream_set_port (self, g_value_get_string (value)); break; case PROP_CARD_INDEX: self->priv->card_index = g_value_get_long (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gvc_mixer_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GvcMixerStream *self = GVC_MIXER_STREAM (object); switch (prop_id) { case PROP_PA_CONTEXT: g_value_set_pointer (value, self->priv->pa_context); break; case PROP_INDEX: g_value_set_ulong (value, self->priv->index); break; case PROP_ID: g_value_set_ulong (value, self->priv->id); break; case PROP_CHANNEL_MAP: g_value_set_object (value, self->priv->channel_map); break; case PROP_NAME: g_value_set_string (value, self->priv->name); break; case PROP_DESCRIPTION: g_value_set_string (value, self->priv->description); break; case PROP_APPLICATION_ID: g_value_set_string (value, self->priv->application_id); break; case PROP_ICON_NAME: g_value_set_string (value, self->priv->icon_name); break; case PROP_FORM_FACTOR: g_value_set_string (value, self->priv->form_factor); break; case PROP_SYSFS_PATH: g_value_set_string (value, self->priv->sysfs_path); break; case PROP_VOLUME: g_value_set_ulong (value, pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map))); break; case PROP_DECIBEL: g_value_set_double (value, pa_sw_volume_to_dB(pa_cvolume_max(gvc_channel_map_get_cvolume(self->priv->channel_map)))); break; case PROP_IS_MUTED: g_value_set_boolean (value, self->priv->is_muted); break; case PROP_IS_EVENT_STREAM: g_value_set_boolean (value, self->priv->is_event_stream); break; case PROP_IS_VIRTUAL: g_value_set_boolean (value, self->priv->is_virtual); break; case PROP_CAN_DECIBEL: g_value_set_boolean (value, self->priv->can_decibel); break; case PROP_PORT: g_value_set_string (value, self->priv->port); break; case PROP_CARD_INDEX: g_value_set_long (value, self->priv->card_index); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gvc_mixer_stream_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GObject *object; GvcMixerStream *self; object = G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->constructor (type, n_construct_properties, construct_params); self = GVC_MIXER_STREAM (object); self->priv->id = get_next_stream_serial (); return object; } static gboolean gvc_mixer_stream_real_change_port (GvcMixerStream *stream, const char *port) { return FALSE; } static gboolean gvc_mixer_stream_real_push_volume (GvcMixerStream *stream, gpointer *op) { return FALSE; } static gboolean gvc_mixer_stream_real_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { return FALSE; } gboolean gvc_mixer_stream_push_volume (GvcMixerStream *stream) { pa_operation *op; gboolean ret; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); if (stream->priv->is_event_stream != FALSE) return TRUE; g_debug ("Pushing new volume to stream '%s' (%s)", stream->priv->description, stream->priv->name); ret = GVC_MIXER_STREAM_GET_CLASS (stream)->push_volume (stream, (gpointer *) &op); if (ret) { if (stream->priv->change_volume_op != NULL) pa_operation_unref (stream->priv->change_volume_op); stream->priv->change_volume_op = op; } return ret; } gboolean gvc_mixer_stream_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { gboolean ret; g_return_val_if_fail (GVC_IS_MIXER_STREAM (stream), FALSE); ret = GVC_MIXER_STREAM_GET_CLASS (stream)->change_is_muted (stream, is_muted); return ret; } gboolean gvc_mixer_stream_is_running (GvcMixerStream *stream) { if (stream->priv->change_volume_op == NULL) return FALSE; if ((pa_operation_get_state(stream->priv->change_volume_op) == PA_OPERATION_RUNNING)) return TRUE; pa_operation_unref(stream->priv->change_volume_op); stream->priv->change_volume_op = NULL; return FALSE; } static void gvc_mixer_stream_class_init (GvcMixerStreamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructor = gvc_mixer_stream_constructor; gobject_class->finalize = gvc_mixer_stream_finalize; gobject_class->set_property = gvc_mixer_stream_set_property; gobject_class->get_property = gvc_mixer_stream_get_property; klass->push_volume = gvc_mixer_stream_real_push_volume; klass->change_port = gvc_mixer_stream_real_change_port; klass->change_is_muted = gvc_mixer_stream_real_change_is_muted; g_object_class_install_property (gobject_class, PROP_INDEX, g_param_spec_ulong ("index", "Index", "The index for this stream", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_ID, g_param_spec_ulong ("id", "id", "The id for this stream", 0, G_MAXULONG, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_CHANNEL_MAP, g_param_spec_object ("channel-map", "channel map", "The channel map for this stream", GVC_TYPE_CHANNEL_MAP, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_PA_CONTEXT, g_param_spec_pointer ("pa-context", "PulseAudio context", "The PulseAudio context for this stream", G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_VOLUME, g_param_spec_ulong ("volume", "Volume", "The volume for this stream", 0, G_MAXULONG, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_DECIBEL, g_param_spec_double ("decibel", "Decibel", "The decibel level for this stream", -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "Name", "Name to display for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_DESCRIPTION, g_param_spec_string ("description", "Description", "Description to display for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_APPLICATION_ID, g_param_spec_string ("application-id", "Application identifier", "Application identifier for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_ICON_NAME, g_param_spec_string ("icon-name", "Icon Name", "Name of icon to display for this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_FORM_FACTOR, g_param_spec_string ("form-factor", "Form Factor", "Device form factor for this stream, as reported by PulseAudio", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_SYSFS_PATH, g_param_spec_string ("sysfs-path", "Sysfs path", "Sysfs path for the device associated with this stream", NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_IS_MUTED, g_param_spec_boolean ("is-muted", "is muted", "Whether stream is muted", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_CAN_DECIBEL, g_param_spec_boolean ("can-decibel", "can decibel", "Whether stream volume can be converted to decibel units", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_IS_EVENT_STREAM, g_param_spec_boolean ("is-event-stream", "is event stream", "Whether stream's role is to play an event", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_IS_VIRTUAL, g_param_spec_boolean ("is-virtual", "is virtual stream", "Whether the stream is virtual", FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_PORT, g_param_spec_string ("port", "Port", "The name of the current port for this stream", NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_CARD_INDEX, g_param_spec_long ("card-index", "Card index", "The index of the card for this stream", PA_INVALID_INDEX, G_MAXLONG, PA_INVALID_INDEX, G_PARAM_READWRITE|G_PARAM_CONSTRUCT)); g_type_class_add_private (klass, sizeof (GvcMixerStreamPrivate)); } static void gvc_mixer_stream_init (GvcMixerStream *stream) { stream->priv = GVC_MIXER_STREAM_GET_PRIVATE (stream); } static void gvc_mixer_stream_finalize (GObject *object) { GvcMixerStream *mixer_stream; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_STREAM (object)); mixer_stream = GVC_MIXER_STREAM (object); g_return_if_fail (mixer_stream->priv != NULL); g_object_unref (mixer_stream->priv->channel_map); mixer_stream->priv->channel_map = NULL; g_free (mixer_stream->priv->name); mixer_stream->priv->name = NULL; g_free (mixer_stream->priv->description); mixer_stream->priv->description = NULL; g_free (mixer_stream->priv->application_id); mixer_stream->priv->application_id = NULL; g_free (mixer_stream->priv->icon_name); mixer_stream->priv->icon_name = NULL; g_free (mixer_stream->priv->form_factor); mixer_stream->priv->form_factor = NULL; g_free (mixer_stream->priv->sysfs_path); mixer_stream->priv->sysfs_path = NULL; g_free (mixer_stream->priv->port); mixer_stream->priv->port = NULL; g_free (mixer_stream->priv->human_port); mixer_stream->priv->human_port = NULL; g_list_foreach (mixer_stream->priv->ports, (GFunc) free_port, NULL); g_list_free (mixer_stream->priv->ports); mixer_stream->priv->ports = NULL; if (mixer_stream->priv->change_volume_op) { pa_operation_unref(mixer_stream->priv->change_volume_op); mixer_stream->priv->change_volume_op = NULL; } G_OBJECT_CLASS (gvc_mixer_stream_parent_class)->finalize (object); } ./plugins/media-keys/gvc/gvc-mixer-sink-input.c0000644000004100000410000001170212735467744021723 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include "gvc-mixer-sink-input.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_SINK_INPUT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SINK_INPUT, GvcMixerSinkInputPrivate)) struct GvcMixerSinkInputPrivate { gpointer dummy; }; static void gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass); static void gvc_mixer_sink_input_init (GvcMixerSinkInput *mixer_sink_input); static void gvc_mixer_sink_input_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSinkInput, gvc_mixer_sink_input, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_sink_input_push_volume (GvcMixerStream *stream, gpointer *op) { pa_operation *o; guint index; const GvcChannelMap *map; pa_context *context; const pa_cvolume *cv; index = gvc_mixer_stream_get_index (stream); map = gvc_mixer_stream_get_channel_map (stream); cv = gvc_channel_map_get_cvolume(map); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_input_volume (context, index, cv, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_input_volume() failed"); return FALSE; } *op = o; return TRUE; } static gboolean gvc_mixer_sink_input_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_sink_input_mute (context, index, is_muted, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_sink_input_mute_by_index() failed"); return FALSE; } pa_operation_unref(o); return TRUE; } static void gvc_mixer_sink_input_class_init (GvcMixerSinkInputClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_sink_input_finalize; stream_class->push_volume = gvc_mixer_sink_input_push_volume; stream_class->change_is_muted = gvc_mixer_sink_input_change_is_muted; g_type_class_add_private (klass, sizeof (GvcMixerSinkInputPrivate)); } static void gvc_mixer_sink_input_init (GvcMixerSinkInput *sink_input) { sink_input->priv = GVC_MIXER_SINK_INPUT_GET_PRIVATE (sink_input); } static void gvc_mixer_sink_input_finalize (GObject *object) { GvcMixerSinkInput *mixer_sink_input; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SINK_INPUT (object)); mixer_sink_input = GVC_MIXER_SINK_INPUT (object); g_return_if_fail (mixer_sink_input->priv != NULL); G_OBJECT_CLASS (gvc_mixer_sink_input_parent_class)->finalize (object); } /** * gvc_mixer_sink_input_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_sink_input_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SINK_INPUT, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } ./plugins/media-keys/gvc/gvc-mixer-card.h0000644000004100000410000000774212735467744020551 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008-2009 Red Hat, Inc. * Copyright (C) Conor Curran 2011 * * 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. * */ #ifndef __GVC_MIXER_CARD_H #define __GVC_MIXER_CARD_H #include #include G_BEGIN_DECLS #define GVC_TYPE_MIXER_CARD (gvc_mixer_card_get_type ()) #define GVC_MIXER_CARD(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_CARD, GvcMixerCard)) #define GVC_MIXER_CARD_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_CARD, GvcMixerCardClass)) #define GVC_IS_MIXER_CARD(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_CARD)) #define GVC_IS_MIXER_CARD_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_CARD)) #define GVC_MIXER_CARD_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_CARD, GvcMixerCardClass)) typedef struct GvcMixerCardPrivate GvcMixerCardPrivate; typedef struct { GObject parent; GvcMixerCardPrivate *priv; } GvcMixerCard; typedef struct { GObjectClass parent_class; /* vtable */ } GvcMixerCardClass; typedef struct { char *profile; char *human_profile; char *status; guint priority; guint n_sinks, n_sources; } GvcMixerCardProfile; typedef struct { char *port; char *human_port; guint priority; gint available; gint direction; GList *profiles; } GvcMixerCardPort; GType gvc_mixer_card_get_type (void); guint gvc_mixer_card_get_id (GvcMixerCard *card); guint gvc_mixer_card_get_index (GvcMixerCard *card); const char * gvc_mixer_card_get_name (GvcMixerCard *card); const char * gvc_mixer_card_get_icon_name (GvcMixerCard *card); GvcMixerCardProfile * gvc_mixer_card_get_profile (GvcMixerCard *card); const GList * gvc_mixer_card_get_profiles (GvcMixerCard *card); const GList * gvc_mixer_card_get_ports (GvcMixerCard *card); gboolean gvc_mixer_card_change_profile (GvcMixerCard *card, const char *profile); GIcon * gvc_mixer_card_get_gicon (GvcMixerCard *card); int gvc_mixer_card_profile_compare (GvcMixerCardProfile *a, GvcMixerCardProfile *b); /* private */ gboolean gvc_mixer_card_set_name (GvcMixerCard *card, const char *name); gboolean gvc_mixer_card_set_icon_name (GvcMixerCard *card, const char *name); gboolean gvc_mixer_card_set_profile (GvcMixerCard *card, const char *profile); gboolean gvc_mixer_card_set_profiles (GvcMixerCard *card, GList *profiles); gboolean gvc_mixer_card_set_ports (GvcMixerCard *stream, GList *ports); G_END_DECLS #endif /* __GVC_MIXER_CARD_H */ ./plugins/media-keys/gvc/gvc-mixer-source.c0000644000004100000410000001353412735467744021127 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include "gvc-mixer-source.h" #include "gvc-mixer-stream-private.h" #include "gvc-channel-map-private.h" #define GVC_MIXER_SOURCE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSourcePrivate)) struct GvcMixerSourcePrivate { gpointer dummy; }; static void gvc_mixer_source_class_init (GvcMixerSourceClass *klass); static void gvc_mixer_source_init (GvcMixerSource *mixer_source); static void gvc_mixer_source_finalize (GObject *object); G_DEFINE_TYPE (GvcMixerSource, gvc_mixer_source, GVC_TYPE_MIXER_STREAM) static gboolean gvc_mixer_source_push_volume (GvcMixerStream *stream, gpointer *op) { pa_operation *o; guint index; const GvcChannelMap *map; pa_context *context; const pa_cvolume *cv; index = gvc_mixer_stream_get_index (stream); map = gvc_mixer_stream_get_channel_map (stream); /* set the volume */ cv = gvc_channel_map_get_cvolume (map); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_source_volume_by_index (context, index, cv, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_source_volume_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } *op = o; return TRUE; } static gboolean gvc_mixer_source_change_is_muted (GvcMixerStream *stream, gboolean is_muted) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_source_mute_by_index (context, index, is_muted, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_source_mute_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static gboolean gvc_mixer_source_change_port (GvcMixerStream *stream, const char *port) { pa_operation *o; guint index; pa_context *context; index = gvc_mixer_stream_get_index (stream); context = gvc_mixer_stream_get_pa_context (stream); o = pa_context_set_source_port_by_index (context, index, port, NULL, NULL); if (o == NULL) { g_warning ("pa_context_set_source_port_by_index() failed: %s", pa_strerror(pa_context_errno(context))); return FALSE; } pa_operation_unref(o); return TRUE; } static void gvc_mixer_source_class_init (GvcMixerSourceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GvcMixerStreamClass *stream_class = GVC_MIXER_STREAM_CLASS (klass); object_class->finalize = gvc_mixer_source_finalize; stream_class->push_volume = gvc_mixer_source_push_volume; stream_class->change_is_muted = gvc_mixer_source_change_is_muted; stream_class->change_port = gvc_mixer_source_change_port; g_type_class_add_private (klass, sizeof (GvcMixerSourcePrivate)); } static void gvc_mixer_source_init (GvcMixerSource *source) { source->priv = GVC_MIXER_SOURCE_GET_PRIVATE (source); } static void gvc_mixer_source_finalize (GObject *object) { GvcMixerSource *mixer_source; g_return_if_fail (object != NULL); g_return_if_fail (GVC_IS_MIXER_SOURCE (object)); mixer_source = GVC_MIXER_SOURCE (object); g_return_if_fail (mixer_source->priv != NULL); G_OBJECT_CLASS (gvc_mixer_source_parent_class)->finalize (object); } /** * gvc_mixer_source_new: (skip) * @context: * @index: * @channel_map: * * Returns: */ GvcMixerStream * gvc_mixer_source_new (pa_context *context, guint index, GvcChannelMap *channel_map) { GObject *object; object = g_object_new (GVC_TYPE_MIXER_SOURCE, "pa-context", context, "index", index, "channel-map", channel_map, NULL); return GVC_MIXER_STREAM (object); } ./plugins/media-keys/gvc/gvc-mixer-event-role.h0000644000004100000410000000433412735467744021712 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_EVENT_ROLE_H #define __GVC_MIXER_EVENT_ROLE_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_EVENT_ROLE (gvc_mixer_event_role_get_type ()) #define GVC_MIXER_EVENT_ROLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRole)) #define GVC_MIXER_EVENT_ROLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass)) #define GVC_IS_MIXER_EVENT_ROLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_EVENT_ROLE)) #define GVC_IS_MIXER_EVENT_ROLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_EVENT_ROLE)) #define GVC_MIXER_EVENT_ROLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_EVENT_ROLE, GvcMixerEventRoleClass)) typedef struct GvcMixerEventRolePrivate GvcMixerEventRolePrivate; typedef struct { GvcMixerStream parent; GvcMixerEventRolePrivate *priv; } GvcMixerEventRole; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerEventRoleClass; GType gvc_mixer_event_role_get_type (void); GvcMixerStream * gvc_mixer_event_role_new (pa_context *context, const char *device, GvcChannelMap *channel_map); G_END_DECLS #endif /* __GVC_MIXER_EVENT_ROLE_H */ ./plugins/media-keys/gvc/gvc-mixer-source.h0000644000004100000410000000420412735467744021126 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * */ #ifndef __GVC_MIXER_SOURCE_H #define __GVC_MIXER_SOURCE_H #include #include "gvc-mixer-stream.h" G_BEGIN_DECLS #define GVC_TYPE_MIXER_SOURCE (gvc_mixer_source_get_type ()) #define GVC_MIXER_SOURCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSource)) #define GVC_MIXER_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass)) #define GVC_IS_MIXER_SOURCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVC_TYPE_MIXER_SOURCE)) #define GVC_IS_MIXER_SOURCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVC_TYPE_MIXER_SOURCE)) #define GVC_MIXER_SOURCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVC_TYPE_MIXER_SOURCE, GvcMixerSourceClass)) typedef struct GvcMixerSourcePrivate GvcMixerSourcePrivate; typedef struct { GvcMixerStream parent; GvcMixerSourcePrivate *priv; } GvcMixerSource; typedef struct { GvcMixerStreamClass parent_class; } GvcMixerSourceClass; GType gvc_mixer_source_get_type (void); GvcMixerStream * gvc_mixer_source_new (pa_context *context, guint index, GvcChannelMap *channel_map); G_END_DECLS #endif /* __GVC_MIXER_SOURCE_H */ ./plugins/media-keys/shortcuts-list.h0000644000004100000410000002337212735467744020166 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2001 Bastien Nocera * * 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. */ #ifndef __SHORTCUTS_LIST_H__ #define __SHORTCUTS_LIST_H__ #include "shell-keybinding-modes.h" #include "gsd-keygrab.h" #define SETTINGS_BINDING_DIR "org.gnome.settings-daemon.plugins.media-keys" #define INPUT_SETTINGS_BINDING_DIR "org.gnome.desktop.wm.keybindings" typedef enum { TOUCHPAD_KEY, TOUCHPAD_ON_KEY, TOUCHPAD_OFF_KEY, MUTE_KEY, VOLUME_DOWN_KEY, VOLUME_UP_KEY, MUTE_QUIET_KEY, VOLUME_DOWN_QUIET_KEY, VOLUME_UP_QUIET_KEY, MIC_MUTE_KEY, LOGOUT_KEY, EJECT_KEY, HOME_KEY, MEDIA_KEY, CALCULATOR_KEY, SEARCH_KEY, EMAIL_KEY, SCREENSAVER_KEY, HELP_KEY, SCREENSHOT_KEY, WINDOW_SCREENSHOT_KEY, AREA_SCREENSHOT_KEY, SCREENSHOT_CLIP_KEY, WINDOW_SCREENSHOT_CLIP_KEY, AREA_SCREENSHOT_CLIP_KEY, TERMINAL_KEY, WWW_KEY, PLAY_KEY, PAUSE_KEY, STOP_KEY, PREVIOUS_KEY, NEXT_KEY, REWIND_KEY, FORWARD_KEY, REPEAT_KEY, RANDOM_KEY, VIDEO_OUT_KEY, ROTATE_VIDEO_KEY, MAGNIFIER_KEY, SCREENREADER_KEY, ON_SCREEN_KEYBOARD_KEY, INCREASE_TEXT_KEY, DECREASE_TEXT_KEY, TOGGLE_CONTRAST_KEY, MAGNIFIER_ZOOM_IN_KEY, MAGNIFIER_ZOOM_OUT_KEY, POWER_KEY, SLEEP_KEY, SUSPEND_KEY, HIBERNATE_KEY, POWER_KEY_NO_DIALOG, SLEEP_KEY_NO_DIALOG, SUSPEND_KEY_NO_DIALOG, HIBERNATE_KEY_NO_DIALOG, SCREEN_BRIGHTNESS_UP_KEY, SCREEN_BRIGHTNESS_DOWN_KEY, KEYBOARD_BRIGHTNESS_UP_KEY, KEYBOARD_BRIGHTNESS_DOWN_KEY, KEYBOARD_BRIGHTNESS_TOGGLE_KEY, BATTERY_KEY, SWITCH_INPUT_SOURCE_KEY, SWITCH_INPUT_SOURCE_BACKWARD_KEY, CUSTOM_KEY } MediaKeyType; #define GSD_KEYBINDING_MODE_LAUNCHER (SHELL_KEYBINDING_MODE_NORMAL | \ SHELL_KEYBINDING_MODE_OVERVIEW) #define SCREENSAVER_MODE SHELL_KEYBINDING_MODE_ALL & ~SHELL_KEYBINDING_MODE_UNLOCK_SCREEN #define POWER_KEYS_MODE (SHELL_KEYBINDING_MODE_NORMAL | \ SHELL_KEYBINDING_MODE_OVERVIEW | \ SHELL_KEYBINDING_MODE_LOGIN_SCREEN) #define POWER_KEYS_MODE_NO_DIALOG (SHELL_KEYBINDING_MODE_LOCK_SCREEN | \ SHELL_KEYBINDING_MODE_UNLOCK_SCREEN) static struct { MediaKeyType key_type; const char *settings_key; const char *key_name; const char *hard_coded; ShellKeyBindingMode modes; } media_keys[] = { { TOUCHPAD_KEY, NULL, N_("Touchpad toggle") ,"XF86TouchpadToggle", SHELL_KEYBINDING_MODE_ALL }, { TOUCHPAD_ON_KEY, NULL, N_("Touchpad On"), "XF86TouchpadOn", SHELL_KEYBINDING_MODE_ALL }, { TOUCHPAD_OFF_KEY, NULL, N_("Touchpad Off"), "XF86TouchpadOff", SHELL_KEYBINDING_MODE_ALL }, { MUTE_KEY, "volume-mute", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { VOLUME_DOWN_KEY, "volume-down", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { VOLUME_UP_KEY, "volume-up", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { MIC_MUTE_KEY, NULL, N_("Microphone Mute"), "F20", SHELL_KEYBINDING_MODE_ALL }, { MIC_MUTE_KEY, NULL, N_("Microphone Mute"), "XF86AudioMicMute", SHELL_KEYBINDING_MODE_ALL }, { MUTE_QUIET_KEY, NULL, N_("Quiet Volume Mute"), "XF86AudioMute", SHELL_KEYBINDING_MODE_ALL }, { VOLUME_DOWN_QUIET_KEY, NULL, N_("Quiet Volume Down"), "XF86AudioLowerVolume", SHELL_KEYBINDING_MODE_ALL }, { VOLUME_UP_QUIET_KEY, NULL, N_("Quiet Volume Up"), "XF86AudioRaiseVolume", SHELL_KEYBINDING_MODE_ALL }, { LOGOUT_KEY, "logout", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { EJECT_KEY, "eject", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { HOME_KEY, "home", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { MEDIA_KEY, "media", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { CALCULATOR_KEY, "calculator", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { SEARCH_KEY, "search", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { EMAIL_KEY, "email", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { SCREENSAVER_KEY, "screensaver", NULL, NULL, SCREENSAVER_MODE }, { SCREENSAVER_KEY, NULL, N_("Lock Screen"), "XF86ScreenSaver", SCREENSAVER_MODE }, { HELP_KEY, "help", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { SCREENSHOT_KEY, "screenshot", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { WINDOW_SCREENSHOT_KEY, "window-screenshot", NULL, NULL, SHELL_KEYBINDING_MODE_NORMAL }, { AREA_SCREENSHOT_KEY, "area-screenshot", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { SCREENSHOT_CLIP_KEY, "screenshot-clip", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { WINDOW_SCREENSHOT_CLIP_KEY, "window-screenshot-clip", NULL, NULL, SHELL_KEYBINDING_MODE_NORMAL }, { AREA_SCREENSHOT_CLIP_KEY, "area-screenshot-clip", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { TERMINAL_KEY, "terminal", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { WWW_KEY, "www", NULL, NULL, GSD_KEYBINDING_MODE_LAUNCHER }, { PLAY_KEY, "play", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { PAUSE_KEY, "pause", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { STOP_KEY, "stop", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { PREVIOUS_KEY, "previous", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { NEXT_KEY, "next", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { REWIND_KEY, NULL, N_("Rewind"), "XF86AudioRewind", SHELL_KEYBINDING_MODE_ALL }, { FORWARD_KEY, NULL, N_("Forward"), "XF86AudioForward", SHELL_KEYBINDING_MODE_ALL }, { REPEAT_KEY, NULL, N_("Repeat"), "XF86AudioRepeat", SHELL_KEYBINDING_MODE_ALL }, { RANDOM_KEY, NULL, N_("Random Play"), "XF86AudioRandomPlay", SHELL_KEYBINDING_MODE_ALL }, { VIDEO_OUT_KEY, NULL, N_("Video Out"), "p", SHELL_KEYBINDING_MODE_ALL }, /* Key code of the XF86Display key (Fn-F7 on Thinkpads, Fn-F4 on HP machines, etc.) */ { VIDEO_OUT_KEY, NULL, N_("Video Out"), "XF86Display", SHELL_KEYBINDING_MODE_ALL }, /* Key code of the XF86RotateWindows key (present on some tablets) */ { ROTATE_VIDEO_KEY, NULL, N_("Rotate Screen"), "XF86RotateWindows", SHELL_KEYBINDING_MODE_NORMAL }, { MAGNIFIER_KEY, "magnifier", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { SCREENREADER_KEY, "screenreader", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { ON_SCREEN_KEYBOARD_KEY, "on-screen-keyboard", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { INCREASE_TEXT_KEY, "increase-text-size", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { DECREASE_TEXT_KEY, "decrease-text-size", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { TOGGLE_CONTRAST_KEY, "toggle-contrast", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { MAGNIFIER_ZOOM_IN_KEY, "magnifier-zoom-in", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { MAGNIFIER_ZOOM_OUT_KEY, "magnifier-zoom-out", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { POWER_KEY, NULL, N_("Power Off"), "XF86PowerOff", POWER_KEYS_MODE }, /* the kernel / Xorg names really are like this... */ /* translators: "Sleep" means putting the machine to sleep, either through hibernate or suspend */ { SLEEP_KEY, NULL, N_("Sleep"), "XF86Suspend", POWER_KEYS_MODE }, { SUSPEND_KEY, NULL, N_("Suspend"), "XF86Sleep", POWER_KEYS_MODE }, { HIBERNATE_KEY, NULL, N_("Hibernate"), "XF86Hibernate", POWER_KEYS_MODE }, { POWER_KEY_NO_DIALOG, NULL, N_("Power Off"), "XF86PowerOff", POWER_KEYS_MODE_NO_DIALOG }, /* the kernel / Xorg names really are like this... */ /* translators: "Sleep" means putting the machine to sleep, either through hibernate or suspend */ { SLEEP_KEY_NO_DIALOG, NULL, N_("Sleep"), "XF86Suspend", POWER_KEYS_MODE_NO_DIALOG }, { SUSPEND_KEY_NO_DIALOG, NULL, N_("Suspend"), "XF86Sleep", POWER_KEYS_MODE_NO_DIALOG }, { HIBERNATE_KEY_NO_DIALOG, NULL, N_("Hibernate"), "XF86Hibernate", POWER_KEYS_MODE_NO_DIALOG }, { SCREEN_BRIGHTNESS_UP_KEY, NULL, N_("Brightness Up"), "XF86MonBrightnessUp", SHELL_KEYBINDING_MODE_ALL }, { SCREEN_BRIGHTNESS_DOWN_KEY, NULL, N_("Brightness Down"), "XF86MonBrightnessDown", SHELL_KEYBINDING_MODE_ALL }, { KEYBOARD_BRIGHTNESS_UP_KEY, NULL, N_("Keyboard Brightness Up"), "XF86KbdBrightnessUp", SHELL_KEYBINDING_MODE_ALL }, { KEYBOARD_BRIGHTNESS_DOWN_KEY, NULL, N_("Keyboard Brightness Down"), "XF86KbdBrightnessDown", SHELL_KEYBINDING_MODE_ALL }, { KEYBOARD_BRIGHTNESS_TOGGLE_KEY, NULL, N_("Keyboard Brightness Toggle"), "XF86KbdLightOnOff", SHELL_KEYBINDING_MODE_ALL }, { BATTERY_KEY, NULL, N_("Battery Status"), "XF86Battery", GSD_KEYBINDING_MODE_LAUNCHER }, { SWITCH_INPUT_SOURCE_KEY, "switch-input-source", NULL, NULL, SHELL_KEYBINDING_MODE_ALL }, { SWITCH_INPUT_SOURCE_BACKWARD_KEY, "switch-input-source-backward", NULL, NULL, SHELL_KEYBINDING_MODE_ALL } }; #undef SCREENSAVER_MODE #endif /* __SHORTCUTS_LIST_H__ */ ./plugins/media-keys/Makefile.am0000644000004100000410000000654612735467744017046 0ustar www-datawww-dataicondir = $(datadir)/icons/hicolor context = actions plugin_name = media-keys NULL = SUBDIRS = gvc plugin_LTLIBRARIES = libmedia-keys.la BUILT_SOURCES = \ gsd-marshal.h \ gsd-marshal.c \ shell-key-grabber.c \ shell-key-grabber.h \ $(NULL) gsd-marshal.c: gsd-marshal.list $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=gsd_marshal $< --header --body --internal > $@ gsd-marshal.h: gsd-marshal.list $(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=gsd_marshal $< --header --internal > $@ shell-key-grabber.c: shell-key-grabber.h shell-key-grabber.h: Makefile.am org.gnome.ShellKeyGrabber.xml gdbus-codegen --interface-prefix org.gnome. \ --generate-c-code shell-key-grabber \ --c-namespace Shell \ $(srcdir)/org.gnome.ShellKeyGrabber.xml libmedia_keys_la_SOURCES = \ what-did-you-plug-in/pa-backend.c \ what-did-you-plug-in/dialog-window.c \ gsd-media-keys-plugin.c \ gsd-media-keys-manager.h \ gsd-media-keys-manager.c \ gsd-screenshot-utils.h \ gsd-screenshot-utils.c \ shortcuts-list.h \ shell-keybinding-modes.h \ $(BUILT_SOURCES) \ $(NULL) libmedia_keys_la_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -I$(top_srcdir)/plugins/media-keys/gvc \ -I$(top_srcdir)/plugins/media-keys/what-did-you-plug-in \ -DBINDIR=\"$(bindir)\" \ -DPIXMAPDIR=\""$(pkgdatadir)"\" \ -DGTKBUILDERDIR=\""$(pkgdatadir)"\" \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libmedia_keys_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(MEDIA_KEYS_CFLAGS) \ $(AM_CFLAGS) libmedia_keys_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libmedia_keys_la_LIBADD = \ $(top_builddir)/plugins/common/libcommon.la \ $(top_builddir)/plugins/media-keys/gvc/libgvc.la \ $(MEDIA_KEYS_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ -lm plugin_in_files = \ media-keys.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) libexec_PROGRAMS = usd-test-media-keys usd_test_media_keys_SOURCES = \ gsd-media-keys-manager.c \ gsd-media-keys-manager.h \ gsd-screenshot-utils.h \ gsd-screenshot-utils.c \ test-media-keys.c \ what-did-you-plug-in/pa-backend.c \ what-did-you-plug-in/dialog-window.c \ $(BUILT_SOURCES) \ $(NULL) usd_test_media_keys_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -I$(top_srcdir)/plugins/media-keys/gvc \ -I$(top_srcdir)/plugins/media-keys/what-did-you-plug-in \ -DBINDIR=\"$(bindir)\" \ -DPIXMAPDIR=\""$(pkgdatadir)"\" \ -DGTKBUILDERDIR=\""$(pkgdatadir)"\" \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) usd_test_media_keys_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(MEDIA_KEYS_CFLAGS) \ $(AM_CFLAGS) usd_test_media_keys_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(top_builddir)/plugins/media-keys/gvc/libgvc.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ $(MEDIA_KEYS_LIBS) \ -lm EXTRA_DIST = \ gsd-marshal.list \ README.media-keys-API \ org.gnome.ShellKeyGrabber.xml \ $(plugin_in_files) CLEANFILES = \ $(BUILT_SOURCES) \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/media-keys/what-did-you-plug-in/0000755000004100000410000000000012735467744020663 5ustar www-datawww-data./plugins/media-keys/what-did-you-plug-in/pa-backend.h0000644000004100000410000000105112735467744023016 0ustar www-datawww-data#ifndef __PA_BACKEND_H__ #define __PA_BACKEND_H__ #include #include typedef struct pa_backend pa_backend; typedef void (*pa_backend_cb)(bool headsetmic, bool headphonemic, void *userdata); pa_backend *pa_backend_new(pa_backend_cb cb, void *cb_userdata); void pa_backend_free(pa_backend *p); void pa_backend_card_changed(pa_backend *p, const pa_card_info *i); void pa_backend_set_context(pa_backend *p, const pa_context *c); void pa_backend_set_port(pa_backend *p, const char *portname, bool is_output); #endif ./plugins/media-keys/what-did-you-plug-in/dialog-window.h0000644000004100000410000000067512735467744023610 0ustar www-datawww-data#ifndef __DIALOG_WINDOW_H__ #define __DIALOG_WINDOW_H__ #include #define WDYPI_DIALOG_CANCELLED 0 #define WDYPI_DIALOG_HEADPHONES 1 #define WDYPI_DIALOG_HEADSET 2 #define WDYPI_DIALOG_MICROPHONE 3 #define WDYPI_DIALOG_SOUND_SETTINGS 4 typedef void (*wdypi_dialog_cb)(int response, void *userdata); void wdypi_dialog_run(bool show_headset, bool show_mic, wdypi_dialog_cb cb, void *cb_userdata); void wdypi_dialog_kill(); #endif ./plugins/media-keys/what-did-you-plug-in/pa-backend.c0000644000004100000410000001644612735467744023027 0ustar www-datawww-data#include #include #include #include #include #include #include "pa-backend.h" struct pa_backend { const pa_context *context; pa_backend_cb dialog_cb; void *cb_userdata; int headset_card; bool headset_plugged_in; bool has_headsetmic; bool has_headphonemic; const char *sink_port_name_to_set; const char *source_port_name_to_set; }; void pa_backend_set_context(pa_backend *p, const pa_context *c) { p->context = c; } pa_backend *pa_backend_new(pa_backend_cb cb, void *cb_userdata) { pa_backend *p = calloc(1, sizeof(*p)); if (!p) return NULL; p->headset_card = -1; p->dialog_cb = cb; p->cb_userdata = cb_userdata; return p; } typedef struct headset_ports { const pa_card_port_info *headphones, *headsetmic, *headphonemic; } headset_ports; /* TODO: Check if we still need this with the changed PA port names In PulseAudio ports will show up with the following names: Headphones - analog-output-headphones Headset mic - analog-input-headset-mic (was: analog-input-microphone-headset) Jack in mic-in mode - analog-input-headphone-mic (was: analog-input-microphone) However, since regular mics also show up as analog-input-microphone, we need to check for certain controls on alsa mixer level too, to know if we deal with a separate mic jack, or a multi-function jack with a mic-in mode (also called "headphone mic"). We check for the following names: Headphone Mic Jack - indicates headphone and mic-in mode share the same jack, i e, not two separate jacks. Hardware cannot distinguish between a headphone and a mic. Headset Mic Phantom Jack - indicates headset jack where hardware can not distinguish between headphones and headsets Headset Mic Jack - indicates headset jack where hardware can distinguish between headphones and headsets. There is no use popping up a dialog in this case, unless we already need to do this for the mic-in mode. */ static headset_ports get_headset_ports(const pa_card_info *c) { headset_ports h = {NULL, NULL, NULL}; int i; for (i = 0; i < c->n_ports; i++) { pa_card_port_info *p = c->ports[i]; if (!strcmp(p->name, "analog-output-headphones")) h.headphones = p; else if (!strcmp(p->name, "analog-input-headset-mic")) h.headsetmic = p; else if (!strcmp(p->name, "analog-input-headphone-mic")) h.headphonemic = p; } return h; } static bool verify_alsa_card(int cardindex, bool *headsetmic, bool *headphonemic) { char ctlstr[20]; snd_hctl_t *hctl; snd_ctl_elem_id_t *id; int err; *headsetmic = false; *headphonemic = false; snprintf(ctlstr, sizeof(ctlstr), "hw:%i", cardindex); if ((err = snd_hctl_open(&hctl, ctlstr, 0)) < 0) { g_warning("snd_hctl_open failed: %s", snd_strerror(err)); return false; } if ((err = snd_hctl_load(hctl)) < 0) { g_warning("snd_hctl_load failed: %s", snd_strerror(err)); snd_hctl_close(hctl); return false; } snd_ctl_elem_id_alloca(&id); snd_ctl_elem_id_clear(id); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD); snd_ctl_elem_id_set_name(id, "Headphone Mic Jack"); if (snd_hctl_find_elem(hctl, id)) *headphonemic = true; snd_ctl_elem_id_clear(id); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD); snd_ctl_elem_id_set_name(id, "Headset Mic Phantom Jack"); if (snd_hctl_find_elem(hctl, id)) *headsetmic = true; if (*headphonemic) { snd_ctl_elem_id_clear(id); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD); snd_ctl_elem_id_set_name(id, "Headset Mic Jack"); if (snd_hctl_find_elem(hctl, id)) *headsetmic = true; } snd_hctl_close(hctl); return *headsetmic || *headphonemic; } void pa_backend_card_changed(pa_backend *p, const pa_card_info *i) { headset_ports h; bool start_dialog = false, stop_dialog = false; h = get_headset_ports(i); if (!h.headphones || (!h.headsetmic && !h.headphonemic)) return; /* Not a headset jack */ if (p->headset_card != (int) i->index) { int cardindex = 0; bool hsmic, hpmic; const char *s = pa_proplist_gets(i->proplist, "alsa.card"); if (!s) return; cardindex = strtol(s, NULL, 10); if (cardindex == 0 && strcmp(s, "0")) return; if (!verify_alsa_card(cardindex, &hsmic, &hpmic)) return; p->headset_card = (int) i->index; p->has_headsetmic = hsmic && h.headsetmic; p->has_headphonemic = hpmic && h.headphonemic; } else { start_dialog = p->dialog_cb && (h.headphones->available != PA_PORT_AVAILABLE_NO) && !p->headset_plugged_in; stop_dialog = p->dialog_cb && (h.headphones->available == PA_PORT_AVAILABLE_NO) && p->headset_plugged_in; } p->headset_plugged_in = h.headphones->available != PA_PORT_AVAILABLE_NO; if (start_dialog) p->dialog_cb(p->has_headsetmic, p->has_headphonemic, p->cb_userdata); else if (stop_dialog) p->dialog_cb(false, false, p->cb_userdata); } /* We need to re-enumerate sources and sinks every time the user makes a choice, because they can change due to use interaction in other software (or policy changes inside PulseAudio). Enumeration means PulseAudio will do a series of callbacks, one for every source/sink. Set the port when we find the correct source/sink. */ static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) { pa_backend *p = userdata; pa_operation *o; int j; const char *s = p->sink_port_name_to_set; if (eol) return; if (i->card != p->headset_card) return; if (i->active_port && !strcmp(i->active_port->name, s)) return; for (j = 0; j < i->n_ports; j++) if (!strcmp(i->ports[j]->name, s)) break; if (j >= i->n_ports) return; o = pa_context_set_sink_port_by_index(c, i->index, s, NULL, NULL); if (o) pa_operation_unref(o); } static void source_info_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) { pa_backend *p = userdata; pa_operation *o; int j; const char *s = p->source_port_name_to_set; if (eol) return; if (i->card != p->headset_card) return; if (i->active_port && !strcmp(i->active_port->name, s)) return; for (j = 0; j < i->n_ports; j++) if (!strcmp(i->ports[j]->name, s)) break; if (j >= i->n_ports) return; o = pa_context_set_source_port_by_index(c, i->index, s, NULL, NULL); if (o) pa_operation_unref(o); } void pa_backend_set_port(pa_backend *p, const char *portname, bool is_output) { pa_operation *o; if (is_output) { p->sink_port_name_to_set = portname; o = pa_context_get_sink_info_list(p->context, sink_info_cb, p); } else { p->source_port_name_to_set = portname; o = pa_context_get_source_info_list(p->context, source_info_cb, p); } if (o) { pa_operation_unref(o); } } void pa_backend_free(pa_backend *p) { if (!p) return; free(p); } ./plugins/media-keys/what-did-you-plug-in/dialog-window.c0000644000004100000410000001131512735467744023574 0ustar www-datawww-data#include #include #include #include #include #include "dialog-window.h" typedef struct dialog_window { GtkWidget *dialog; GtkWidget *ca_box; GtkWidget *v_box; GtkWidget *icon_box; GtkWidget *btn_box; GtkWidget *label; GtkWidget *cancel_btn; GtkWidget *settings_btn; GtkWidget *hp_btn; GtkWidget *hs_btn; GtkWidget *mic_btn; int button_response; wdypi_dialog_cb cb; void *cb_userdata; } dialog_window; /* It's okay to have a global here - we should never show more than one dialog */ dialog_window dlg; void wdypi_dialog_kill() { dialog_window *d = &dlg; if (d->dialog) { gtk_widget_destroy(d->dialog); d->dialog = NULL; } } static void on_iconbtn_clicked (GtkWidget *widget, gpointer data) { dialog_window *d = &dlg; d->button_response = (ssize_t) data; gtk_dialog_response(GTK_DIALOG(d->dialog), GTK_RESPONSE_OK); } static void on_response (GtkWidget *widget, gint response_id, gpointer data) { int resp; dialog_window *d = data; if (!d->cb) return; switch (response_id) { case GTK_RESPONSE_YES: resp = WDYPI_DIALOG_SOUND_SETTINGS; break; case GTK_RESPONSE_OK: resp = d->button_response; break; default: resp = WDYPI_DIALOG_CANCELLED; } d->cb(resp, d->cb_userdata); wdypi_dialog_kill(); } static GtkWidget * create_icon_button(int response, const char *name, const char *icon) { GtkWidget *btn = gtk_button_new(); GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); GtkWidget *lbl = gtk_label_new(name); GtkWidget *img = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_DIALOG); gtk_box_pack_end(GTK_BOX(box), lbl, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(box), img, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(box), 6); gtk_container_add(GTK_CONTAINER(btn), box); g_signal_connect(btn, "clicked", G_CALLBACK(on_iconbtn_clicked), (void*) (ssize_t) response); return btn; } static void dialog_create(dialog_window *d, bool show_headset, bool show_mic) { guint32 timestamp; d->dialog = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(d->dialog), _("Unknown Audio Device")); gtk_container_set_border_width(GTK_CONTAINER(d->dialog), 6); gtk_window_set_icon_name(GTK_WINDOW(d->dialog), "audio-headphones"); gtk_window_set_resizable(GTK_WINDOW(d->dialog), FALSE); d->ca_box = gtk_dialog_get_content_area(GTK_DIALOG(d->dialog)); d->v_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_container_set_border_width(GTK_CONTAINER(d->v_box), 5); d->icon_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_set_homogeneous(GTK_BOX(d->icon_box), TRUE); d->label = gtk_label_new(_("What kind of device did you plug in?")); gtk_misc_set_alignment(GTK_MISC(d->label), 0.5, 0.5); gtk_box_pack_start(GTK_CONTAINER(d->v_box), d->label, FALSE, FALSE, 6); d->hp_btn = create_icon_button(WDYPI_DIALOG_HEADPHONES, _("Headphones"), "audio-headphones"); gtk_box_pack_start(GTK_BOX(d->icon_box), d->hp_btn, FALSE, TRUE, 0); if (show_headset) { d->hs_btn = create_icon_button(WDYPI_DIALOG_HEADSET, _("Headset"), "audio-headset"); gtk_box_pack_start(GTK_BOX(d->icon_box), d->hs_btn, FALSE, TRUE, 0); } if (show_mic) { d->mic_btn = create_icon_button(WDYPI_DIALOG_MICROPHONE, _("Microphone"), "audio-input-microphone"); gtk_box_pack_start(GTK_BOX(d->icon_box), d->mic_btn, FALSE, TRUE, 0); } gtk_box_pack_start(GTK_CONTAINER(d->v_box), d->icon_box, FALSE, FALSE, 6); d->cancel_btn = gtk_dialog_add_button(GTK_DIALOG(d->dialog), _("Cancel"), GTK_RESPONSE_CANCEL); d->settings_btn = gtk_dialog_add_button(GTK_DIALOG(d->dialog), _("Sound Settings…"), GTK_RESPONSE_YES); gtk_container_add(GTK_CONTAINER(d->ca_box), d->v_box); g_signal_connect(d->dialog, "response", G_CALLBACK(on_response), d); gtk_widget_show_all(d->dialog); /* This event did not originate from a pointer or key movement, so we can't use GDK_CURRENT_TIME (or just gtk_window_present) here. There is also no other source timestamp to rely on. Without a timestamp, the dialog would often show up behind other windows. */ timestamp = gdk_x11_get_server_time(gtk_widget_get_window(GTK_WIDGET(d->dialog))); gtk_window_present_with_time(GTK_WINDOW(d->dialog), timestamp); } void wdypi_dialog_run(bool show_headset, bool show_mic, wdypi_dialog_cb cb, void *cb_userdata) { dialog_window *d = &dlg; wdypi_dialog_kill(); d->cb = cb; d->cb_userdata = cb_userdata; dialog_create(d, show_headset, show_mic); } ./plugins/media-keys/gsd-media-keys-manager.h0000644000004100000410000000522412735467744021366 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_MEDIA_KEYS_MANAGER_H #define __GSD_MEDIA_KEYS_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_MEDIA_KEYS_MANAGER (gsd_media_keys_manager_get_type ()) #define GSD_MEDIA_KEYS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_MEDIA_KEYS_MANAGER, GsdMediaKeysManager)) #define GSD_MEDIA_KEYS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_MEDIA_KEYS_MANAGER, GsdMediaKeysManagerClass)) #define GSD_IS_MEDIA_KEYS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_MEDIA_KEYS_MANAGER)) #define GSD_IS_MEDIA_KEYS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_MEDIA_KEYS_MANAGER)) #define GSD_MEDIA_KEYS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_MEDIA_KEYS_MANAGER, GsdMediaKeysManagerClass)) typedef struct GsdMediaKeysManagerPrivate GsdMediaKeysManagerPrivate; typedef struct { GObject parent; GsdMediaKeysManagerPrivate *priv; } GsdMediaKeysManager; typedef struct { GObjectClass parent_class; void (* media_player_key_pressed) (GsdMediaKeysManager *manager, const char *application, const char *key); } GsdMediaKeysManagerClass; GType gsd_media_keys_manager_get_type (void); GsdMediaKeysManager * gsd_media_keys_manager_new (void); gboolean gsd_media_keys_manager_start (GsdMediaKeysManager *manager, GError **error); void gsd_media_keys_manager_stop (GsdMediaKeysManager *manager); G_END_DECLS #endif /* __GSD_MEDIA_KEYS_MANAGER_H */ ./plugins/media-keys/test-media-keys.c0000644000004100000410000000033512735467744020151 0ustar www-datawww-data#define NEW gsd_media_keys_manager_new #define START gsd_media_keys_manager_start #define STOP gsd_media_keys_manager_stop #define MANAGER GsdMediaKeysManager #include "gsd-media-keys-manager.h" #include "test-plugin.h" ./plugins/media-keys/org.gnome.ShellKeyGrabber.xml0000644000004100000410000000147412735467744022426 0ustar www-datawww-data ./plugins/media-keys/gsd-marshal.list0000644000004100000410000000002312735467744020071 0ustar www-datawww-dataVOID:STRING,STRING ./plugins/media-keys/gsd-media-keys-plugin.c0000644000004100000410000000203312735467744021240 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-media-keys-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdMediaKeys, gsd_media_keys) ./plugins/media-keys/shell-keybinding-modes.h0000644000004100000410000000324312735467744021507 0ustar www-datawww-data/** * ShellKeyBindingMode: * @SHELL_KEYBINDING_MODE_NONE: block keybinding * @SHELL_KEYBINDING_MODE_NORMAL: allow keybinding when in window mode, * e.g. when the focus is in an application window * @SHELL_KEYBINDING_MODE_OVERVIEW: allow keybinding while the overview * is active * @SHELL_KEYBINDING_MODE_LOCK_SCREEN: allow keybinding when the screen * is locked, e.g. when the screen shield is shown * @SHELL_KEYBINDING_MODE_UNLOCK_SCREEN: allow keybinding in the unlock * dialog * @SHELL_KEYBINDING_MODE_LOGIN_SCREEN: allow keybinding in the login screen * @SHELL_KEYBINDING_MODE_MESSAGE_TRAY: allow keybinding while the message * tray is popped up * @SHELL_KEYBINDING_MODE_SYSTEM_MODAL: allow keybinding when a system modal * dialog (e.g. authentification or session dialogs) is open * @SHELL_KEYBINDING_MODE_LOOKING_GLASS: allow keybinding in looking glass * @SHELL_KEYBINDING_MODE_TOPBAR_POPUP: allow keybinding while a top bar menu * is open * @SHELL_KEYBINDING_MODE_ALL: always allow keybinding * * Controls in which GNOME Shell states a keybinding should be handled. */ typedef enum { SHELL_KEYBINDING_MODE_NONE = 0, SHELL_KEYBINDING_MODE_NORMAL = 1 << 0, SHELL_KEYBINDING_MODE_OVERVIEW = 1 << 1, SHELL_KEYBINDING_MODE_LOCK_SCREEN = 1 << 2, SHELL_KEYBINDING_MODE_UNLOCK_SCREEN = 1 << 3, SHELL_KEYBINDING_MODE_LOGIN_SCREEN = 1 << 4, SHELL_KEYBINDING_MODE_MESSAGE_TRAY = 1 << 5, SHELL_KEYBINDING_MODE_SYSTEM_MODAL = 1 << 6, SHELL_KEYBINDING_MODE_LOOKING_GLASS = 1 << 7, SHELL_KEYBINDING_MODE_TOPBAR_POPUP = 1 << 8, SHELL_KEYBINDING_MODE_ALL = ~SHELL_KEYBINDING_MODE_NONE } ShellKeyBindingMode; ./plugins/media-keys/gsd-screenshot-utils.c0000644000004100000410000002314512735467744021236 0ustar www-datawww-data/* gsd-screenshot-utils.c - utilities to take screenshots * * Copyright (C) 2012 Red Hat, Inc. * * Adapted from gnome-screenshot code, which is * Copyright (C) 2001-2006 Jonathan Blandford * Copyright (C) 2006 Emmanuele Bassi * Copyright (C) 2008-2012 Cosimo Cecchi * * 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, */ #include #include #include #include #include #include #include #include "gsd-screenshot-utils.h" #define SHELL_SCREENSHOT_BUS_NAME "org.gnome.Shell" #define SHELL_SCREENSHOT_BUS_PATH "/org/gnome/Shell/Screenshot" #define SHELL_SCREENSHOT_BUS_IFACE "org.gnome.Shell.Screenshot" typedef enum { SCREENSHOT_TYPE_SCREEN, SCREENSHOT_TYPE_WINDOW, SCREENSHOT_TYPE_AREA } ScreenshotType; typedef struct { ScreenshotType type; gboolean copy_to_clipboard; GdkRectangle area_selection; gchar *save_filename; gchar *used_filename; GDBusConnection *connection; } ScreenshotContext; static void screenshot_play_sound_effect (const gchar *event_id, const gchar *event_desc) { ca_context *c; c = ca_gtk_context_get (); ca_context_play (c, 0, CA_PROP_EVENT_ID, event_id, CA_PROP_EVENT_DESCRIPTION, event_desc, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", NULL); } static void screenshot_context_free (ScreenshotContext *ctx) { g_free (ctx->save_filename); g_free (ctx->used_filename); g_clear_object (&ctx->connection); g_slice_free (ScreenshotContext, ctx); } static void screenshot_context_error (ScreenshotContext *ctx, GError *error, const gchar *warning_format) { screenshot_play_sound_effect ("dialog-error", _("Unable to capture a screenshot")); g_warning (warning_format, error->message); g_error_free (error); } static void screenshot_save_to_recent (ScreenshotContext *ctx) { GFile *file = g_file_new_for_path (ctx->used_filename); gchar *uri = g_file_get_uri (file); gtk_recent_manager_add_item (gtk_recent_manager_get_default (), uri); g_free (uri); g_object_unref (file); } static void screenshot_save_to_clipboard (ScreenshotContext *ctx) { GdkPixbuf *screenshot; GtkClipboard *clipboard; GError *error = NULL; screenshot = gdk_pixbuf_new_from_file (ctx->used_filename, &error); if (error != NULL) { screenshot_context_error (ctx, error, "Failed to save a screenshot to clipboard: %s\n"); return; } screenshot_play_sound_effect ("screen-capture", _("Screenshot taken")); clipboard = gtk_clipboard_get_for_display (gdk_display_get_default (), GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_image (clipboard, screenshot); /* remove the temporary file created by the shell */ g_unlink (ctx->used_filename); g_object_unref (screenshot); } static void bus_call_ready_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; ScreenshotContext *ctx = user_data; GVariant *variant; gboolean success; variant = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error); if (error != NULL) { screenshot_context_error (ctx, error, "Failed to save a screenshot: %s\n"); screenshot_context_free (ctx); return; } g_variant_get (variant, "(bs)", &success, &ctx->used_filename); if (success) { if (ctx->copy_to_clipboard) { screenshot_save_to_clipboard (ctx); } else { screenshot_play_sound_effect ("screen-capture", _("Screenshot taken")); screenshot_save_to_recent (ctx); } } screenshot_context_free (ctx); g_variant_unref (variant); } static void screenshot_call_shell (ScreenshotContext *ctx) { const gchar *method_name; GVariant *method_params; if (ctx->type == SCREENSHOT_TYPE_SCREEN) { method_name = "Screenshot"; method_params = g_variant_new ("(bbs)", FALSE, /* include pointer */ TRUE, /* flash */ ctx->save_filename); } else if (ctx->type == SCREENSHOT_TYPE_WINDOW) { method_name = "ScreenshotWindow"; method_params = g_variant_new ("(bbbs)", TRUE, /* include border */ FALSE, /* include pointer */ TRUE, /* flash */ ctx->save_filename); } else { method_name = "ScreenshotArea"; method_params = g_variant_new ("(iiiibs)", ctx->area_selection.x, ctx->area_selection.y, ctx->area_selection.width, ctx->area_selection.height, TRUE, /* flash */ ctx->save_filename); } g_dbus_connection_call (ctx->connection, SHELL_SCREENSHOT_BUS_NAME, SHELL_SCREENSHOT_BUS_PATH, SHELL_SCREENSHOT_BUS_IFACE, method_name, method_params, NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, bus_call_ready_cb, ctx); } static void area_selection_ready_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GdkRectangle rectangle; ScreenshotContext *ctx = user_data; GVariant *geometry; geometry = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, NULL); /* cancelled by the user */ if (!geometry) { screenshot_context_free (ctx); return; } g_variant_get (geometry, "(iiii)", &rectangle.x, &rectangle.y, &rectangle.width, &rectangle.height); ctx->area_selection = rectangle; screenshot_call_shell (ctx); g_variant_unref (geometry); } static void bus_connection_ready_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GError *error = NULL; ScreenshotContext *ctx = user_data; ctx->connection = g_bus_get_finish (res, &error); if (error != NULL) { screenshot_context_error (ctx, error, "Failed to save a screenshot: %s\n"); screenshot_context_free (ctx); return; } if (ctx->type == SCREENSHOT_TYPE_AREA) g_dbus_connection_call (ctx->connection, SHELL_SCREENSHOT_BUS_NAME, SHELL_SCREENSHOT_BUS_PATH, SHELL_SCREENSHOT_BUS_IFACE, "SelectArea", NULL, NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, area_selection_ready_cb, ctx); else screenshot_call_shell (ctx); } static void screenshot_take (ScreenshotContext *ctx) { g_bus_get (G_BUS_TYPE_SESSION, NULL, bus_connection_ready_cb, ctx); } static gchar * screenshot_build_tmp_path (void) { gchar *path; gint fd; fd = g_file_open_tmp ("gnome-settings-daemon-screenshot-XXXXXX", &path, NULL); close (fd); return path; } static gchar * screenshot_build_filename (void) { char *file_name, *origin; GDateTime *d; d = g_date_time_new_now_local (); origin = g_date_time_format (d, "%Y-%m-%d %H:%M:%S"); g_date_time_unref (d); /* translators: this is the name of the file that gets made up * with the screenshot */ file_name = g_strdup_printf (_("Screenshot from %s"), origin); g_free (origin); return file_name; } static void screenshot_check_name_ready (ScreenshotContext *ctx) { if (ctx->copy_to_clipboard) ctx->save_filename = screenshot_build_tmp_path (); else ctx->save_filename = screenshot_build_filename (); screenshot_take (ctx); } void gsd_screenshot_take (MediaKeyType key_type) { ScreenshotContext *ctx = g_slice_new0 (ScreenshotContext); ctx->copy_to_clipboard = (key_type == SCREENSHOT_CLIP_KEY || key_type == WINDOW_SCREENSHOT_CLIP_KEY || key_type == AREA_SCREENSHOT_CLIP_KEY); switch (key_type) { case SCREENSHOT_KEY: case SCREENSHOT_CLIP_KEY: ctx->type = SCREENSHOT_TYPE_SCREEN; break; case WINDOW_SCREENSHOT_KEY: case WINDOW_SCREENSHOT_CLIP_KEY: ctx->type = SCREENSHOT_TYPE_WINDOW; break; case AREA_SCREENSHOT_KEY: case AREA_SCREENSHOT_CLIP_KEY: ctx->type = SCREENSHOT_TYPE_AREA; break; default: g_assert_not_reached (); break; } screenshot_check_name_ready (ctx); } ./plugins/media-keys/gsd-screenshot-utils.h0000644000004100000410000000234312735467744021240 0ustar www-datawww-data/* gsd-screenshot-utils.h - utilities to take screenshots * * Copyright (C) 2012 Red Hat, Inc. * * Adapted from gnome-screenshot code, which is * Copyright (C) 2001-2006 Jonathan Blandford * Copyright (C) 2006 Emmanuele Bassi * Copyright (C) 2008-2012 Cosimo Cecchi * * 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, */ #ifndef __GSD_SCREENSHOT_UTILS_H__ #define __GSD_SCREENSHOT_UTILS_H__ #include "shortcuts-list.h" G_BEGIN_DECLS void gsd_screenshot_take (MediaKeyType key_type); G_END_DECLS #endif /* __GSD_SCREENSHOT_UTILS_H__ */ ./plugins/media-keys/gsd-media-keys-manager.c0000644000004100000410000035456612735467763021402 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2001-2003 Bastien Nocera * Copyright (C) 2006-2007 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GUDEV #include #endif #include "gnome-settings-plugin.h" #include "gnome-settings-bus.h" #include "gnome-settings-profile.h" #include "gsd-marshal.h" #include "gsd-media-keys-manager.h" #include "shortcuts-list.h" #include "shell-key-grabber.h" #include "gsd-screenshot-utils.h" #include "gsd-input-helper.h" #include "gsd-enums.h" #include #include #include "gvc-mixer-control.h" #include "gvc-mixer-control-private.h" #include "gvc-mixer-sink.h" #include "pa-backend.h" #include "dialog-window.h" #include #ifdef HAVE_FCITX #include #endif #define GSD_MEDIA_KEYS_DBUS_PATH GSD_DBUS_PATH "/MediaKeys" #define GSD_MEDIA_KEYS_DBUS_NAME GSD_DBUS_NAME ".MediaKeys" #define GNOME_KEYRING_DBUS_NAME "org.gnome.keyring" #define GNOME_KEYRING_DBUS_PATH "/org/gnome/keyring/daemon" #define GNOME_KEYRING_DBUS_INTERFACE "org.gnome.keyring.Daemon" #define SHELL_DBUS_NAME "org.gnome.Shell" #define SHELL_DBUS_PATH "/org/gnome/Shell" #define PANEL_DBUS_NAME "org.gnome.Panel" #define UNITY_DBUS_NAME "com.canonical.Unity" #define CUSTOM_BINDING_SCHEMA SETTINGS_BINDING_DIR ".custom-keybinding" #define SHELL_GRABBER_RETRY_INTERVAL 1 static const gchar introspection_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; #define SETTINGS_INTERFACE_DIR "org.gnome.desktop.interface" #define SETTINGS_POWER_DIR "org.gnome.settings-daemon.plugins.power" #define SETTINGS_XSETTINGS_DIR "org.gnome.settings-daemon.plugins.xsettings" #define SETTINGS_TOUCHPAD_DIR "org.gnome.desktop.peripherals.touchpad" #define TOUCHPAD_ENABLED_KEY "send-events" #define HIGH_CONTRAST "HighContrast" #define VOLUME_STEP 6 /* percents for one volume button press */ #define ENV_GTK_IM_MODULE "GTK_IM_MODULE" #define GTK_IM_MODULE_IBUS "ibus" #define GTK_IM_MODULE_FCITX "fcitx" #define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" #define KEY_CURRENT_INPUT_SOURCE "current" #define KEY_INPUT_SOURCES "sources" #define INPUT_SOURCE_TYPE_XKB "xkb" #define INPUT_SOURCE_TYPE_IBUS "ibus" #define INPUT_SOURCE_TYPE_FCITX "fcitx" #define FCITX_XKB_PREFIX "fcitx-keyboard-" #define SYSTEMD_DBUS_NAME "org.freedesktop.login1" #define SYSTEMD_DBUS_PATH "/org/freedesktop/login1" #define SYSTEMD_DBUS_INTERFACE "org.freedesktop.login1.Manager" #define GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_MEDIA_KEYS_MANAGER, GsdMediaKeysManagerPrivate)) typedef struct { char *application; char *dbus_name; guint32 time; guint watch_id; } MediaPlayer; typedef struct { MediaKeyType key_type; ShellKeyBindingMode modes; const char *settings_key; const char *hard_coded; char *custom_path; char *custom_command; Key *key; guint accel_id; } MediaKey; typedef struct { GsdMediaKeysManager *manager; MediaKey *key; } GrabData; struct GsdMediaKeysManagerPrivate { /* Volume bits */ GvcMixerControl *volume; GvcMixerStream *sink; GvcMixerStream *source; ca_context *ca; GtkSettings *gtksettings; #ifdef HAVE_GUDEV GHashTable *streams; /* key = X device ID, value = stream id */ GUdevClient *udev_client; #endif /* HAVE_GUDEV */ GSettings *settings; GSettings *input_settings; GHashTable *custom_settings; GSettings *sound_settings; GPtrArray *keys; /* HighContrast theme settings */ GSettings *interface_settings; char *icon_theme; char *gtk_theme; /* Power stuff */ GSettings *power_settings; GDBusProxy *power_proxy; GDBusProxy *power_screen_proxy; GDBusProxy *power_keyboard_proxy; /* Shell stuff */ guint name_owner_id; GDBusProxy *shell_proxy; ShellKeyGrabber *key_grabber; GCancellable *shell_cancellable; GCancellable *grab_cancellable; /* ScreenSaver stuff */ GsdScreenSaver *screen_saver_proxy; /* systemd stuff */ GDBusProxy *logind_proxy; gint inhibit_keys_fd; /* Multihead stuff */ GdkScreen *current_screen; GSList *screens; GdkScreen *screen; int opcode; GList *media_players; GDBusNodeInfo *introspection_data; GDBusConnection *connection; GCancellable *bus_cancellable; GDBusProxy *xrandr_proxy; GCancellable *cancellable; guint start_idle_id; /* Ubuntu notifications */ NotifyNotification *volume_notification; NotifyNotification *brightness_notification; NotifyNotification *kb_backlight_notification; /* Legacy keygrabber stuff */ guint unity_name_owner_id; guint panel_name_owner_id; guint have_legacy_keygrabber; #ifdef HAVE_FCITX FcitxInputMethod *fcitx; #endif gboolean is_ibus_active; gboolean is_fcitx_active; /* What did you plug in dialog */ pa_backend *wdypi_pa_backend; }; static void gsd_media_keys_manager_class_init (GsdMediaKeysManagerClass *klass); static void gsd_media_keys_manager_init (GsdMediaKeysManager *media_keys_manager); static void gsd_media_keys_manager_finalize (GObject *object); static void register_manager (GsdMediaKeysManager *manager); static void custom_binding_changed (GSettings *settings, const char *settings_key, GsdMediaKeysManager *manager); static void grab_media_keys (GsdMediaKeysManager *manager); static void grab_media_key (MediaKey *key, GsdMediaKeysManager *manager); static void ungrab_media_key (MediaKey *key, GsdMediaKeysManager *manager); G_DEFINE_TYPE (GsdMediaKeysManager, gsd_media_keys_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; #define NOTIFY_CAP_PRIVATE_SYNCHRONOUS "x-canonical-private-synchronous" #define NOTIFY_CAP_PRIVATE_ICON_ONLY "x-canonical-private-icon-only" #define NOTIFY_HINT_TRUE "true" typedef struct { GsdMediaKeysManager *manager; MediaKeyType type; guint old_percentage; } GsdBrightnessActionData; static const char *volume_icons[] = { "notification-audio-volume-muted", "notification-audio-volume-low", "notification-audio-volume-medium", "notification-audio-volume-high", NULL }; static const char *audio_volume_icons[] = { "audio-volume-muted-symbolic", "audio-volume-low-symbolic", "audio-volume-medium-symbolic", "audio-volume-high-symbolic", NULL }; static const char *mic_volume_icons[] = { "microphone-sensitivity-muted-symbolic", "microphone-sensitivity-low-symbolic", "microphone-sensitivity-medium-symbolic", "microphone-sensitivity-high-symbolic", NULL }; static const char *brightness_icons[] = { "notification-display-brightness-off", "notification-display-brightness-low", "notification-display-brightness-medium", "notification-display-brightness-high", "notification-display-brightness-full", NULL }; static const char *kb_backlight_icons[] = { "notification-keyboard-brightness-off", "notification-keyboard-brightness-low", "notification-keyboard-brightness-medium", "notification-keyboard-brightness-high", "notification-keyboard-brightness-full", NULL }; static const char * calculate_icon_name (gint value, const char **icon_names) { value = CLAMP (value, 0, 100); gint length = g_strv_length (icon_names); gint s = (length - 1) * value / 100 + 1; s = CLAMP (s, 1, length - 1); return icon_names[s]; } static gboolean ubuntu_osd_notification_is_supported (void) { GList *caps; gboolean has_cap; caps = notify_get_server_caps (); has_cap = (g_list_find_custom (caps, NOTIFY_CAP_PRIVATE_SYNCHRONOUS, (GCompareFunc) g_strcmp0) != NULL); g_list_foreach (caps, (GFunc) g_free, NULL); g_list_free (caps); return has_cap; } static gboolean ubuntu_osd_notification_show_icon (const char *icon, const char *hint) { if (!ubuntu_osd_notification_is_supported ()) return FALSE; NotifyNotification *notification = notify_notification_new (" ", "", icon); notify_notification_set_hint_string (notification, NOTIFY_CAP_PRIVATE_SYNCHRONOUS, hint); notify_notification_set_hint_string (notification, NOTIFY_CAP_PRIVATE_ICON_ONLY, NOTIFY_HINT_TRUE); gboolean res = notify_notification_show (notification, NULL); g_object_unref (notification); return res; } static gboolean ubuntu_osd_do_notification (NotifyNotification **notification, const char *hint, gint value, gboolean muted, const char **icon_names) { if (!ubuntu_osd_notification_is_supported ()) return FALSE; if (!*notification) { *notification = notify_notification_new (" ", "", NULL); notify_notification_set_hint_string (*notification, NOTIFY_CAP_PRIVATE_SYNCHRONOUS, hint); } value = CLAMP (value, -1, 101); const char *icon = muted ? icon_names[0] : calculate_icon_name (value, icon_names); notify_notification_set_hint_int32 (*notification, "value", value); notify_notification_update (*notification, " ", "", icon); return notify_notification_show (*notification, NULL); } static gboolean ubuntu_osd_notification_show_volume (GsdMediaKeysManager *manager, gint value, gboolean muted, gboolean is_mic) { const char **icons_name = is_mic ? mic_volume_icons : volume_icons; return ubuntu_osd_do_notification (&manager->priv->volume_notification, "volume", value, muted, icons_name); } static gboolean ubuntu_osd_notification_show_brightness (GsdMediaKeysManager *manager, gint value) { return ubuntu_osd_do_notification (&manager->priv->brightness_notification, "brightness", value, value <= 0, brightness_icons); } static gboolean ubuntu_osd_notification_show_kb_backlight (GsdMediaKeysManager *manager, gint value) { return ubuntu_osd_do_notification (&manager->priv->kb_backlight_notification, "keyboard", value, value <= 0, kb_backlight_icons); } static void init_screens (GsdMediaKeysManager *manager) { GdkDisplay *display; int i; display = gdk_display_get_default (); for (i = 0; i < gdk_display_get_n_screens (display); i++) { GdkScreen *screen; screen = gdk_display_get_screen (display, i); if (screen == NULL) { continue; } manager->priv->screens = g_slist_append (manager->priv->screens, screen); } manager->priv->current_screen = manager->priv->screens->data; } static void media_key_free (MediaKey *key) { if (key == NULL) return; g_free (key->custom_path); g_free (key->custom_command); if (key->key) free_key (key->key); g_free (key); } static char * get_term_command (GsdMediaKeysManager *manager) { char *cmd_term, *cmd_args;; char *cmd = NULL; GSettings *settings; settings = g_settings_new ("org.gnome.desktop.default-applications.terminal"); cmd_term = g_settings_get_string (settings, "exec"); if (cmd_term[0] == '\0') cmd_term = g_strdup ("gnome-terminal"); cmd_args = g_settings_get_string (settings, "exec-arg"); if (strcmp (cmd_term, "") != 0) { cmd = g_strdup_printf ("%s %s -e", cmd_term, cmd_args); } else { cmd = g_strdup_printf ("%s -e", cmd_term); } g_free (cmd_args); g_free (cmd_term); g_object_unref (settings); return cmd; } static char ** get_keyring_env (GsdMediaKeysManager *manager) { GError *error = NULL; GVariant *variant, *item; GVariantIter *iter; char **envp; variant = g_dbus_connection_call_sync (manager->priv->connection, GNOME_KEYRING_DBUS_NAME, GNOME_KEYRING_DBUS_PATH, GNOME_KEYRING_DBUS_INTERFACE, "GetEnvironment", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (variant == NULL) { g_warning ("Failed to call GetEnvironment on keyring daemon: %s", error->message); g_error_free (error); return NULL; } envp = g_get_environ (); envp = g_environ_unsetenv (envp, "DESKTOP_AUTOSTART_ID"); g_variant_get (variant, "(a{ss})", &iter); while ((item = g_variant_iter_next_value (iter))) { char *key; char *value; g_variant_get (item, "{ss}", &key, &value); envp = g_environ_setenv (envp, key, value, TRUE); g_variant_unref (item); g_free (key); g_free (value); } g_variant_iter_free (iter); g_variant_unref (variant); return envp; } static void execute (GsdMediaKeysManager *manager, char *cmd, gboolean need_term) { gboolean retval; char **argv; int argc; char *exec; char *term = NULL; GError *error = NULL; retval = FALSE; if (need_term) term = get_term_command (manager); if (term) { exec = g_strdup_printf ("%s %s", term, cmd); g_free (term); } else { exec = g_strdup (cmd); } if (g_shell_parse_argv (exec, &argc, &argv, NULL)) { char **envp; envp = get_keyring_env (manager); retval = g_spawn_async (g_get_home_dir (), argv, envp, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); g_strfreev (argv); g_strfreev (envp); } if (retval == FALSE) { g_warning ("Couldn't execute command: %s: %s", exec, error->message); g_error_free (error); } g_free (exec); } static void print_key_parse_error (MediaKey *key, const char *str) { if (str == NULL || *str == '\0') return; if (key->settings_key != NULL) g_debug ("Unable to parse key '%s' for GSettings entry '%s'", str, key->settings_key); else g_debug ("Unable to parse hard-coded key '%s'", key->hard_coded); } static char * get_key_string (GsdMediaKeysManager *manager, MediaKey *key) { if (key->settings_key == "switch-input-source" || key->settings_key == "switch-input-source-backward") return g_settings_get_strv (manager->priv->input_settings, key->settings_key)[0]; else if (key->settings_key != NULL) return g_settings_get_string (manager->priv->settings, key->settings_key); else if (key->hard_coded != NULL) return g_strdup (key->hard_coded); else if (key->custom_path != NULL) { GSettings *settings; settings = g_hash_table_lookup (manager->priv->custom_settings, key->custom_path); return g_settings_get_string (settings, "binding"); } else g_assert_not_reached (); } static void ensure_cancellable (GCancellable **cancellable) { if (*cancellable == NULL) { *cancellable = g_cancellable_new (); g_object_add_weak_pointer (G_OBJECT (*cancellable), (gpointer *)cancellable); } else { g_object_ref (*cancellable); } } static void show_osd (GsdMediaKeysManager *manager, const char *icon, const char *label, int level) { GVariantBuilder builder; if (manager->priv->shell_proxy == NULL) return; g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE); g_variant_builder_open (&builder, G_VARIANT_TYPE_VARDICT); if (icon) g_variant_builder_add (&builder, "{sv}", "icon", g_variant_new_string (icon)); if (label) g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (label)); if (level >= 0) g_variant_builder_add (&builder, "{sv}", "level", g_variant_new_int32 (level)); g_variant_builder_close (&builder); g_dbus_proxy_call (manager->priv->shell_proxy, "ShowOSD", g_variant_builder_end (&builder), G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, manager->priv->shell_cancellable, NULL, NULL); } static const char * get_icon_name_for_volume (gboolean is_mic, gboolean muted, int volume) { int n; if (muted) { n = 0; } else { /* select image */ n = 3 * volume / 100 + 1; if (n < 1) { n = 1; } else if (n > 3) { n = 3; } } if (is_mic) return mic_volume_icons[n]; else return audio_volume_icons[n]; } static gboolean retry_grabs (gpointer data) { GsdMediaKeysManager *manager = data; grab_media_keys (manager); return FALSE; } static void grab_accelerators_complete (GObject *object, GAsyncResult *result, gpointer user_data) { GVariant *actions; gboolean retry = FALSE; GError *error = NULL; GsdMediaKeysManager *manager = user_data; shell_key_grabber_call_grab_accelerators_finish (SHELL_KEY_GRABBER (object), &actions, result, &error); if (error) { retry = (error->code == G_DBUS_ERROR_UNKNOWN_METHOD); if (!retry) g_warning ("%d: %s", error->code, error->message); g_error_free (error); } else { int i; for (i = 0; i < manager->priv->keys->len; i++) { MediaKey *key; key = g_ptr_array_index (manager->priv->keys, i); g_variant_get_child (actions, i, "u", &key->accel_id); } } if (retry) g_timeout_add_seconds (SHELL_GRABBER_RETRY_INTERVAL, retry_grabs, manager); } static void grab_media_keys (GsdMediaKeysManager *manager) { GVariantBuilder builder; int i; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)")); for (i = 0; i < manager->priv->keys->len; i++) { MediaKey *key; char *tmp; key = g_ptr_array_index (manager->priv->keys, i); tmp = get_key_string (manager, key); g_variant_builder_add (&builder, "(su)", tmp, key->modes); g_free (tmp); } shell_key_grabber_call_grab_accelerators (manager->priv->key_grabber, g_variant_builder_end (&builder), manager->priv->grab_cancellable, grab_accelerators_complete, manager); } static void grab_accelerator_complete (GObject *object, GAsyncResult *result, gpointer user_data) { GrabData *data = user_data; MediaKey *key = data->key; shell_key_grabber_call_grab_accelerator_finish (SHELL_KEY_GRABBER (object), &key->accel_id, result, NULL); g_slice_free (GrabData, data); } static void grab_media_key (MediaKey *key, GsdMediaKeysManager *manager) { GrabData *data; char *tmp; ungrab_media_key (key, manager); tmp = get_key_string (manager, key); data = g_slice_new0 (GrabData); data->manager = manager; data->key = key; shell_key_grabber_call_grab_accelerator (manager->priv->key_grabber, tmp, key->modes, manager->priv->grab_cancellable, grab_accelerator_complete, data); g_free (tmp); } static gboolean grab_media_key_legacy (MediaKey *key, GsdMediaKeysManager *manager) { char *tmp; gboolean need_flush; need_flush = FALSE; if (key->key != NULL) { need_flush = TRUE; ungrab_key_unsafe (key->key, manager->priv->screens); } free_key (key->key); key->key = NULL; tmp = get_key_string (manager, key); key->key = parse_key (tmp); if (key->key == NULL) { print_key_parse_error (key, tmp); g_free (tmp); return need_flush; } grab_key_unsafe (key->key, GSD_KEYGRAB_NORMAL, manager->priv->screens); g_free (tmp); return TRUE; } static void ungrab_accelerator_complete (GObject *object, GAsyncResult *result, gpointer user_data) { GsdMediaKeysManager *manager = user_data; shell_key_grabber_call_ungrab_accelerator_finish (SHELL_KEY_GRABBER (object), NULL, result, NULL); } static void ungrab_media_key (MediaKey *key, GsdMediaKeysManager *manager) { if (key->accel_id == 0) return; shell_key_grabber_call_ungrab_accelerator (manager->priv->key_grabber, key->accel_id, manager->priv->grab_cancellable, ungrab_accelerator_complete, manager); key->accel_id = 0; } static void gsettings_changed_cb (GSettings *settings, const gchar *settings_key, GsdMediaKeysManager *manager) { int i; gboolean need_flush = FALSE; if (manager->priv->have_legacy_keygrabber) need_flush = TRUE; /* Give up if we don't have proxy to the shell */ else if (!manager->priv->key_grabber) return; /* handled in gsettings_custom_changed_cb() */ if (g_str_equal (settings_key, "custom-keybindings")) return; if (manager->priv->have_legacy_keygrabber) gdk_error_trap_push (); /* Find the key that was modified */ for (i = 0; i < manager->priv->keys->len; i++) { MediaKey *key; key = g_ptr_array_index (manager->priv->keys, i); /* Skip over hard-coded and GConf keys */ if (key->settings_key == NULL) continue; if (strcmp (settings_key, key->settings_key) == 0) { if (!manager->priv->have_legacy_keygrabber) grab_media_key (key, manager); else { if (grab_media_key_legacy (key, manager)) need_flush = TRUE; } break; } } if (manager->priv->have_legacy_keygrabber) { if (need_flush) gdk_flush (); if (gdk_error_trap_pop ()) g_warning ("Grab failed for some keys, another application may already have access the them."); } } static MediaKey * media_key_new_for_path (GsdMediaKeysManager *manager, char *path) { GSettings *settings; char *command, *binding; MediaKey *key; g_debug ("media_key_new_for_path: %s", path); settings = g_hash_table_lookup (manager->priv->custom_settings, path); if (settings == NULL) { settings = g_settings_new_with_path (CUSTOM_BINDING_SCHEMA, path); g_signal_connect (settings, "changed", G_CALLBACK (custom_binding_changed), manager); g_hash_table_insert (manager->priv->custom_settings, g_strdup (path), settings); } command = g_settings_get_string (settings, "command"); binding = g_settings_get_string (settings, "binding"); if (*command == '\0' && *binding == '\0') { g_debug ("Key binding (%s) is incomplete", path); g_free (command); g_free (binding); return NULL; } g_free (binding); key = g_new0 (MediaKey, 1); key->key_type = CUSTOM_KEY; key->modes = GSD_KEYBINDING_MODE_LAUNCHER; key->custom_path = g_strdup (path); key->custom_command = command; return key; } static void update_custom_binding (GsdMediaKeysManager *manager, char *path) { MediaKey *key; int i; /* Remove the existing key */ for (i = 0; i < manager->priv->keys->len; i++) { key = g_ptr_array_index (manager->priv->keys, i); if (key->custom_path == NULL) continue; if (strcmp (key->custom_path, path) == 0) { g_debug ("Removing custom key binding %s", path); ungrab_media_key (key, manager); g_ptr_array_remove_index_fast (manager->priv->keys, i); break; } } /* And create a new one! */ key = media_key_new_for_path (manager, path); if (key) { g_debug ("Adding new custom key binding %s", path); g_ptr_array_add (manager->priv->keys, key); grab_media_key (key, manager); } } static void custom_binding_changed (GSettings *settings, const char *settings_key, GsdMediaKeysManager *manager) { char *path; if (strcmp (settings_key, "name") == 0) return; /* we don't care */ g_object_get (settings, "path", &path, NULL); update_custom_binding (manager, path); g_free (path); } static void gsettings_custom_changed_cb (GSettings *settings, const char *settings_key, GsdMediaKeysManager *manager) { char **bindings; int i, j, n_bindings; bindings = g_settings_get_strv (settings, settings_key); n_bindings = g_strv_length (bindings); /* Handle additions */ for (i = 0; i < n_bindings; i++) { if (g_hash_table_lookup (manager->priv->custom_settings, bindings[i])) continue; update_custom_binding (manager, bindings[i]); } /* Handle removals */ for (i = 0; i < manager->priv->keys->len; i++) { gboolean found = FALSE; MediaKey *key = g_ptr_array_index (manager->priv->keys, i); if (key->key_type != CUSTOM_KEY) continue; for (j = 0; j < n_bindings && !found; j++) found = strcmp (bindings[j], key->custom_path) == 0; if (found) continue; if (manager->priv->have_legacy_keygrabber && key->key) { gdk_error_trap_push (); ungrab_key_unsafe (key->key, manager->priv->screens); gdk_flush (); if (gdk_error_trap_pop ()) g_warning ("Ungrab failed for custom key '%s'", key->custom_path); } else ungrab_media_key (key, manager); g_hash_table_remove (manager->priv->custom_settings, key->custom_path); g_ptr_array_remove_index_fast (manager->priv->keys, i); --i; /* make up for the removed key */ } g_strfreev (bindings); } static void add_key (GsdMediaKeysManager *manager, guint i) { MediaKey *key; key = g_new0 (MediaKey, 1); key->key_type = media_keys[i].key_type; key->settings_key = media_keys[i].settings_key; key->hard_coded = media_keys[i].hard_coded; key->modes = media_keys[i].modes; g_ptr_array_add (manager->priv->keys, key); if (manager->priv->have_legacy_keygrabber) grab_media_key_legacy (key, manager); } static void init_kbd (GsdMediaKeysManager *manager) { char **custom_paths; int i; gnome_settings_profile_start (NULL); if (manager->priv->have_legacy_keygrabber) gdk_error_trap_push (); /* Media keys * Add hard-coded shortcuts first so that they can't be preempted */ for (i = 0; i < G_N_ELEMENTS (media_keys); i++) { if (media_keys[i].hard_coded) add_key (manager, i); } for (i = 0; i < G_N_ELEMENTS (media_keys); i++) { if (media_keys[i].hard_coded == NULL) add_key (manager, i); } /* Custom shortcuts */ custom_paths = g_settings_get_strv (manager->priv->settings, "custom-keybindings"); for (i = 0; i < g_strv_length (custom_paths); i++) { MediaKey *key; g_debug ("Setting up custom keybinding %s", custom_paths[i]); key = media_key_new_for_path (manager, custom_paths[i]); if (!key) { continue; } g_ptr_array_add (manager->priv->keys, key); if (manager->priv->have_legacy_keygrabber) grab_media_key_legacy (key, manager); } g_strfreev (custom_paths); if (!manager->priv->have_legacy_keygrabber) grab_media_keys (manager); else { gdk_flush (); if (gdk_error_trap_pop ()) g_warning ("Grab failed for some keys, another application may already have access the them."); } gnome_settings_profile_end (NULL); } static void launch_app (GAppInfo *app_info, gint64 timestamp) { GError *error = NULL; GdkAppLaunchContext *launch_context; /* setup the launch context so the startup notification is correct */ launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ()); gdk_app_launch_context_set_timestamp (launch_context, timestamp); if (!g_app_info_launch (app_info, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error)) { g_warning ("Could not launch '%s': %s", g_app_info_get_commandline (app_info), error->message); g_error_free (error); } g_object_unref (launch_context); } static void do_url_action (GsdMediaKeysManager *manager, const char *scheme, gint64 timestamp) { GAppInfo *app_info; app_info = g_app_info_get_default_for_uri_scheme (scheme); if (app_info != NULL) { launch_app (app_info, timestamp); g_object_unref (app_info); } else { g_warning ("Could not find default application for '%s' scheme", scheme); } } static void do_media_action (GsdMediaKeysManager *manager, gint64 timestamp) { GAppInfo *app_info; app_info = g_app_info_get_default_for_type ("audio/x-vorbis+ogg", FALSE); if (app_info != NULL) { launch_app (app_info, timestamp); g_object_unref (app_info); } else { g_warning ("Could not find default application for '%s' mime-type", "audio/x-vorbis+ogg"); } } static void do_terminal_action (GsdMediaKeysManager *manager) { GSettings *settings; char *term; settings = g_settings_new ("org.gnome.desktop.default-applications.terminal"); term = g_settings_get_string (settings, "exec"); if (term) execute (manager, term, FALSE); g_free (term); g_object_unref (settings); } static void gnome_session_shutdown (GsdMediaKeysManager *manager) { GError *error = NULL; GVariant *variant; GDBusProxy *proxy; proxy = G_DBUS_PROXY (gnome_settings_bus_get_session_proxy ()); variant = g_dbus_proxy_call_sync (proxy, "Shutdown", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (variant == NULL) { g_warning ("Failed to call Shutdown on session manager: %s", error->message); g_error_free (error); return; } g_variant_unref (variant); g_object_unref (proxy); } static void do_logout_action (GsdMediaKeysManager *manager) { execute (manager, "gnome-session-quit --logout", FALSE); } static void do_eject_action_cb (GDrive *drive, GAsyncResult *res, GsdMediaKeysManager *manager) { g_drive_eject_with_operation_finish (drive, res, NULL); } #define NO_SCORE 0 #define SCORE_CAN_EJECT 50 #define SCORE_HAS_MEDIA 100 static void do_eject_action (GsdMediaKeysManager *manager) { GList *drives, *l; GDrive *fav_drive; guint score; GVolumeMonitor *volume_monitor; volume_monitor = g_volume_monitor_get (); /* Find the best drive to eject */ fav_drive = NULL; score = NO_SCORE; drives = g_volume_monitor_get_connected_drives (volume_monitor); for (l = drives; l != NULL; l = l->next) { GDrive *drive = l->data; if (g_drive_can_eject (drive) == FALSE) continue; if (g_drive_is_media_removable (drive) == FALSE) continue; if (score < SCORE_CAN_EJECT) { fav_drive = drive; score = SCORE_CAN_EJECT; } if (g_drive_has_media (drive) == FALSE) continue; if (score < SCORE_HAS_MEDIA) { fav_drive = drive; score = SCORE_HAS_MEDIA; break; } } /* Show OSD */ if (!ubuntu_osd_notification_show_icon ("notification-device-eject", "Eject")) { show_osd (manager, "media-eject-symbolic", NULL, -1); } /* Clean up the drive selection and exit if no suitable * drives are found */ if (fav_drive != NULL) fav_drive = g_object_ref (fav_drive); g_list_foreach (drives, (GFunc) g_object_unref, NULL); if (fav_drive == NULL) return; /* Eject! */ g_drive_eject_with_operation (fav_drive, G_MOUNT_UNMOUNT_FORCE, NULL, NULL, (GAsyncReadyCallback) do_eject_action_cb, manager); g_object_unref (fav_drive); g_object_unref (volume_monitor); } static void do_home_key_action (GsdMediaKeysManager *manager, gint64 timestamp) { GFile *file; GError *error = NULL; char *uri; file = g_file_new_for_path (g_get_home_dir ()); uri = g_file_get_uri (file); g_object_unref (file); if (gtk_show_uri (NULL, uri, timestamp, &error) == FALSE) { g_warning ("Failed to launch '%s': %s", uri, error->message); g_error_free (error); } g_free (uri); } static void do_search_action (GsdMediaKeysManager *manager, gint64 timestamp) { g_dbus_proxy_call (manager->priv->shell_proxy, "FocusSearch", NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, manager->priv->shell_cancellable, NULL, NULL); } static void do_execute_desktop_or_desktop (GsdMediaKeysManager *manager, const char *desktop, const char *alt_desktop, gint64 timestamp) { GDesktopAppInfo *app_info; app_info = g_desktop_app_info_new (desktop); if (app_info == NULL) app_info = g_desktop_app_info_new (alt_desktop); if (app_info != NULL) { launch_app (G_APP_INFO (app_info), timestamp); g_object_unref (app_info); return; } g_warning ("Could not find application '%s' or '%s'", desktop, alt_desktop); } static void do_touchpad_osd_action (GsdMediaKeysManager *manager, gboolean state) { if (!ubuntu_osd_notification_show_icon ((!state) ? "touchpad-disabled-symbolic" : "input-touchpad-symbolic", "Touchpad")) { show_osd (manager, state ? "input-touchpad-symbolic" : "touchpad-disabled-symbolic", NULL, -1); } } static void do_touchpad_action (GsdMediaKeysManager *manager) { GSettings *settings; gboolean state; if (touchpad_is_present () == FALSE) { do_touchpad_osd_action (manager, FALSE); return; } settings = g_settings_new (SETTINGS_TOUCHPAD_DIR); state = (g_settings_get_enum (settings, TOUCHPAD_ENABLED_KEY) == G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED); do_touchpad_osd_action (manager, !state); g_settings_set_enum (settings, TOUCHPAD_ENABLED_KEY, !state ? G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED : G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED); g_object_unref (settings); } static void on_screen_locked (GsdScreenSaver *screen_saver, GAsyncResult *result, GsdMediaKeysManager *manager) { gboolean is_locked; GError *error = NULL; is_locked = gsd_screen_saver_call_lock_finish (screen_saver, result, &error); if (!is_locked) { g_warning ("Couldn't lock screen: %s", error->message); g_error_free (error); return; } } static void do_lock_screensaver (GsdMediaKeysManager *manager) { GsdMediaKeysManagerPrivate *priv = manager->priv; if (priv->screen_saver_proxy == NULL) priv->screen_saver_proxy = gnome_settings_bus_get_screen_saver_proxy (); gsd_screen_saver_call_lock (priv->screen_saver_proxy, priv->cancellable, (GAsyncReadyCallback) on_screen_locked, manager); } static void sound_theme_changed (GtkSettings *settings, GParamSpec *pspec, GsdMediaKeysManager *manager) { char *theme_name; g_object_get (G_OBJECT (manager->priv->gtksettings), "gtk-sound-theme-name", &theme_name, NULL); if (theme_name) ca_context_change_props (manager->priv->ca, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL); g_free (theme_name); } static void ensure_canberra (GsdMediaKeysManager *manager) { char *theme_name; if (manager->priv->ca != NULL) return; ca_context_create (&manager->priv->ca); ca_context_set_driver (manager->priv->ca, "pulse"); ca_context_change_props (manager->priv->ca, 0, CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl", NULL); manager->priv->gtksettings = gtk_settings_get_for_screen (gdk_screen_get_default ()); g_object_get (G_OBJECT (manager->priv->gtksettings), "gtk-sound-theme-name", &theme_name, NULL); if (theme_name) ca_context_change_props (manager->priv->ca, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL); g_free (theme_name); g_signal_connect (manager->priv->gtksettings, "notify::gtk-sound-theme-name", G_CALLBACK (sound_theme_changed), manager); } static void update_dialog (GsdMediaKeysManager *manager, GvcMixerStream *stream, guint vol, gboolean muted, gboolean sound_changed, gboolean quiet) { GvcMixerUIDevice *device; const GvcMixerStreamPort *port; const char *icon; if (ubuntu_osd_notification_show_volume (manager, vol, muted, !GVC_IS_MIXER_SINK (stream))) goto done; vol = CLAMP (vol, 0, 100); icon = get_icon_name_for_volume (!GVC_IS_MIXER_SINK (stream), muted, vol); port = gvc_mixer_stream_get_port (stream); if (g_strcmp0 (gvc_mixer_stream_get_form_factor (stream), "internal") != 0 || (port != NULL && g_strcmp0 (port->port, "analog-output-speaker") != 0 && g_strcmp0 (port->port, "analog-output") != 0)) { device = gvc_mixer_control_lookup_device_from_stream (manager->priv->volume, stream); show_osd (manager, icon, gvc_mixer_ui_device_get_description (device), vol); } else { show_osd (manager, icon, NULL, vol); } done: if (quiet == FALSE && sound_changed != FALSE && muted == FALSE) { ensure_canberra (manager); ca_context_change_device (manager->priv->ca, gvc_mixer_stream_get_name (stream)); ca_context_play (manager->priv->ca, 1, CA_PROP_EVENT_ID, "audio-volume-change", CA_PROP_EVENT_DESCRIPTION, "volume changed through key press", CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", NULL); } } #ifdef HAVE_GUDEV /* PulseAudio gives us /devices/... paths, when udev * expects /sys/devices/... paths. */ static GUdevDevice * get_udev_device_for_sysfs_path (GsdMediaKeysManager *manager, const char *sysfs_path) { char *path; GUdevDevice *dev; path = g_strdup_printf ("/sys%s", sysfs_path); dev = g_udev_client_query_by_sysfs_path (manager->priv->udev_client, path); g_free (path); return dev; } static GvcMixerStream * get_stream_for_device_id (GsdMediaKeysManager *manager, gboolean is_output, guint deviceid) { char *devnode; gpointer id_ptr; GvcMixerStream *res; GUdevDevice *dev, *parent; GSList *streams, *l; id_ptr = g_hash_table_lookup (manager->priv->streams, GUINT_TO_POINTER (deviceid)); if (id_ptr != NULL) { if (GPOINTER_TO_UINT (id_ptr) == (guint) -1) return NULL; else return gvc_mixer_control_lookup_stream_id (manager->priv->volume, GPOINTER_TO_UINT (id_ptr)); } devnode = xdevice_get_device_node (deviceid); if (devnode == NULL) { g_debug ("Could not find device node for XInput device %d", deviceid); return NULL; } dev = g_udev_client_query_by_device_file (manager->priv->udev_client, devnode); if (dev == NULL) { g_debug ("Could not find udev device for device path '%s'", devnode); g_free (devnode); return NULL; } g_free (devnode); if (g_strcmp0 (g_udev_device_get_property (dev, "ID_BUS"), "usb") != 0) { g_debug ("Not handling XInput device %d, not USB", deviceid); g_hash_table_insert (manager->priv->streams, GUINT_TO_POINTER (deviceid), GUINT_TO_POINTER ((guint) -1)); g_object_unref (dev); return NULL; } parent = g_udev_device_get_parent_with_subsystem (dev, "usb", "usb_device"); if (parent == NULL) { g_warning ("No USB device parent for XInput device %d even though it's USB", deviceid); g_object_unref (dev); return NULL; } res = NULL; if (is_output) streams = gvc_mixer_control_get_sinks (manager->priv->volume); else streams = gvc_mixer_control_get_sources (manager->priv->volume); for (l = streams; l; l = l->next) { GvcMixerStream *stream = l->data; const char *sysfs_path; GUdevDevice *stream_dev, *stream_parent; sysfs_path = gvc_mixer_stream_get_sysfs_path (stream); stream_dev = get_udev_device_for_sysfs_path (manager, sysfs_path); if (stream_dev == NULL) continue; stream_parent = g_udev_device_get_parent_with_subsystem (stream_dev, "usb", "usb_device"); g_object_unref (stream_dev); if (stream_parent == NULL) continue; if (g_strcmp0 (g_udev_device_get_sysfs_path (stream_parent), g_udev_device_get_sysfs_path (parent)) == 0) { res = stream; } g_object_unref (stream_parent); if (res != NULL) break; } g_slist_free (streams); if (res) g_hash_table_insert (manager->priv->streams, GUINT_TO_POINTER (deviceid), GUINT_TO_POINTER (gvc_mixer_stream_get_id (res))); else g_hash_table_insert (manager->priv->streams, GUINT_TO_POINTER (deviceid), GUINT_TO_POINTER ((guint) -1)); return res; } #endif /* HAVE_GUDEV */ static void do_sound_action (GsdMediaKeysManager *manager, guint deviceid, int type, gboolean is_output, gboolean quiet) { GvcMixerStream *stream; gboolean old_muted, new_muted; guint old_vol, new_vol, norm_vol_step, osd_vol; gboolean sound_changed; pa_volume_t max_volume; /* Find the stream that corresponds to the device, if any */ stream = NULL; #ifdef HAVE_GUDEV stream = get_stream_for_device_id (manager, is_output, deviceid); #endif /* HAVE_GUDEV */ if (stream == NULL) { if (is_output) stream = manager->priv->sink; else stream = manager->priv->source; } if (stream == NULL) return; if (g_settings_get_boolean (manager->priv->sound_settings, "allow-amplified-volume")) max_volume = PA_VOLUME_UI_MAX; else max_volume = PA_VOLUME_NORM; norm_vol_step = PA_VOLUME_NORM * VOLUME_STEP / 100; /* FIXME: this is racy */ new_vol = old_vol = gvc_mixer_stream_get_volume (stream); new_muted = old_muted = gvc_mixer_stream_get_is_muted (stream); sound_changed = FALSE; switch (type) { case MUTE_KEY: new_muted = !old_muted; break; case VOLUME_DOWN_KEY: if (old_vol <= norm_vol_step) { new_vol = 0; new_muted = TRUE; } else { new_vol = old_vol - norm_vol_step; } break; case VOLUME_UP_KEY: new_muted = FALSE; /* When coming out of mute only increase the volume if it was 0 */ if (!old_muted || old_vol == 0) new_vol = MIN (old_vol + norm_vol_step, max_volume); break; } if (old_muted != new_muted) { gvc_mixer_stream_change_is_muted (stream, new_muted); sound_changed = TRUE; } if (old_vol != new_vol) { if (gvc_mixer_stream_set_volume (stream, new_vol) != FALSE) { gvc_mixer_stream_push_volume (stream); sound_changed = TRUE; } } if (type == VOLUME_DOWN_KEY && old_vol == 0 && old_muted) osd_vol = -1; else if (type == VOLUME_UP_KEY && old_vol == max_volume && !old_muted) osd_vol = 101; else if (!new_muted) osd_vol = (int) (100 * (double) new_vol / max_volume); else osd_vol = 0; update_dialog (manager, stream, osd_vol, new_muted, sound_changed, quiet); } static void update_default_sink (GsdMediaKeysManager *manager) { GvcMixerStream *stream; stream = gvc_mixer_control_get_default_sink (manager->priv->volume); if (stream == manager->priv->sink) return; g_clear_object (&manager->priv->sink); if (stream != NULL) { manager->priv->sink = g_object_ref (stream); } else { g_warning ("Unable to get default sink"); } } static void update_default_source (GsdMediaKeysManager *manager) { GvcMixerStream *stream; stream = gvc_mixer_control_get_default_source (manager->priv->volume); if (stream == manager->priv->source) return; g_clear_object (&manager->priv->source); if (stream != NULL) { manager->priv->source = g_object_ref (stream); } else { g_warning ("Unable to get default source"); } } static void on_control_state_changed (GvcMixerControl *control, GvcMixerControlState new_state, GsdMediaKeysManager *manager) { update_default_sink (manager); update_default_source (manager); } static void on_control_default_sink_changed (GvcMixerControl *control, guint id, GsdMediaKeysManager *manager) { update_default_sink (manager); } static void on_control_default_source_changed (GvcMixerControl *control, guint id, GsdMediaKeysManager *manager) { update_default_source (manager); } #ifdef HAVE_GUDEV static gboolean remove_stream (gpointer key, gpointer value, gpointer id) { if (GPOINTER_TO_UINT (value) == GPOINTER_TO_UINT (id)) return TRUE; return FALSE; } #endif /* HAVE_GUDEV */ static void on_control_stream_removed (GvcMixerControl *control, guint id, GsdMediaKeysManager *manager) { if (manager->priv->sink != NULL) { if (gvc_mixer_stream_get_id (manager->priv->sink) == id) g_clear_object (&manager->priv->sink); } if (manager->priv->source != NULL) { if (gvc_mixer_stream_get_id (manager->priv->source) == id) g_clear_object (&manager->priv->source); } #ifdef HAVE_GUDEV g_hash_table_foreach_remove (manager->priv->streams, (GHRFunc) remove_stream, GUINT_TO_POINTER (id)); #endif } static void free_media_player (MediaPlayer *player) { if (player->watch_id > 0) { g_bus_unwatch_name (player->watch_id); player->watch_id = 0; } g_free (player->application); g_free (player->dbus_name); g_free (player); } static gint find_by_application (gconstpointer a, gconstpointer b) { return strcmp (((MediaPlayer *)a)->application, b); } static gint find_by_name (gconstpointer a, gconstpointer b) { return strcmp (((MediaPlayer *)a)->dbus_name, b); } static gint find_by_time (gconstpointer a, gconstpointer b) { return ((MediaPlayer *)a)->time < ((MediaPlayer *)b)->time; } static void name_vanished_handler (GDBusConnection *connection, const gchar *name, GsdMediaKeysManager *manager) { GList *iter; iter = g_list_find_custom (manager->priv->media_players, name, find_by_name); if (iter != NULL) { MediaPlayer *player; player = iter->data; g_debug ("Deregistering vanished %s (dbus_name: %s)", player->application, player->dbus_name); free_media_player (player); manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter); } } /* * Register a new media player. Most applications will want to call * this with time = GDK_CURRENT_TIME. This way, the last registered * player will receive media events. In some cases, applications * may want to register with a lower priority (usually 1), to grab * events only nobody is interested. */ static void gsd_media_keys_manager_grab_media_player_keys (GsdMediaKeysManager *manager, const char *application, const char *dbus_name, guint32 time) { GList *iter; MediaPlayer *media_player; guint watch_id; if (time == GDK_CURRENT_TIME) { GTimeVal tv; g_get_current_time (&tv); time = tv.tv_sec * 1000 + tv.tv_usec / 1000; } iter = g_list_find_custom (manager->priv->media_players, application, find_by_application); if (iter != NULL) { if (((MediaPlayer *)iter->data)->time < time) { MediaPlayer *player = iter->data; free_media_player (player); manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter); } else { return; } } watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, dbus_name, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, (GBusNameVanishedCallback) name_vanished_handler, manager, NULL); g_debug ("Registering %s at %u", application, time); media_player = g_new0 (MediaPlayer, 1); media_player->application = g_strdup (application); media_player->dbus_name = g_strdup (dbus_name); media_player->time = time; media_player->watch_id = watch_id; manager->priv->media_players = g_list_insert_sorted (manager->priv->media_players, media_player, find_by_time); } static void gsd_media_keys_manager_release_media_player_keys (GsdMediaKeysManager *manager, const char *application, const char *name) { GList *iter = NULL; g_return_if_fail (application != NULL || name != NULL); if (application != NULL) { iter = g_list_find_custom (manager->priv->media_players, application, find_by_application); } if (iter == NULL && name != NULL) { iter = g_list_find_custom (manager->priv->media_players, name, find_by_name); } if (iter != NULL) { MediaPlayer *player; player = iter->data; g_debug ("Deregistering %s (dbus_name: %s)", application, player->dbus_name); free_media_player (player); manager->priv->media_players = g_list_delete_link (manager->priv->media_players, iter); } } static gboolean gsd_media_player_key_pressed (GsdMediaKeysManager *manager, const char *key) { const char *application; gboolean have_listeners; GError *error = NULL; MediaPlayer *player; g_return_val_if_fail (key != NULL, FALSE); g_debug ("Media key '%s' pressed", key); have_listeners = (manager->priv->media_players != NULL); if (!have_listeners) { /* Popup a dialog with an (/) icon */ show_osd (manager, "action-unavailable-symbolic", NULL, -1); return TRUE; } player = manager->priv->media_players->data; application = player->application; if (g_dbus_connection_emit_signal (manager->priv->connection, player->dbus_name, GSD_MEDIA_KEYS_DBUS_PATH, GSD_MEDIA_KEYS_DBUS_NAME, "MediaPlayerKeyPressed", g_variant_new ("(ss)", application ? application : "", key), &error) == FALSE) { g_debug ("Error emitting signal: %s", error->message); g_error_free (error); } return !have_listeners; } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GsdMediaKeysManager *manager = (GsdMediaKeysManager *) user_data; g_debug ("Calling method '%s' for media-keys", method_name); if (g_strcmp0 (method_name, "ReleaseMediaPlayerKeys") == 0) { const char *app_name; g_variant_get (parameters, "(&s)", &app_name); gsd_media_keys_manager_release_media_player_keys (manager, app_name, sender); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "GrabMediaPlayerKeys") == 0) { const char *app_name; guint32 time; g_variant_get (parameters, "(&su)", &app_name, &time); gsd_media_keys_manager_grab_media_player_keys (manager, app_name, sender, time); g_dbus_method_invocation_return_value (invocation, NULL); } } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, /* Get Property */ NULL, /* Set Property */ }; static gboolean do_multimedia_player_action (GsdMediaKeysManager *manager, const char *icon, const char *key) { if (icon != NULL) ubuntu_osd_notification_show_icon (icon, key); return gsd_media_player_key_pressed (manager, key); } static void on_xrandr_action_call_finished (GObject *source_object, GAsyncResult *res, GsdMediaKeysManager *manager) { GError *error = NULL; GVariant *variant; char *action; action = g_object_get_data (G_OBJECT (source_object), "gsd-media-keys-manager-xrandr-action"); variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); g_object_unref (manager->priv->cancellable); manager->priv->cancellable = NULL; if (error != NULL) { g_warning ("Unable to call '%s': %s", action, error->message); g_error_free (error); } else { g_variant_unref (variant); } g_free (action); } static void do_xrandr_action (GsdMediaKeysManager *manager, const char *action, gint64 timestamp) { GsdMediaKeysManagerPrivate *priv = manager->priv; if (priv->connection == NULL || priv->xrandr_proxy == NULL) { g_warning ("No existing D-Bus connection trying to handle XRANDR keys"); return; } if (priv->cancellable != NULL) { g_debug ("xrandr action already in flight"); return; } priv->cancellable = g_cancellable_new (); g_object_set_data (G_OBJECT (priv->xrandr_proxy), "gsd-media-keys-manager-xrandr-action", g_strdup (action)); g_dbus_proxy_call (priv->xrandr_proxy, action, g_variant_new ("(x)", timestamp), G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, (GAsyncReadyCallback) on_xrandr_action_call_finished, manager); } static gboolean do_video_out_action (GsdMediaKeysManager *manager, gint64 timestamp) { do_xrandr_action (manager, "VideoModeSwitch", timestamp); return FALSE; } static gboolean do_video_rotate_action (GsdMediaKeysManager *manager, gint64 timestamp) { do_xrandr_action (manager, "Rotate", timestamp); return FALSE; } static void do_toggle_accessibility_key (const char *key) { GSettings *settings; gboolean state; settings = g_settings_new ("org.gnome.desktop.a11y.applications"); state = g_settings_get_boolean (settings, key); g_settings_set_boolean (settings, key, !state); g_object_unref (settings); } static void do_magnifier_action (GsdMediaKeysManager *manager) { do_toggle_accessibility_key ("screen-magnifier-enabled"); } static void do_screenreader_action (GsdMediaKeysManager *manager) { do_toggle_accessibility_key ("screen-reader-enabled"); } static void do_on_screen_keyboard_action (GsdMediaKeysManager *manager) { do_toggle_accessibility_key ("screen-keyboard-enabled"); } static void do_text_size_action (GsdMediaKeysManager *manager, MediaKeyType type) { gdouble factor, best, distance; guint i; /* Same values used in the Seeing tab of the Universal Access panel */ static gdouble factors[] = { 0.75, 1.0, 1.25, 1.5 }; /* Figure out the current DPI scaling factor */ factor = g_settings_get_double (manager->priv->interface_settings, "text-scaling-factor"); factor += (type == INCREASE_TEXT_KEY ? 0.25 : -0.25); /* Try to find a matching value */ distance = 1e6; best = 1.0; for (i = 0; i < G_N_ELEMENTS(factors); i++) { gdouble d; d = fabs (factor - factors[i]); if (d < distance) { best = factors[i]; distance = d; } } if (best == 1.0) g_settings_reset (manager->priv->interface_settings, "text-scaling-factor"); else g_settings_set_double (manager->priv->interface_settings, "text-scaling-factor", best); } static void do_magnifier_zoom_action (GsdMediaKeysManager *manager, MediaKeyType type) { GSettings *settings; gdouble offset, value; if (type == MAGNIFIER_ZOOM_IN_KEY) offset = 1.0; else offset = -1.0; settings = g_settings_new ("org.gnome.desktop.a11y.magnifier"); value = g_settings_get_double (settings, "mag-factor"); value += offset; value = roundl (value); g_settings_set_double (settings, "mag-factor", value); g_object_unref (settings); } static void do_toggle_contrast_action (GsdMediaKeysManager *manager) { gboolean high_contrast; char *theme; /* Are we using HighContrast now? */ theme = g_settings_get_string (manager->priv->interface_settings, "gtk-theme"); high_contrast = g_str_equal (theme, HIGH_CONTRAST); g_free (theme); if (high_contrast != FALSE) { if (manager->priv->gtk_theme == NULL) g_settings_reset (manager->priv->interface_settings, "gtk-theme"); else g_settings_set (manager->priv->interface_settings, "gtk-theme", manager->priv->gtk_theme); g_settings_set (manager->priv->interface_settings, "icon-theme", manager->priv->icon_theme); } else { g_settings_set (manager->priv->interface_settings, "gtk-theme", HIGH_CONTRAST); g_settings_set (manager->priv->interface_settings, "icon-theme", HIGH_CONTRAST); } } static void power_action (GsdMediaKeysManager *manager, const char *action, gboolean allow_interaction) { g_dbus_proxy_call (manager->priv->logind_proxy, action, g_variant_new ("(b)", allow_interaction), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, manager->priv->bus_cancellable, NULL, NULL); } static void do_config_power_action (GsdMediaKeysManager *manager, const gchar *config_key, gboolean in_lock_screen) { GsdPowerActionType action_type; action_type = g_settings_get_enum (manager->priv->power_settings, config_key); switch (action_type) { case GSD_POWER_ACTION_SUSPEND: power_action (manager, "Suspend", !in_lock_screen); break; case GSD_POWER_ACTION_INTERACTIVE: if (!in_lock_screen) gnome_session_shutdown (manager); break; case GSD_POWER_ACTION_SHUTDOWN: power_action (manager, "PowerOff", !in_lock_screen); break; case GSD_POWER_ACTION_HIBERNATE: power_action (manager, "Hibernate", !in_lock_screen); break; case GSD_POWER_ACTION_BLANK: case GSD_POWER_ACTION_NOTHING: /* these actions cannot be handled by media-keys and * are not used in this context */ break; } } #ifdef HAVE_FCITX static gchar * get_fcitx_name (const gchar *name) { gchar *fcitx_name = g_strdup (name); gchar *separator = strchr (fcitx_name, '+'); if (separator) *separator = '-'; return fcitx_name; } static gboolean input_source_is_fcitx_engine (const gchar *type, const gchar *name, const gchar *engine) { if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { if (g_str_has_prefix (engine, FCITX_XKB_PREFIX)) { gboolean equal; gchar *fcitx_name = get_fcitx_name (name); equal = g_str_equal (fcitx_name, engine + strlen (FCITX_XKB_PREFIX)); g_free (fcitx_name); return equal; } } else if (g_str_equal (type, INPUT_SOURCE_TYPE_FCITX)) { return g_str_equal (name, engine); } return FALSE; } #endif static void do_switch_input_source_action (GsdMediaKeysManager *manager, MediaKeyType type) { GsdMediaKeysManagerPrivate *priv = manager->priv; GSettings *settings; GVariant *sources; const gchar *source_type; guint first; gint i, n; if (g_strcmp0 (g_getenv ("DESKTOP_SESSION"), "ubuntu") != 0) if (!priv->have_legacy_keygrabber) return; settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); n = g_variant_n_children (sources); if (n < 2) goto out; i = -1; #ifdef HAVE_FCITX if (priv->is_fcitx_active && priv->fcitx) { gchar *engine = fcitx_input_method_get_current_im (priv->fcitx); if (engine) { GVariantIter iter; const gchar *source_name; g_variant_iter_init (&iter, sources); for (i = 0; g_variant_iter_next (&iter, "(&s&s)", &source_type, &source_name); i++) { if (input_source_is_fcitx_engine (source_type, source_name, engine)) { break; } } if (i >= g_variant_n_children (sources)) i = -1; g_free (engine); } } #endif if (i < 0) i = g_settings_get_uint (settings, KEY_CURRENT_INPUT_SOURCE); first = i; if (type == SWITCH_INPUT_SOURCE_KEY) { do { i = (i + 1) % n; g_variant_get_child (sources, i, "(&s&s)", &source_type, NULL); } while (i != first && ((g_str_equal (source_type, INPUT_SOURCE_TYPE_IBUS) && !priv->is_ibus_active) || (g_str_equal (source_type, INPUT_SOURCE_TYPE_FCITX) && !priv->is_fcitx_active))); } else { do { i = (i + n - 1) % n; g_variant_get_child (sources, i, "(&s&s)", &source_type, NULL); } while (i != first && ((g_str_equal (source_type, INPUT_SOURCE_TYPE_IBUS) && !priv->is_ibus_active) || (g_str_equal (source_type, INPUT_SOURCE_TYPE_FCITX) && !priv->is_fcitx_active))); } if (i != first) g_settings_set_uint (settings, KEY_CURRENT_INPUT_SOURCE, i); out: g_variant_unref (sources); g_object_unref (settings); } static void update_screen_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; guint percentage; GVariant *new_percentage; GsdBrightnessActionData *data = (GsdBrightnessActionData *) user_data; GsdMediaKeysManager *manager = data->manager; new_percentage = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); if (new_percentage == NULL) { g_warning ("Failed to set new screen percentage: %s", error->message); g_error_free (error); g_free (data); return; } /* update the dialog with the new value */ g_variant_get (new_percentage, "(u)", &percentage); guint osd_percentage; if (data->old_percentage == 100 && data->type == SCREEN_BRIGHTNESS_UP_KEY) osd_percentage = 101; else if (data->old_percentage == 0 && data->type == SCREEN_BRIGHTNESS_DOWN_KEY) osd_percentage = -1; else osd_percentage = CLAMP (percentage, 0, 100); if (!ubuntu_osd_notification_show_brightness (manager, osd_percentage)) { show_osd (manager, "display-brightness-symbolic", NULL, percentage); } g_free (data); g_variant_unref (new_percentage); } static void do_screen_brightness_action_real (GObject *source_object, GAsyncResult *res, gpointer user_data) { GsdBrightnessActionData *data = (GsdBrightnessActionData *) user_data; GsdMediaKeysManager *manager = data->manager; GError *error = NULL; GVariant *old_percentage = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); if (old_percentage == NULL) { g_warning ("Failed to get old screen percentage: %s", error->message); g_error_free (error); g_free (data); return; } g_variant_get (old_percentage, "(u)", &data->old_percentage); /* call into the power plugin */ g_dbus_proxy_call (manager->priv->power_screen_proxy, data->type == SCREEN_BRIGHTNESS_UP_KEY ? "StepUp" : "StepDown", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, update_screen_cb, data); g_variant_unref (old_percentage); } static void do_screen_brightness_action (GsdMediaKeysManager *manager, MediaKeyType type) { if (manager->priv->connection == NULL || manager->priv->power_screen_proxy == NULL) { g_warning ("No existing D-Bus connection trying to handle power keys"); return; } GsdBrightnessActionData *data = g_new0 (GsdBrightnessActionData, 1); data->manager = manager; data->type = type; g_dbus_proxy_call (manager->priv->power_screen_proxy, "GetPercentage", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, do_screen_brightness_action_real, data); } static void update_keyboard_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; guint percentage; GVariant *new_percentage; GsdMediaKeysManager *manager = GSD_MEDIA_KEYS_MANAGER (user_data); new_percentage = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); if (new_percentage == NULL) { g_warning ("Failed to set new keyboard percentage: %s", error->message); g_error_free (error); return; } /* update the dialog with the new value */ g_variant_get (new_percentage, "(u)", &percentage); /* FIXME: No overshoot effect for keyboard, as the power plugin has no interface * to get the old brightness */ if (!ubuntu_osd_notification_show_kb_backlight (manager, CLAMP (percentage, 0, 100))) { show_osd (manager, "keyboard-brightness-symbolic", NULL, percentage); } g_variant_unref (new_percentage); } static void do_keyboard_brightness_action (GsdMediaKeysManager *manager, MediaKeyType type) { const char *cmd; if (manager->priv->connection == NULL || manager->priv->power_keyboard_proxy == NULL) { g_warning ("No existing D-Bus connection trying to handle power keys"); return; } switch (type) { case KEYBOARD_BRIGHTNESS_UP_KEY: cmd = "StepUp"; break; case KEYBOARD_BRIGHTNESS_DOWN_KEY: cmd = "StepDown"; break; case KEYBOARD_BRIGHTNESS_TOGGLE_KEY: cmd = "Toggle"; break; default: g_assert_not_reached (); } /* call into the power plugin */ g_dbus_proxy_call (manager->priv->power_keyboard_proxy, cmd, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, update_keyboard_cb, manager); } static void do_screenshot_action (GsdMediaKeysManager *manager, MediaKeyType type) { switch (type){ case SCREENSHOT_KEY: execute (manager, "gnome-screenshot", FALSE); break; case WINDOW_SCREENSHOT_KEY: execute (manager, "gnome-screenshot --window", FALSE); break; case AREA_SCREENSHOT_KEY: execute (manager, "gnome-screenshot --area", FALSE); break; case SCREENSHOT_CLIP_KEY: execute (manager, "gnome-screenshot --clipboard", FALSE); break; case WINDOW_SCREENSHOT_CLIP_KEY: execute (manager, "gnome-screenshot --window --clipboard", FALSE); break; case AREA_SCREENSHOT_CLIP_KEY: execute (manager, "gnome-screenshot --area --clipboard", FALSE); } } static void do_custom_action (GsdMediaKeysManager *manager, guint deviceid, MediaKey *key, gint64 timestamp) { g_debug ("Launching custom action for key (on device id %d)", deviceid); execute (manager, key->custom_command, FALSE); } static gboolean do_action (GsdMediaKeysManager *manager, guint deviceid, MediaKeyType type, gint64 timestamp) { g_debug ("Launching action for key type '%d' (on device id %d)", type, deviceid); switch (type) { case TOUCHPAD_KEY: do_touchpad_action (manager); break; case TOUCHPAD_ON_KEY: do_touchpad_osd_action (manager, TRUE); break; case TOUCHPAD_OFF_KEY: do_touchpad_osd_action (manager, FALSE); break; case MUTE_KEY: case VOLUME_DOWN_KEY: case VOLUME_UP_KEY: do_sound_action (manager, deviceid, type, TRUE, FALSE); break; case MIC_MUTE_KEY: do_sound_action (manager, deviceid, MUTE_KEY, FALSE, TRUE); break; case MUTE_QUIET_KEY: do_sound_action (manager, deviceid, MUTE_KEY, TRUE, TRUE); break; case VOLUME_DOWN_QUIET_KEY: do_sound_action (manager, deviceid, VOLUME_DOWN_KEY, TRUE, TRUE); break; case VOLUME_UP_QUIET_KEY: do_sound_action (manager, deviceid, VOLUME_UP_KEY, TRUE, TRUE); break; case LOGOUT_KEY: do_logout_action (manager); break; case EJECT_KEY: do_eject_action (manager); break; case HOME_KEY: do_home_key_action (manager, timestamp); break; case SEARCH_KEY: do_search_action (manager, timestamp); break; case EMAIL_KEY: do_url_action (manager, "mailto", timestamp); break; case SCREENSAVER_KEY: do_lock_screensaver (manager); break; case HELP_KEY: do_url_action (manager, "ghelp", timestamp); break; case SCREENSHOT_KEY: case SCREENSHOT_CLIP_KEY: case WINDOW_SCREENSHOT_KEY: case WINDOW_SCREENSHOT_CLIP_KEY: case AREA_SCREENSHOT_KEY: case AREA_SCREENSHOT_CLIP_KEY: do_screenshot_action (manager, type); break; case TERMINAL_KEY: do_terminal_action (manager); break; case WWW_KEY: do_url_action (manager, "http", timestamp); break; case MEDIA_KEY: do_media_action (manager, timestamp); break; case CALCULATOR_KEY: do_execute_desktop_or_desktop (manager, "gcalctool.desktop", "gnome-calculator.desktop", timestamp); break; case PLAY_KEY: return do_multimedia_player_action (manager, NULL, "Play"); case PAUSE_KEY: return do_multimedia_player_action (manager, NULL, "Pause"); case STOP_KEY: return do_multimedia_player_action (manager, NULL, "Stop"); case PREVIOUS_KEY: return do_multimedia_player_action (manager, NULL, "Previous"); case NEXT_KEY: return do_multimedia_player_action (manager, NULL, "Next"); case REWIND_KEY: return do_multimedia_player_action (manager, NULL, "Rewind"); case FORWARD_KEY: return do_multimedia_player_action (manager, NULL, "FastForward"); case REPEAT_KEY: return do_multimedia_player_action (manager, NULL, "Repeat"); case RANDOM_KEY: return do_multimedia_player_action (manager, NULL, "Shuffle"); case VIDEO_OUT_KEY: do_video_out_action (manager, timestamp); break; case ROTATE_VIDEO_KEY: do_video_rotate_action (manager, timestamp); break; case MAGNIFIER_KEY: do_magnifier_action (manager); break; case SCREENREADER_KEY: do_screenreader_action (manager); break; case ON_SCREEN_KEYBOARD_KEY: do_on_screen_keyboard_action (manager); break; case INCREASE_TEXT_KEY: case DECREASE_TEXT_KEY: do_text_size_action (manager, type); break; case MAGNIFIER_ZOOM_IN_KEY: case MAGNIFIER_ZOOM_OUT_KEY: do_magnifier_zoom_action (manager, type); break; case TOGGLE_CONTRAST_KEY: do_toggle_contrast_action (manager); break; case POWER_KEY: do_config_power_action (manager, "button-power", FALSE); break; case SLEEP_KEY: do_config_power_action (manager, "button-sleep", FALSE); break; case SUSPEND_KEY: do_config_power_action (manager, "button-suspend", FALSE); break; case HIBERNATE_KEY: do_config_power_action (manager, "button-hibernate", FALSE); break; case POWER_KEY_NO_DIALOG: do_config_power_action (manager, "button-power", TRUE); break; case SLEEP_KEY_NO_DIALOG: do_config_power_action (manager, "button-sleep", TRUE); break; case SUSPEND_KEY_NO_DIALOG: do_config_power_action (manager, "button-suspend", TRUE); break; case HIBERNATE_KEY_NO_DIALOG: do_config_power_action (manager, "button-hibernate", TRUE); break; case SCREEN_BRIGHTNESS_UP_KEY: case SCREEN_BRIGHTNESS_DOWN_KEY: do_screen_brightness_action (manager, type); break; case KEYBOARD_BRIGHTNESS_UP_KEY: case KEYBOARD_BRIGHTNESS_DOWN_KEY: case KEYBOARD_BRIGHTNESS_TOGGLE_KEY: do_keyboard_brightness_action (manager, type); break; case BATTERY_KEY: do_execute_desktop_or_desktop (manager, "gnome-power-statistics.desktop", "", timestamp); break; case SWITCH_INPUT_SOURCE_KEY: case SWITCH_INPUT_SOURCE_BACKWARD_KEY: do_switch_input_source_action (manager, type); break; /* Note, no default so compiler catches missing keys */ case CUSTOM_KEY: g_assert_not_reached (); } return FALSE; } static GdkScreen * get_screen_from_root (GsdMediaKeysManager *manager, Window root) { GSList *l; /* Look for which screen we're receiving events */ for (l = manager->priv->screens; l != NULL; l = l->next) { GdkScreen *screen = (GdkScreen *) l->data; GdkWindow *window = gdk_screen_get_root_window (screen); if (GDK_WINDOW_XID (window) == root) return screen; } return NULL; } static GdkFilterReturn filter_key_events (XEvent *xevent, GdkEvent *event, GsdMediaKeysManager *manager) { static gboolean ok_to_switch = TRUE; XIEvent *xiev; XIDeviceEvent *xev; XGenericEventCookie *cookie; guint i; guint deviceid; /* verify we have a key event */ if (xevent->type != GenericEvent) return GDK_FILTER_CONTINUE; cookie = &xevent->xcookie; if (cookie->extension != manager->priv->opcode) return GDK_FILTER_CONTINUE; xiev = (XIEvent *) xevent->xcookie.data; if (xiev->evtype != XI_KeyPress && xiev->evtype != XI_KeyRelease) return GDK_FILTER_CONTINUE; xev = (XIDeviceEvent *) xiev; deviceid = xev->sourceid; if (xiev->evtype == XI_KeyPress) ok_to_switch = TRUE; for (i = 0; i < manager->priv->keys->len; i++) { MediaKey *key; key = g_ptr_array_index (manager->priv->keys, i); if (match_xi2_key (key->key, xev)) { switch (key->key_type) { case VOLUME_DOWN_KEY: case VOLUME_UP_KEY: case VOLUME_DOWN_QUIET_KEY: case VOLUME_UP_QUIET_KEY: case SCREEN_BRIGHTNESS_UP_KEY: case SCREEN_BRIGHTNESS_DOWN_KEY: case KEYBOARD_BRIGHTNESS_UP_KEY: case KEYBOARD_BRIGHTNESS_DOWN_KEY: /* auto-repeatable keys */ if (xiev->evtype != XI_KeyPress) return GDK_FILTER_CONTINUE; break; default: if (xiev->evtype != XI_KeyRelease) { return GDK_FILTER_CONTINUE; } } manager->priv->current_screen = get_screen_from_root (manager, xev->root); if (key->key_type == CUSTOM_KEY) { do_custom_action (manager, deviceid, key, xev->time); return GDK_FILTER_REMOVE; } if (key->key_type == SWITCH_INPUT_SOURCE_KEY || key->key_type == SWITCH_INPUT_SOURCE_BACKWARD_KEY) { if (ok_to_switch) { do_action (manager, deviceid, key->key_type, xev->time); ok_to_switch = FALSE; } return GDK_FILTER_CONTINUE; } if (do_action (manager, deviceid, key->key_type, xev->time) == FALSE) { return GDK_FILTER_REMOVE; } else { return GDK_FILTER_CONTINUE; } } } return GDK_FILTER_CONTINUE; } static void on_accelerator_activated (ShellKeyGrabber *grabber, guint accel_id, guint deviceid, guint timestamp, GsdMediaKeysManager *manager) { guint i; for (i = 0; i < manager->priv->keys->len; i++) { MediaKey *key; key = g_ptr_array_index (manager->priv->keys, i); if (key->accel_id != accel_id) continue; if (key->key_type == CUSTOM_KEY) do_custom_action (manager, deviceid, key, timestamp); else do_action (manager, deviceid, key->key_type, timestamp); return; } } static void update_theme_settings (GSettings *settings, const char *key, GsdMediaKeysManager *manager) { char *theme; theme = g_settings_get_string (manager->priv->interface_settings, key); if (g_str_equal (theme, HIGH_CONTRAST)) { g_free (theme); } else { if (g_str_equal (key, "gtk-theme")) { g_free (manager->priv->gtk_theme); manager->priv->gtk_theme = theme; } else { g_free (manager->priv->icon_theme); manager->priv->icon_theme = theme; } } } static void on_wdypi_action (int action, void *userdata) { GsdMediaKeysManager *manager = userdata; pa_backend *pb = manager->priv->wdypi_pa_backend; pa_backend_set_context(pb, gvc_mixer_control_get_pa_context(manager->priv->volume)); switch (action) { case WDYPI_DIALOG_SOUND_SETTINGS: execute(manager, "unity-control-center sound", FALSE); break; case WDYPI_DIALOG_HEADPHONES: pa_backend_set_port(pb, "analog-output-headphones", true); pa_backend_set_port(pb, "analog-input-internal-mic", false); break; case WDYPI_DIALOG_HEADSET: pa_backend_set_port(pb, "analog-output-headphones", true); pa_backend_set_port(pb, "analog-input-headset-mic", false); break; case WDYPI_DIALOG_MICROPHONE: pa_backend_set_port(pb, "analog-output-speaker", true); pa_backend_set_port(pb, "analog-input-headphone-mic", false); break; default: break; } } static void on_wdypi_popup (bool hsmic, bool hpmic, void *userdata) { if (!hpmic && !hsmic) wdypi_dialog_kill(); else wdypi_dialog_run(hsmic, hpmic, on_wdypi_action, userdata); } static void on_control_card_info_updated (GvcMixerControl *control, gpointer card_info, GsdMediaKeysManager *manager) { pa_backend_card_changed (manager->priv->wdypi_pa_backend, card_info); #ifdef TEST_WDYPI_DIALOG /* Just a simple way to test the dialog on all types of hardware (pops up dialog on program start, and on every plug in) */ on_wdypi_popup (true, true, manager); #endif } static void initialize_volume_handler (GsdMediaKeysManager *manager) { /* initialise Volume handler * * We do this one here to force checking gstreamer cache, etc. * The rest (grabbing and setting the keys) can happen in an * idle. */ gnome_settings_profile_start ("gvc_mixer_control_new"); manager->priv->volume = gvc_mixer_control_new ("GNOME Volume Control Media Keys"); manager->priv->wdypi_pa_backend = pa_backend_new(on_wdypi_popup, manager); g_signal_connect (manager->priv->volume, "state-changed", G_CALLBACK (on_control_state_changed), manager); g_signal_connect (manager->priv->volume, "default-sink-changed", G_CALLBACK (on_control_default_sink_changed), manager); g_signal_connect (manager->priv->volume, "default-source-changed", G_CALLBACK (on_control_default_source_changed), manager); g_signal_connect (manager->priv->volume, "stream-removed", G_CALLBACK (on_control_stream_removed), manager); g_signal_connect (manager->priv->volume, "card-info", G_CALLBACK (on_control_card_info_updated), manager); gvc_mixer_control_open (manager->priv->volume); gnome_settings_profile_end ("gvc_mixer_control_new"); } static void on_shell_proxy_ready (GObject *source, GAsyncResult *result, gpointer data) { GsdMediaKeysManager *manager = data; manager->priv->shell_proxy = g_dbus_proxy_new_for_bus_finish (result, NULL); } static void on_key_grabber_ready (GObject *source, GAsyncResult *result, gpointer data) { GsdMediaKeysManager *manager = data; manager->priv->key_grabber = shell_key_grabber_proxy_new_for_bus_finish (result, NULL); if (!manager->priv->key_grabber) return; g_signal_connect (manager->priv->key_grabber, "accelerator-activated", G_CALLBACK (on_accelerator_activated), manager); init_kbd (manager); } static gboolean session_has_key_grabber (void) { const gchar *session = g_getenv ("DESKTOP_SESSION"); return g_strcmp0 (session, "gnome") == 0 || g_strcmp0 (session, "ubuntu") == 0; } static void on_shell_appeared (GDBusConnection *connection, const char *name, const char *name_owner, gpointer user_data) { if (!session_has_key_grabber ()) return; GsdMediaKeysManager *manager = user_data; shell_key_grabber_proxy_new_for_bus (G_BUS_TYPE_SESSION, 0, name_owner, SHELL_DBUS_PATH, manager->priv->grab_cancellable, on_key_grabber_ready, manager); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, 0, NULL, name_owner, SHELL_DBUS_PATH, SHELL_DBUS_NAME, manager->priv->shell_cancellable, on_shell_proxy_ready, manager); } static void on_shell_vanished (GDBusConnection *connection, const char *name, gpointer user_data) { if (!session_has_key_grabber ()) return; GsdMediaKeysManager *manager = user_data; g_ptr_array_set_size (manager->priv->keys, 0); g_clear_object (&manager->priv->key_grabber); g_clear_object (&manager->priv->shell_proxy); } static void start_legacy_grabber (GDBusConnection *connection, const char *name, const char *name_owner, gpointer user_data) { GsdMediaKeysManager *manager = user_data; GSList *l; if (session_has_key_grabber ()) return; manager->priv->have_legacy_keygrabber = TRUE; g_debug ("start_legacy_grabber"); if (manager->priv->keys == NULL) return; if (!name_owner) return; init_screens (manager); init_kbd (manager); /* Start filtering the events */ for (l = manager->priv->screens; l != NULL; l = l->next) { gnome_settings_profile_start ("gdk_window_add_filter"); g_debug ("adding key filter for screen: %d", gdk_screen_get_number (l->data)); gdk_window_add_filter (gdk_screen_get_root_window (l->data), (GdkFilterFunc) filter_key_events, manager); gnome_settings_profile_end ("gdk_window_add_filter"); } } static void stop_legacy_grabber (GDBusConnection *connection, const char *name, gpointer user_data) { GsdMediaKeysManager *manager = user_data; if (session_has_key_grabber ()) return; manager->priv->have_legacy_keygrabber = FALSE; g_ptr_array_set_size (manager->priv->keys, 0); } static gboolean start_media_keys_idle_cb (GsdMediaKeysManager *manager) { const gchar *module; g_debug ("Starting media_keys manager"); gnome_settings_profile_start (NULL); module = g_getenv (ENV_GTK_IM_MODULE); #ifdef HAVE_IBUS manager->priv->is_ibus_active = g_strcmp0 (module, GTK_IM_MODULE_IBUS) == 0; #endif #ifdef HAVE_FCITX manager->priv->is_fcitx_active = g_strcmp0 (module, GTK_IM_MODULE_FCITX) == 0; if (manager->priv->is_fcitx_active) { GError *error = NULL; manager->priv->fcitx = fcitx_input_method_new (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, 0, NULL, &error); if (!manager->priv->fcitx) { g_warning ("Fcitx connection unavailable: %s", error->message); g_error_free (error); } } #endif manager->priv->keys = g_ptr_array_new_with_free_func ((GDestroyNotify) media_key_free); initialize_volume_handler (manager); manager->priv->settings = g_settings_new (SETTINGS_BINDING_DIR); g_signal_connect (G_OBJECT (manager->priv->settings), "changed", G_CALLBACK (gsettings_changed_cb), manager); g_signal_connect (G_OBJECT (manager->priv->settings), "changed::custom-keybindings", G_CALLBACK (gsettings_custom_changed_cb), manager); manager->priv->input_settings = g_settings_new (INPUT_SETTINGS_BINDING_DIR); g_signal_connect (G_OBJECT (manager->priv->input_settings), "changed", G_CALLBACK (gsettings_changed_cb), manager); g_signal_connect (G_OBJECT (manager->priv->input_settings), "changed::custom-keybindings", G_CALLBACK (gsettings_custom_changed_cb), manager); manager->priv->custom_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); manager->priv->sound_settings = g_settings_new ("com.ubuntu.sound"); /* for the power plugin interface code */ manager->priv->power_settings = g_settings_new (SETTINGS_POWER_DIR); /* Logic from http://git.gnome.org/browse/gnome-shell/tree/js/ui/status/accessibility.js#n163 */ manager->priv->interface_settings = g_settings_new (SETTINGS_INTERFACE_DIR); g_signal_connect (G_OBJECT (manager->priv->interface_settings), "changed::gtk-theme", G_CALLBACK (update_theme_settings), manager); g_signal_connect (G_OBJECT (manager->priv->interface_settings), "changed::icon-theme", G_CALLBACK (update_theme_settings), manager); manager->priv->gtk_theme = g_settings_get_string (manager->priv->interface_settings, "gtk-theme"); if (g_str_equal (manager->priv->gtk_theme, HIGH_CONTRAST)) { g_free (manager->priv->gtk_theme); manager->priv->gtk_theme = NULL; } manager->priv->icon_theme = g_settings_get_string (manager->priv->interface_settings, "icon-theme"); ensure_cancellable (&manager->priv->grab_cancellable); ensure_cancellable (&manager->priv->shell_cancellable); manager->priv->name_owner_id = g_bus_watch_name (G_BUS_TYPE_SESSION, SHELL_DBUS_NAME, 0, on_shell_appeared, on_shell_vanished, manager, NULL); manager->priv->unity_name_owner_id = g_bus_watch_name (G_BUS_TYPE_SESSION, UNITY_DBUS_NAME, 0, start_legacy_grabber, stop_legacy_grabber, manager, NULL); manager->priv->panel_name_owner_id = g_bus_watch_name (G_BUS_TYPE_SESSION, PANEL_DBUS_NAME, 0, start_legacy_grabber, stop_legacy_grabber, manager, NULL); gnome_settings_profile_end (NULL); manager->priv->start_idle_id = 0; return FALSE; } gboolean gsd_media_keys_manager_start (GsdMediaKeysManager *manager, GError **error) { const char * const subsystems[] = { "input", "usb", "sound", NULL }; gnome_settings_profile_start (NULL); if (supports_xinput2_devices (&manager->priv->opcode) == FALSE) { g_debug ("No Xinput2 support, disabling plugin"); return TRUE; } #ifdef HAVE_GUDEV manager->priv->streams = g_hash_table_new (g_direct_hash, g_direct_equal); manager->priv->udev_client = g_udev_client_new (subsystems); #endif manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_media_keys_idle_cb, manager); register_manager (manager_object); gnome_settings_profile_end (NULL); return TRUE; } void gsd_media_keys_manager_stop (GsdMediaKeysManager *manager) { GsdMediaKeysManagerPrivate *priv = manager->priv; GSList *ls; int i; g_debug ("Stopping media_keys manager"); if (priv->bus_cancellable != NULL) { g_cancellable_cancel (priv->bus_cancellable); g_object_unref (priv->bus_cancellable); priv->bus_cancellable = NULL; } if (manager->priv->have_legacy_keygrabber){ for (ls = priv->screens; ls != NULL; ls = ls->next) { gdk_window_remove_filter (gdk_screen_get_root_window (ls->data), (GdkFilterFunc) filter_key_events, manager); } } if (manager->priv->gtksettings != NULL) { g_signal_handlers_disconnect_by_func (manager->priv->gtksettings, sound_theme_changed, manager); manager->priv->gtksettings = NULL; } g_clear_pointer (&manager->priv->ca, ca_context_destroy); #ifdef HAVE_GUDEV g_clear_pointer (&priv->streams, g_hash_table_destroy); g_clear_object (&priv->udev_client); #endif /* HAVE_GUDEV */ g_clear_object (&priv->logind_proxy); g_clear_object (&priv->settings); g_clear_object (&priv->input_settings); g_clear_object (&priv->power_settings); g_clear_object (&priv->power_proxy); g_clear_object (&priv->power_screen_proxy); g_clear_object (&priv->power_keyboard_proxy); g_clear_object (&priv->sound_settings); if (manager->priv->name_owner_id) { g_bus_unwatch_name (manager->priv->name_owner_id); manager->priv->name_owner_id = 0; } if (manager->priv->unity_name_owner_id) { g_bus_unwatch_name (manager->priv->unity_name_owner_id); manager->priv->unity_name_owner_id = 0; } if (manager->priv->panel_name_owner_id) { g_bus_unwatch_name (manager->priv->panel_name_owner_id); manager->priv->panel_name_owner_id = 0; } if (priv->cancellable != NULL) { g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); } g_clear_pointer (&priv->introspection_data, g_dbus_node_info_unref); g_clear_object (&priv->connection); if (priv->volume_notification != NULL) { notify_notification_close (priv->volume_notification, NULL); g_object_unref (priv->volume_notification); priv->volume_notification = NULL; } if (priv->brightness_notification != NULL) { notify_notification_close (priv->brightness_notification, NULL); g_object_unref (priv->brightness_notification); priv->brightness_notification = NULL; } if (priv->kb_backlight_notification != NULL) { notify_notification_close (priv->kb_backlight_notification, NULL); g_object_unref (priv->kb_backlight_notification); priv->kb_backlight_notification = NULL; } if (priv->keys != NULL) { if (manager->priv->have_legacy_keygrabber) gdk_error_trap_push (); for (i = 0; i < priv->keys->len; ++i) { MediaKey *key; key = g_ptr_array_index (manager->priv->keys, i); if (manager->priv->have_legacy_keygrabber && key->key) ungrab_key_unsafe (key->key, priv->screens); else ungrab_media_key (key, manager); } g_ptr_array_free (priv->keys, TRUE); priv->keys = NULL; } if (manager->priv->have_legacy_keygrabber){ gdk_flush (); gdk_error_trap_pop_ignored (); } if (manager->priv->wdypi_pa_backend) { pa_backend_free (manager->priv->wdypi_pa_backend); manager->priv->wdypi_pa_backend = NULL; } wdypi_dialog_kill(); if (priv->grab_cancellable != NULL) { g_cancellable_cancel (priv->grab_cancellable); g_clear_object (&priv->grab_cancellable); } if (priv->shell_cancellable != NULL) { g_cancellable_cancel (priv->shell_cancellable); g_clear_object (&priv->shell_cancellable); } g_clear_pointer (&priv->screens, g_slist_free); g_clear_object (&priv->sink); g_clear_object (&priv->source); g_clear_object (&priv->volume); if (priv->media_players != NULL) { g_list_free_full (priv->media_players, (GDestroyNotify) free_media_player); priv->media_players = NULL; } } static GObject * gsd_media_keys_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdMediaKeysManager *media_keys_manager; media_keys_manager = GSD_MEDIA_KEYS_MANAGER (G_OBJECT_CLASS (gsd_media_keys_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (media_keys_manager); } static void gsd_media_keys_manager_class_init (GsdMediaKeysManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_media_keys_manager_constructor; object_class->finalize = gsd_media_keys_manager_finalize; g_type_class_add_private (klass, sizeof (GsdMediaKeysManagerPrivate)); } static void inhibit_done (GObject *source, GAsyncResult *result, gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY (source); GsdMediaKeysManager *manager = GSD_MEDIA_KEYS_MANAGER (user_data); GError *error = NULL; GVariant *res; GUnixFDList *fd_list = NULL; gint idx; res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error); if (res == NULL) { g_warning ("Unable to inhibit keypresses: %s", error->message); g_error_free (error); } else { g_variant_get (res, "(h)", &idx); manager->priv->inhibit_keys_fd = g_unix_fd_list_get (fd_list, idx, &error); if (manager->priv->inhibit_keys_fd == -1) { g_warning ("Failed to receive system inhibitor fd: %s", error->message); g_error_free (error); } g_debug ("System inhibitor fd is %d", manager->priv->inhibit_keys_fd); g_object_unref (fd_list); g_variant_unref (res); } } static void gsd_media_keys_manager_init (GsdMediaKeysManager *manager) { GError *error; GDBusConnection *bus; error = NULL; manager->priv = GSD_MEDIA_KEYS_MANAGER_GET_PRIVATE (manager); bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (bus == NULL) { g_warning ("Failed to connect to system bus: %s", error->message); g_error_free (error); return; } manager->priv->logind_proxy = g_dbus_proxy_new_sync (bus, 0, NULL, SYSTEMD_DBUS_NAME, SYSTEMD_DBUS_PATH, SYSTEMD_DBUS_INTERFACE, NULL, &error); if (manager->priv->logind_proxy == NULL) { g_warning ("Failed to connect to systemd: %s", error->message); g_error_free (error); } g_object_unref (bus); g_debug ("Adding system inhibitors for power keys"); manager->priv->inhibit_keys_fd = -1; g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy, "Inhibit", g_variant_new ("(ssss)", "handle-power-key:handle-suspend-key:handle-hibernate-key", g_get_user_name (), "GNOME handling keypresses", "block"), 0, G_MAXINT, NULL, NULL, inhibit_done, manager); } static void gsd_media_keys_manager_finalize (GObject *object) { GsdMediaKeysManager *media_keys_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_MEDIA_KEYS_MANAGER (object)); media_keys_manager = GSD_MEDIA_KEYS_MANAGER (object); g_return_if_fail (media_keys_manager->priv != NULL); if (media_keys_manager->priv->start_idle_id != 0) g_source_remove (media_keys_manager->priv->start_idle_id); if (media_keys_manager->priv->inhibit_keys_fd != -1) close (media_keys_manager->priv->inhibit_keys_fd); g_clear_object (&media_keys_manager->priv->screen_saver_proxy); G_OBJECT_CLASS (gsd_media_keys_manager_parent_class)->finalize (object); } static void xrandr_ready_cb (GObject *source_object, GAsyncResult *res, GsdMediaKeysManager *manager) { GError *error = NULL; manager->priv->xrandr_proxy = g_dbus_proxy_new_finish (res, &error); if (manager->priv->xrandr_proxy == NULL) { g_warning ("Failed to get proxy for XRandR operations: %s", error->message); g_error_free (error); } } static void power_ready_cb (GObject *source_object, GAsyncResult *res, GsdMediaKeysManager *manager) { GError *error = NULL; manager->priv->power_proxy = g_dbus_proxy_new_finish (res, &error); if (manager->priv->power_proxy == NULL) { g_warning ("Failed to get proxy for power: %s", error->message); g_error_free (error); } } static void power_screen_ready_cb (GObject *source_object, GAsyncResult *res, GsdMediaKeysManager *manager) { GError *error = NULL; manager->priv->power_screen_proxy = g_dbus_proxy_new_finish (res, &error); if (manager->priv->power_screen_proxy == NULL) { g_warning ("Failed to get proxy for power (screen): %s", error->message); g_error_free (error); } } static void power_keyboard_ready_cb (GObject *source_object, GAsyncResult *res, GsdMediaKeysManager *manager) { GError *error = NULL; manager->priv->power_keyboard_proxy = g_dbus_proxy_new_finish (res, &error); if (manager->priv->power_keyboard_proxy == NULL) { g_warning ("Failed to get proxy for power (keyboard): %s", error->message); g_error_free (error); } } static void on_bus_gotten (GObject *source_object, GAsyncResult *res, GsdMediaKeysManager *manager) { GDBusConnection *connection; GError *error = NULL; if (manager->priv->bus_cancellable == NULL || g_cancellable_is_cancelled (manager->priv->bus_cancellable)) { g_warning ("Operation has been cancelled, so not retrieving session bus"); return; } connection = g_bus_get_finish (res, &error); if (connection == NULL) { g_warning ("Could not get session bus: %s", error->message); g_error_free (error); return; } manager->priv->connection = connection; g_dbus_connection_register_object (connection, GSD_MEDIA_KEYS_DBUS_PATH, manager->priv->introspection_data->interfaces[0], &interface_vtable, manager, NULL, NULL); g_dbus_proxy_new (manager->priv->connection, G_DBUS_PROXY_FLAGS_NONE, NULL, GSD_DBUS_NAME ".XRANDR", GSD_DBUS_PATH "/XRANDR", GSD_DBUS_BASE_INTERFACE ".XRANDR_2", NULL, (GAsyncReadyCallback) xrandr_ready_cb, manager); g_dbus_proxy_new (manager->priv->connection, G_DBUS_PROXY_FLAGS_NONE, NULL, GSD_DBUS_NAME ".Power", GSD_DBUS_PATH "/Power", GSD_DBUS_BASE_INTERFACE ".Power", NULL, (GAsyncReadyCallback) power_ready_cb, manager); g_dbus_proxy_new (manager->priv->connection, G_DBUS_PROXY_FLAGS_NONE, NULL, GSD_DBUS_NAME ".Power", GSD_DBUS_PATH "/Power", GSD_DBUS_BASE_INTERFACE ".Power.Screen", NULL, (GAsyncReadyCallback) power_screen_ready_cb, manager); g_dbus_proxy_new (manager->priv->connection, G_DBUS_PROXY_FLAGS_NONE, NULL, GSD_DBUS_NAME ".Power", GSD_DBUS_PATH "/Power", GSD_DBUS_BASE_INTERFACE ".Power.Keyboard", NULL, (GAsyncReadyCallback) power_keyboard_ready_cb, manager); } static void register_manager (GsdMediaKeysManager *manager) { manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); manager->priv->bus_cancellable = g_cancellable_new (); g_assert (manager->priv->introspection_data != NULL); g_bus_get (G_BUS_TYPE_SESSION, manager->priv->bus_cancellable, (GAsyncReadyCallback) on_bus_gotten, manager); } GsdMediaKeysManager * gsd_media_keys_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_MEDIA_KEYS_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_MEDIA_KEYS_MANAGER (manager_object); } ./plugins/media-keys/README.media-keys-API0000644000004100000410000000345212735467744020321 0ustar www-datawww-dataThis is very simple documentation to gnome-settings-daemon's D-Bus API for media players. gnome-settings-daemon will send key press events from multimedia keys to applications that register their interest in those events. This allows the play/pause button to control an audio player that's not focused for example. The D-Bus API is described in gsd-media-keys-manager.c (look for introspection_xml), but a small explanation follows here. 1. Create yourself a proxy object for the remote interface: Object path: /org/gnome/SettingsDaemon/MediaKeys D-Bus name: org.gnome.SettingsDaemon.MediaKeys Interface name: org.gnome.SettingsDaemon.MediaKeys 2. Register your application with gnome-settings-daemon GrabMediaPlayerKeys ("my-application", 0) with the second argument being the current time (usually 0, or the time passed to you from an event, such as a mouse click) 3. Listen to the MediaPlayerKeyPressed() signal 4. When receiving a MediaPlayerKeyPressed() signal, check whether the first argument (application) matches the value you passed to GrabMediaPlayerKeys() and apply the action depending on the key (2nd argument) Possible values of key are: - Play - Pause - Stop - Previous - Next - Rewind - FastForward - Repeat - Shuffle 5. Every time your application is focused, you should call GrabMediaPlayerKeys() again, so that gnome-settings-daemon knows which one was last used. This allows switching between a movie player and a music player, for example, and have the buttons control the last used application. 6. When your application wants to stop using the functionality it can call ReleaseMediaPlayerKeys(). If your application does not call ReleaseMediaPlayerKeys() and releases its D-Bus connection then the application will be automatically removed from the list of applications held by gnome-settings-daemon. ./plugins/media-keys/media-keys.gnome-settings-plugin.in0000644000004100000410000000026112735467744023614 0ustar www-datawww-data[GNOME Settings Plugin] Module=media-keys IAge=0 # Default Priority # Priority=100 _Name=Media keys _Description=Media keys plugin Authors= Copyright=Copyright © 2007 Website= ./plugins/xsettings/0000755000004100000410000000000012735467763015000 5ustar www-datawww-data./plugins/xsettings/gsd-xsettings-manager.h0000644000004100000410000000466212735467744021373 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GNOME_XSETTINGS_MANAGER_H #define __GNOME_XSETTINGS_MANAGER_H #include G_BEGIN_DECLS #define GNOME_TYPE_XSETTINGS_MANAGER (gnome_xsettings_manager_get_type ()) #define GNOME_XSETTINGS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNOME_TYPE_XSETTINGS_MANAGER, GnomeXSettingsManager)) #define GNOME_XSETTINGS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNOME_TYPE_XSETTINGS_MANAGER, GnomeXSettingsManagerClass)) #define GNOME_IS_XSETTINGS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNOME_TYPE_XSETTINGS_MANAGER)) #define GNOME_IS_XSETTINGS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNOME_TYPE_XSETTINGS_MANAGER)) #define GNOME_XSETTINGS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNOME_TYPE_XSETTINGS_MANAGER, GnomeXSettingsManagerClass)) typedef struct GnomeXSettingsManagerPrivate GnomeXSettingsManagerPrivate; typedef struct { GObject parent; GnomeXSettingsManagerPrivate *priv; } GnomeXSettingsManager; typedef struct { GObjectClass parent_class; } GnomeXSettingsManagerClass; GType gnome_xsettings_manager_get_type (void); GnomeXSettingsManager * gnome_xsettings_manager_new (void); gboolean gnome_xsettings_manager_start (GnomeXSettingsManager *manager, GError **error); void gnome_xsettings_manager_stop (GnomeXSettingsManager *manager); G_END_DECLS #endif /* __GNOME_XSETTINGS_MANAGER_H */ ./plugins/xsettings/xsettings-manager.h0000644000004100000410000000524012735467744020611 0ustar www-datawww-data/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #ifndef XSETTINGS_MANAGER_H #define XSETTINGS_MANAGER_H #include #include "xsettings-common.h" typedef struct _XSettingsManager XSettingsManager; typedef void (*XSettingsTerminateFunc) (void *cb_data); Bool xsettings_manager_check_running (Display *display, int screen); XSettingsManager *xsettings_manager_new (Display *display, int screen, XSettingsTerminateFunc terminate, void *cb_data); void xsettings_manager_destroy (XSettingsManager *manager); void xsettings_manager_delete_setting (XSettingsManager *manager, const char *name); void xsettings_manager_set_int (XSettingsManager *manager, const char *name, int value); void xsettings_manager_set_string (XSettingsManager *manager, const char *name, const char *value); void xsettings_manager_set_color (XSettingsManager *manager, const char *name, XSettingsColor *value); void xsettings_manager_notify (XSettingsManager *manager); void xsettings_manager_set_overrides (XSettingsManager *manager, GVariant *overrides); #endif /* XSETTINGS_MANAGER_H */ ./plugins/xsettings/gsd-xsettings-gtk.h0000644000004100000410000000414112735467744020536 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 Bastien Nocera * * 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. * */ #ifndef __GSD_XSETTINGS_GTK_H__ #define __GSD_XSETTINGS_GTK_H__ #include #include #include G_BEGIN_DECLS #define GSD_TYPE_XSETTINGS_GTK (gsd_xsettings_gtk_get_type ()) #define GSD_XSETTINGS_GTK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_XSETTINGS_GTK, GsdXSettingsGtk)) #define GSD_XSETTINGS_GTK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_XSETTINGS_GTK, GsdXSettingsGtkClass)) #define GSD_IS_XSETTINGS_GTK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_XSETTINGS_GTK)) #define GSD_IS_XSETTINGS_GTK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_XSETTINGS_GTK)) #define GSD_XSETTINGS_GTK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_XSETTINGS_GTK, GsdXSettingsGtkClass)) typedef struct GsdXSettingsGtkPrivate GsdXSettingsGtkPrivate; typedef struct { GObject parent; GsdXSettingsGtkPrivate *priv; } GsdXSettingsGtk; typedef struct { GObjectClass parent_class; } GsdXSettingsGtkClass; GType gsd_xsettings_gtk_get_type (void) G_GNUC_CONST; GsdXSettingsGtk *gsd_xsettings_gtk_new (void); const char * gsd_xsettings_gtk_get_modules (GsdXSettingsGtk *gtk); G_END_DECLS #endif /* __GSD_XSETTINGS_GTK_H__ */ ./plugins/xsettings/wm-button-layout-translation.h0000644000004100000410000000170712735467744022760 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2014 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 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, see . * * Author: Florian Müllner */ #ifndef __WM_BUTTON_LAYOUT_TRANSLATION__ #define __WM_BUTTON_LAYOUT_TRANSLATION__ void translate_wm_button_layout_to_gtk (char *layout); #endif ./plugins/xsettings/wm-button-layout-translation.c0000644000004100000410000000434512735467744022754 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2014 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 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, see . * * Author: Florian Müllner */ #include #include #include #include "wm-button-layout-translation.h" static void translate_buttons (char *layout, int *len_p) { char *strp = layout, *button; int len = 0; if (!layout || !*layout) goto out; while ((button = strsep (&strp, ","))) { char *gtkbutton; if (strcmp (button, "menu") == 0) gtkbutton = "icon"; else if (strcmp (button, "appmenu") == 0) gtkbutton = "menu"; else if (strcmp (button, "minimize") == 0) gtkbutton = "minimize"; else if (strcmp (button, "maximize") == 0) gtkbutton = "maximize"; else if (strcmp (button, "close") == 0) gtkbutton = "close"; else continue; if (len) layout[len++] = ','; strcpy (layout + len, gtkbutton); len += strlen (gtkbutton); } layout[len] = '\0'; out: if (len_p) *len_p = len; } void translate_wm_button_layout_to_gtk (char *layout) { char *strp = layout, *left_buttons, *right_buttons; int left_len, right_len = 0; left_buttons = strsep (&strp, ":"); right_buttons = strp; translate_buttons (left_buttons, &left_len); memmove (layout, left_buttons, left_len); if (strp == NULL) goto out; /* no ":" in layout */ layout[left_len++] = ':'; translate_buttons (right_buttons, &right_len); memmove (layout + left_len, right_buttons, right_len); out: layout[left_len + right_len] = '\0'; } ./plugins/xsettings/Makefile.am0000644000004100000410000000576712735467744017052 0ustar www-datawww-dataNULL = plugin_name = xsettings noinst_PROGRAMS = test-gtk-modules test_gtk_modules_SOURCES = \ gsd-xsettings-gtk.c \ gsd-xsettings-gtk.h \ test-gtk-modules.c test_gtk_modules_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) test_gtk_modules_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(XSETTINGS_LIBS) \ $(SETTINGS_PLUGIN_LIBS) test_gtk_modules_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/data/ \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DGTK_MODULES_DIRECTORY=\""$(libdir)/unity-settings-daemon-@GSD_API_VERSION@/gtk-modules/"\" \ $(AM_CPPFLAGS) noinst_PROGRAMS += test-wm-button-layout-translations test_wm_button_layout_translations_SOURCES = \ test-wm-button-layout-translations.c \ wm-button-layout-translation.c \ wm-button-layout-translation.h \ $(NULL) test_wm_button_layout_translations_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(XSETTINGS_CFLAGS) \ $(AM_CFLAGS) \ $(NULL) test_wm_button_layout_translations_LDADD = \ $(XSETTINGS_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ $(NULL) libexec_PROGRAMS = usd-test-xsettings usd_test_xsettings_SOURCES = \ gsd-xsettings-gtk.c \ gsd-xsettings-gtk.h \ gsd-xsettings-manager.c \ gsd-xsettings-gtk.h \ xsettings-common.c \ xsettings-common.h \ xsettings-manager.c \ xsettings-manager.h \ fontconfig-monitor.c \ fontconfig-monitor.h \ wm-button-layout-translation.c \ wm-button-layout-translation.h \ test-xsettings.c usd_test_xsettings_CFLAGS = $(test_gtk_modules_CFLAGS) usd_test_xsettings_CPPFLAGS = $(test_gtk_modules_CPPFLAGS) -I$(top_srcdir)/plugins/common usd_test_xsettings_LDADD = $(test_gtk_modules_LDADD) plugin_LTLIBRARIES = \ libxsettings.la \ $(NULL) libxsettings_la_SOURCES = \ gsd-xsettings-plugin.c \ gsd-xsettings-manager.h \ gsd-xsettings-manager.c \ gsd-xsettings-gtk.c \ gsd-xsettings-gtk.h \ xsettings-common.h \ xsettings-common.c \ xsettings-manager.h \ xsettings-manager.c \ fontconfig-monitor.h \ fontconfig-monitor.c \ wm-button-layout-translation.c \ wm-button-layout-translation.h \ $(NULL) libxsettings_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/data/ \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DGTK_MODULES_DIRECTORY=\""$(libdir)/unity-settings-daemon-@GSD_API_VERSION@/gtk-modules/"\" \ $(AM_CPPFLAGS) libxsettings_la_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(XSETTINGS_CFLAGS) \ $(AM_CFLAGS) libxsettings_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) \ $(NULL) libxsettings_la_LIBADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(SETTINGS_PLUGIN_LIBS) \ $(XSETTINGS_LIBS) \ $(NULL) plugin_in_files = \ xsettings.gnome-settings-plugin.in \ $(NULL) plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ README.xsettings \ $(plugin_in_files) \ $(NULL) CLEANFILES = \ $(plugin_DATA) \ $(NULL) DISTCLEANFILES = \ $(plugin_DATA) \ $(NULL) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/xsettings/fontconfig-monitor.h0000644000004100000410000000254112735467744020773 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * * Author: Behdad Esfahbod, Red Hat, Inc. */ #ifndef __FONTCONFIG_MONITOR_H #define __FONTCONFIG_MONITOR_H #include G_BEGIN_DECLS void fontconfig_cache_init (void); gboolean fontconfig_cache_update (void); typedef struct _fontconfig_monitor_handle fontconfig_monitor_handle_t; fontconfig_monitor_handle_t * fontconfig_monitor_start (GFunc notify_callback, gpointer notify_data); void fontconfig_monitor_stop (fontconfig_monitor_handle_t *handle); G_END_DECLS #endif /* __FONTCONFIG_MONITOR_H */ ./plugins/xsettings/xsettings-common.h0000644000004100000410000000442712735467744020475 0ustar www-datawww-data/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #ifndef XSETTINGS_COMMON_H #define XSETTINGS_COMMON_H #include #define XSETTINGS_N_TIERS 2 typedef struct _XSettingsColor XSettingsColor; typedef struct _XSettingsSetting XSettingsSetting; /* Types of settings possible. Enum values correspond to * protocol values. */ typedef enum { XSETTINGS_TYPE_INT = 0, XSETTINGS_TYPE_STRING = 1, XSETTINGS_TYPE_COLOR = 2 } XSettingsType; struct _XSettingsColor { unsigned short red, green, blue, alpha; }; struct _XSettingsSetting { char *name; GVariant *value[XSETTINGS_N_TIERS]; unsigned long last_change_serial; }; XSettingsSetting *xsettings_setting_new (const gchar *name); GVariant * xsettings_setting_get (XSettingsSetting *setting); void xsettings_setting_set (XSettingsSetting *setting, gint tier, GVariant *value, guint32 serial); void xsettings_setting_free (XSettingsSetting *setting); char xsettings_byte_order (void); #endif /* XSETTINGS_COMMON_H */ ./plugins/xsettings/test-gtk-modules.c0000644000004100000410000000124112735467744020351 0ustar www-datawww-data #include "gsd-xsettings-gtk.h" static void gtk_modules_callback (GsdXSettingsGtk *gtk, GParamSpec *spec, gpointer user_data) { const char *modules; modules = gsd_xsettings_gtk_get_modules (gtk); g_message ("GTK+ modules list changed to: %s", modules ? modules : "(empty)"); } int main (int argc, char **argv) { GMainLoop *loop; GsdXSettingsGtk *gtk; gtk = gsd_xsettings_gtk_new (); g_signal_connect (G_OBJECT (gtk), "notify::gtk-modules", G_CALLBACK (gtk_modules_callback), NULL); gtk_modules_callback (gtk, NULL, NULL); loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (loop); return 0; } ./plugins/xsettings/xsettings.gnome-settings-plugin.in0000644000004100000410000000027612735467744023622 0ustar www-datawww-data[GNOME Settings Plugin] Module=xsettings IAge=0 Priority=2 _Name=X Settings _Description=Manage X Settings Authors=William Jon McCann Copyright=Copyright © 2007 William Jon McCann Website= ./plugins/xsettings/test-wm-button-layout-translations.c0000644000004100000410000000255112735467744024111 0ustar www-datawww-data#include #include "wm-button-layout-translation.h" static void test_button_layout_translations (void) { static struct { char *layout; char *expected; } tests[] = { { "", "" }, { "invalid", "" }, { ":", ":" }, { ":invalid", ":" }, { "invalid:", ":" }, { "invalid:invalid", ":" }, { "appmenu", "menu" }, { "appmenu:", "menu:" }, { ":menu", ":icon" }, { "appmenu:close", "menu:close" }, { "appmenu:minimize,maximize,close", "menu:minimize,maximize,close" }, { "menu,appmenu:minimize,maximize,close", "icon,menu:minimize,maximize,close" }, { "close,close,close:close,close,close", "close,close,close:close,close,close" }, { "invalid,appmenu:invalid,minimize", "menu:minimize" }, { "appmenu,invalid:minimize,invalid", "menu:minimize" }, { "invalidmenu:invalidclose", ":" }, { "invalid,invalid,invalid:invalid,minimize,maximize,close", ":minimize,maximize,close" }, }; int i; for (i = 0; i < G_N_ELEMENTS (tests); i++) { char *layout = g_strdup (tests[i].layout); translate_wm_button_layout_to_gtk (layout); g_assert_cmpstr (layout, ==, tests[i].expected); g_free (layout); } } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/layout-translations", test_button_layout_translations); return g_test_run (); } ./plugins/xsettings/fontconfig-monitor.c0000644000004100000410000001125612735467744020771 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. * * Author: Behdad Esfahbod, Red Hat, Inc. */ #include "fontconfig-monitor.h" #include #include #define TIMEOUT_SECONDS 2 static void stuff_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer handle); void fontconfig_cache_init (void) { FcInit (); } gboolean fontconfig_cache_update (void) { return !FcConfigUptoDate (NULL) && FcInitReinitialize (); } static void monitor_files (GPtrArray *monitors, FcStrList *list, gpointer data) { const char *str; while ((str = (const char *) FcStrListNext (list))) { GFile *file; GFileMonitor *monitor; file = g_file_new_for_path (str); monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL); g_object_unref (file); if (!monitor) continue; g_signal_connect (monitor, "changed", G_CALLBACK (stuff_changed), data); g_ptr_array_add (monitors, monitor); } FcStrListDone (list); } struct _fontconfig_monitor_handle { GPtrArray *monitors; guint timeout; GFunc notify_callback; gpointer notify_data; }; static GPtrArray * monitors_create (gpointer data) { GPtrArray *monitors = g_ptr_array_new (); monitor_files (monitors, FcConfigGetConfigFiles (NULL), data); monitor_files (monitors, FcConfigGetFontDirs (NULL) , data); return monitors; } static void monitors_free (GPtrArray *monitors) { if (!monitors) return; g_ptr_array_foreach (monitors, (GFunc) g_object_unref, NULL); g_ptr_array_free (monitors, TRUE); } static gboolean update (gpointer data) { fontconfig_monitor_handle_t *handle = data; gboolean notify = FALSE; handle->timeout = 0; if (fontconfig_cache_update ()) { notify = TRUE; monitors_free (handle->monitors); handle->monitors = monitors_create (data); } /* we finish modifying handle before calling the notify callback, * allowing the callback to free the monitor if it decides to. */ if (notify && handle->notify_callback) handle->notify_callback (data, handle->notify_data); return FALSE; } static void stuff_changed (GFileMonitor *monitor G_GNUC_UNUSED, GFile *file G_GNUC_UNUSED, GFile *other_file G_GNUC_UNUSED, GFileMonitorEvent event_type G_GNUC_UNUSED, gpointer data) { fontconfig_monitor_handle_t *handle = data; /* wait for quiescence */ if (handle->timeout) g_source_remove (handle->timeout); handle->timeout = g_timeout_add_seconds (TIMEOUT_SECONDS, update, data); } fontconfig_monitor_handle_t * fontconfig_monitor_start (GFunc notify_callback, gpointer notify_data) { fontconfig_monitor_handle_t *handle = g_slice_new0 (fontconfig_monitor_handle_t); handle->notify_callback = notify_callback; handle->notify_data = notify_data; handle->monitors = monitors_create (handle); return handle; } void fontconfig_monitor_stop (fontconfig_monitor_handle_t *handle) { if (handle->timeout) g_source_remove (handle->timeout); handle->timeout = 0; monitors_free (handle->monitors); handle->monitors = NULL; } #ifdef FONTCONFIG_MONITOR_TEST static void yay (void) { g_message ("yay"); } int main (void) { GMainLoop *loop; fontconfig_monitor_start ((GFunc) yay, NULL); loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (loop); return 0; } #endif ./plugins/xsettings/gsd-xsettings-manager.c0000644000004100000410000013502512735467763021365 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 Rodrigo Moya * Copyright (C) 2007 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-profile.h" #include "gsd-enums.h" #include "gsd-xsettings-manager.h" #include "gsd-xsettings-gtk.h" #include "xsettings-manager.h" #include "fontconfig-monitor.h" #include "wm-button-layout-translation.h" #define GNOME_XSETTINGS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNOME_TYPE_XSETTINGS_MANAGER, GnomeXSettingsManagerPrivate)) #define MOUSE_SETTINGS_SCHEMA "org.gnome.settings-daemon.peripherals.mouse" #define INTERFACE_SETTINGS_SCHEMA "org.gnome.desktop.interface" #define SOUND_SETTINGS_SCHEMA "org.gnome.desktop.sound" #define PRIVACY_SETTINGS_SCHEMA "org.gnome.desktop.privacy" #define WM_SETTINGS_SCHEMA "org.gnome.desktop.wm.preferences" #define XSETTINGS_PLUGIN_SCHEMA "org.gnome.settings-daemon.plugins.xsettings" #define XSETTINGS_OVERRIDE_KEY "overrides" #define GTK_MODULES_DISABLED_KEY "disabled-gtk-modules" #define GTK_MODULES_ENABLED_KEY "enabled-gtk-modules" #define TEXT_SCALING_FACTOR_KEY "text-scaling-factor" #define SCALING_FACTOR_KEY "scaling-factor" #define CURSOR_SIZE_KEY "cursor-size" #define CURSOR_THEME_KEY "cursor-theme" #define FONT_ANTIALIASING_KEY "antialiasing" #define FONT_HINTING_KEY "hinting" #define FONT_RGBA_ORDER_KEY "rgba-order" /* As we cannot rely on the X server giving us good DPI information, and * that we don't want multi-monitor screens to have different DPIs (thus * different text sizes), we'll hard-code the value of the DPI * * See also: * https://bugzilla.novell.com/show_bug.cgi?id=217790• * https://bugzilla.gnome.org/show_bug.cgi?id=643704 * * http://lists.fedoraproject.org/pipermail/devel/2011-October/157671.html * Why EDID is not trustworthy for DPI * Adam Jackson ajax at redhat.com * Tue Oct 4 17:54:57 UTC 2011 * * Previous message: GNOME 3 - font point sizes now scaled? * Next message: Why EDID is not trustworthy for DPI * Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] * * On Tue, 2011-10-04 at 11:46 -0400, Kaleb S. KEITHLEY wrote: * * > Grovelling around in the F15 xorg-server sources and reviewing the Xorg * > log file on my F15 box, I see, with _modern hardware_ at least, that we * > do have the monitor geometry available from DDC or EDIC, and obviously * > it is trivial to compute the actual, correct DPI for each screen. * * I am clearly going to have to explain this one more time, forever. * Let's see if I can't write it authoritatively once and simply answer * with a URL from here out. (As always, use of the second person "you" * herein is plural, not singular.) * * EDID does not reliably give you the size of the display. * * Base EDID has at least two different places where you can give a * physical size (before considering extensions that aren't widely deployed * so whatever). The first is a global property, measured in centimeters, * of the physical size of the glass. The second is attached to your (zero * or more) detailed timing specifications, and reflects the size of the * mode, in millimeters. * * So, how does this screw you? * * a) Glass size is too coarse. On a large display that cm roundoff isn't * a big deal, but on subnotebooks it's a different game. The 11" MBA is * 25.68x14.44 cm, so that gives you a range of 52.54-54.64 dpcm horizontal * and 51.20-54.86 dpcm vertical (133.4-138.8 dpi h and 130.0-139.3 dpi v). * Which is optimistic, because that's doing the math forward from knowing * the actual size, and you as the EDID parser can't know which way the * manufacturer rounded. * * b) Glass size need not be non-zero. This is in fact the usual case for * projectors, which don't have a fixed display size since it's a function * of how far away the wall is from the lens. * * c) Glass size could be partially non-zero. Yes, really. EDID 1.4 * defines a method of using these two bytes to encode aspect ratio, where * if vertical size is 0 then the aspect ratio is computed as (horizontal * value + 99) / 100 in portrait mode (and the obvious reverse thing if * horizontal is zero). Admittedly, unlike every other item in this list, * I've never seen this in the wild. But it's legal. * * d) Glass size could be a direct encoding of the aspect ratio. Base EDID * doesn't condone this behaviour, but the CEA spec (to which all HDMI * monitors must conform) does allow-but-not-require it, which means your * 1920x1080 TV could claim to be 16 "cm" by 9 "cm". So of course that's * what TV manufacturers do because that way they don't have to modify the * EDID info when physical construction changes, and that's cheaper. * * e) You could use mode size to get size in millimeters, but you might not * have any detailed timings. * * f) You could use mode size, but mode size is explicitly _not_ glass * size. It's the size that the display chooses to present that mode. * Sometimes those are the same, and sometimes they're not. You could be * scaled or {letter,pillar}boxed, and that's not necessarily something you * can control from the host side. * * g) You could use mode size, but it could be an encoded aspect ratio, as * in case d above, because CEA says that's okay. * * h) You could use mode size, but it could be the aspect ratio from case d * multiplied by 10 in each direction (because, of course, you gave size in * centimeters and so your authoring tool just multiplied it up). * * i) Any or all of the above could be complete and utter garbage, because * - and I really, really need you to understand this - there is no * requirements program for any commercial OS or industry standard that * requires honesty here, as far as I'm aware. There is every incentive * for there to _never_ be one, because it would make the manufacturing * process more expensive. * * So from this point the suggestion is usually "well come up with some * heuristic to make a good guess assuming there's some correlation between * the various numbers you're given". I have in fact written heuristics * for this, and they're in your kernel and your X server, and they still * encounter a huge number of cases where we simply _cannot_ know from EDID * anything like a physical size, because - to pick only one example - the * consumer electronics industry are cheap bastards, because you the * consumer demanded that they be cheap. * * And then your only recourse is to an external database, and now you're * up the creek again because the identifying information here is a * vendor/model/serial tuple, and the vendor can and does change physical * construction without changing model number. Now you get to play the * guessing game of how big the serial number range is for each subvariant, * assuming they bothered to encode a serial number - and they didn't. Or, * if they bothered to encode week/year of manufacturer correctly - and * they didn't - which weeks meant which models. And then you still have * to go out and buy one of every TV at Fry's, and that covers you for one * market, for three months. * * If someone wants to write something better, please, by all means. If * it's kernel code, send it to dri-devel at lists.freedesktop.org and cc me * and I will happily review it. Likewise xorg-devel@ for X server * changes. * * I gently suggest that doing so is a waste of time. * * But if there's one thing free software has taught me, it's that you can * not tell people something is a bad idea and have any expectation they * will believe you. * * > Obviously in a multi-screen set-up using Xinerama this has the potential * > to be a Hard Problem if the monitors differ greatly in their DPI. * > * > If the major resistance is over what to do with older hardware that * > doesn't have this data available, then yes, punt; use a hard-coded * > default. Likewise, if the two monitors really differ greatly, then punt. * * I'm going to limit myself to observing that "greatly" is a matter of * opinion, and that in order to be really useful you'd need some way of * communicating "I punted" to the desktop. * * Beyond that, sure, pick a heuristic, accept that it's going to be * insufficient for someone, and then sit back and wait to get * second-guessed on it over and over. * * > And it wouldn't be so hard to to add something like -dpi:0, -dpi:1, * > -dpi:2 command line options to specify per-screen dpi. I kinda thought I * > did that a long, long time ago, but maybe I only thought about doing it * > and never actually got around to it. * * The RANDR extension as of version 1.2 does allow you to override * physical size on a per-output basis at runtime. We even try pretty hard * to set them as honestly as we can up front. The 96dpi thing people * complain about is from the per-screen info, which is simply a default * because of all the tl;dr above; because you have N outputs per screen * which means a single number is in general useless; and because there is * no way to refresh the per-screen info at runtime, as it's only ever sent * in the initial connection handshake. * * - ajax * */ #define DPI_FALLBACK 96 #define HIDPI_LIMIT (DPI_FALLBACK * 2) typedef struct _TranslationEntry TranslationEntry; typedef void (* TranslationFunc) (GnomeXSettingsManager *manager, TranslationEntry *trans, GVariant *value); struct _TranslationEntry { const char *gsettings_schema; const char *gsettings_key; const char *xsetting_name; TranslationFunc translate; }; struct GnomeXSettingsManagerPrivate { guint start_idle_id; XSettingsManager **managers; GHashTable *settings; GSettings *plugin_settings; fontconfig_monitor_handle_t *fontconfig_handle; GsdXSettingsGtk *gtk; guint shell_name_watch_id; guint unity_name_watch_id; gboolean have_shell; gboolean have_unity; guint notify_idle_id; }; #define GSD_XSETTINGS_ERROR gsd_xsettings_error_quark () enum { GSD_XSETTINGS_ERROR_INIT }; static void gnome_xsettings_manager_class_init (GnomeXSettingsManagerClass *klass); static void gnome_xsettings_manager_init (GnomeXSettingsManager *xsettings_manager); static void gnome_xsettings_manager_finalize (GObject *object); G_DEFINE_TYPE (GnomeXSettingsManager, gnome_xsettings_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static GQuark gsd_xsettings_error_quark (void) { return g_quark_from_static_string ("gsd-xsettings-error-quark"); } static void translate_bool_int (GnomeXSettingsManager *manager, TranslationEntry *trans, GVariant *value) { int i; for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name, g_variant_get_boolean (value)); } } static void translate_int_int (GnomeXSettingsManager *manager, TranslationEntry *trans, GVariant *value) { int i; for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_int (manager->priv->managers [i], trans->xsetting_name, g_variant_get_int32 (value)); } } static void translate_string_string (GnomeXSettingsManager *manager, TranslationEntry *trans, GVariant *value) { int i; for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_string (manager->priv->managers [i], trans->xsetting_name, g_variant_get_string (value, NULL)); } } static void translate_string_string_toolbar (GnomeXSettingsManager *manager, TranslationEntry *trans, GVariant *value) { int i; const char *tmp; /* This is kind of a workaround since GNOME expects the key value to be * "both_horiz" and gtk+ wants the XSetting to be "both-horiz". */ tmp = g_variant_get_string (value, NULL); if (tmp && strcmp (tmp, "both_horiz") == 0) { tmp = "both-horiz"; } for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_string (manager->priv->managers [i], trans->xsetting_name, tmp); } } static void translate_button_layout (GnomeXSettingsManager *manager, TranslationEntry *trans, GVariant *value) { char *layout = g_variant_dup_string (value, NULL); int i; translate_wm_button_layout_to_gtk (layout); for (i = 0; manager->priv->managers [i]; i++) xsettings_manager_set_string (manager->priv->managers [i], trans->xsetting_name, layout); g_free (layout); } static TranslationEntry translations [] = { { "org.gnome.settings-daemon.peripherals.mouse", "double-click", "Net/DoubleClickTime", translate_int_int }, { "org.gnome.settings-daemon.peripherals.mouse", "drag-threshold", "Net/DndDragThreshold", translate_int_int }, { "org.gnome.desktop.interface", "gtk-color-palette", "Gtk/ColorPalette", translate_string_string }, { "org.gnome.desktop.interface", "font-name", "Gtk/FontName", translate_string_string }, { "org.gnome.desktop.interface", "gtk-key-theme", "Gtk/KeyThemeName", translate_string_string }, { "org.gnome.desktop.interface", "toolbar-style", "Gtk/ToolbarStyle", translate_string_string_toolbar }, { "org.gnome.desktop.interface", "toolbar-icons-size", "Gtk/ToolbarIconSize", translate_string_string }, { "org.gnome.desktop.interface", "can-change-accels", "Gtk/CanChangeAccels", translate_bool_int }, { "org.gnome.desktop.interface", "cursor-blink", "Net/CursorBlink", translate_bool_int }, { "org.gnome.desktop.interface", "cursor-blink-time", "Net/CursorBlinkTime", translate_int_int }, { "org.gnome.desktop.interface", "cursor-blink-timeout", "Gtk/CursorBlinkTimeout", translate_int_int }, { "org.gnome.desktop.interface", "gtk-theme", "Net/ThemeName", translate_string_string }, { "org.gnome.desktop.interface", "gtk-timeout-initial", "Gtk/TimeoutInitial", translate_int_int }, { "org.gnome.desktop.interface", "gtk-timeout-repeat", "Gtk/TimeoutRepeat", translate_int_int }, { "org.gnome.desktop.interface", "gtk-color-scheme", "Gtk/ColorScheme", translate_string_string }, { "org.gnome.desktop.interface", "gtk-im-preedit-style", "Gtk/IMPreeditStyle", translate_string_string }, { "org.gnome.desktop.interface", "gtk-im-status-style", "Gtk/IMStatusStyle", translate_string_string }, { "org.gnome.desktop.interface", "gtk-im-module", "Gtk/IMModule", translate_string_string }, { "org.gnome.desktop.interface", "icon-theme", "Net/IconThemeName", translate_string_string }, { "org.gnome.desktop.interface", "menus-have-icons", "Gtk/MenuImages", translate_bool_int }, { "org.gnome.desktop.interface", "buttons-have-icons", "Gtk/ButtonImages", translate_bool_int }, { "org.gnome.desktop.interface", "menubar-accel", "Gtk/MenuBarAccel", translate_string_string }, { "org.gnome.desktop.interface", "enable-animations", "Gtk/EnableAnimations", translate_bool_int }, { "org.gnome.desktop.interface", "cursor-theme", "Gtk/CursorThemeName", translate_string_string }, /* cursor-size is handled via the Xft side as it needs the scaling factor */ { "org.gnome.desktop.interface", "show-input-method-menu", "Gtk/ShowInputMethodMenu", translate_bool_int }, { "org.gnome.desktop.interface", "show-unicode-menu", "Gtk/ShowUnicodeMenu", translate_bool_int }, { "org.gnome.desktop.interface", "automatic-mnemonics", "Gtk/AutoMnemonics", translate_bool_int }, { "org.gnome.desktop.sound", "theme-name", "Net/SoundThemeName", translate_string_string }, { "org.gnome.desktop.sound", "event-sounds", "Net/EnableEventSounds" , translate_bool_int }, { "org.gnome.desktop.sound", "input-feedback-sounds", "Net/EnableInputFeedbackSounds", translate_bool_int }, { "org.gnome.desktop.privacy", "recent-files-max-age", "Gtk/RecentFilesMaxAge", translate_int_int }, { "org.gnome.desktop.privacy", "remember-recent-files", "Gtk/RecentFilesEnabled", translate_bool_int }, { "org.gnome.desktop.wm.preferences", "button-layout", "Gtk/DecorationLayout", translate_button_layout } }; static gboolean notify_idle (gpointer data) { GnomeXSettingsManager *manager = data; gint i; for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_notify (manager->priv->managers[i]); } manager->priv->notify_idle_id = 0; return G_SOURCE_REMOVE; } static void queue_notify (GnomeXSettingsManager *manager) { if (manager->priv->notify_idle_id != 0) return; manager->priv->notify_idle_id = g_idle_add (notify_idle, manager); } static double get_dpi_from_gsettings (GnomeXSettingsManager *manager) { GSettings *interface_settings; double dpi; double factor; interface_settings = g_hash_table_lookup (manager->priv->settings, INTERFACE_SETTINGS_SCHEMA); factor = g_settings_get_double (interface_settings, TEXT_SCALING_FACTOR_KEY); dpi = DPI_FALLBACK; return dpi * factor; } static gboolean in_desktop (const gchar *name) { const gchar *desktop_name_list; gchar **names; gboolean in_list = FALSE; gint i; desktop_name_list = g_getenv ("XDG_CURRENT_DESKTOP"); if (!desktop_name_list) return FALSE; names = g_strsplit (desktop_name_list, ":", -1); for (i = 0; names[i] && !in_list; i++) if (strcmp (names[i], name) == 0) { in_list = TRUE; break; } g_strfreev (names); return in_list; } static int get_window_scale (GnomeXSettingsManager *manager) { GSettings *interface_settings; int window_scale; GdkRectangle rect; GdkDisplay *display; GdkScreen *screen; int width_mm, height_mm; int monitor_scale; double dpi_x, dpi_y; interface_settings = g_hash_table_lookup (manager->priv->settings, INTERFACE_SETTINGS_SCHEMA); window_scale = g_settings_get_uint (interface_settings, SCALING_FACTOR_KEY); if (window_scale == 0) { window_scale = 1; /* Under Unity let the shell handle the scaling */ if (in_desktop ("Unity")) goto out; display = gdk_display_get_default (); screen = gdk_display_get_default_screen (display); gdk_screen_get_monitor_geometry (screen, 0, &rect); width_mm = gdk_screen_get_monitor_width_mm (screen, 0); height_mm = gdk_screen_get_monitor_height_mm (screen, 0); monitor_scale = gdk_screen_get_monitor_scale_factor (screen, 0); /* Somebody encoded the aspect ratio (16/9 or 16/10) * instead of the physical size */ if ((width_mm == 160 && height_mm == 90) || (width_mm == 160 && height_mm == 100) || (width_mm == 16 && height_mm == 9) || (width_mm == 16 && height_mm == 10)) goto out; if (width_mm > 0 && height_mm > 0) { dpi_x = (double)rect.width * monitor_scale / (width_mm / 25.4); dpi_y = (double)rect.height * monitor_scale / (height_mm / 25.4); /* We don't completely trust these values so both must be high, and never pick higher ratio than 2 automatically */ if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT) window_scale = 2; } } out: return window_scale; } typedef struct { gboolean antialias; gboolean hinting; int scaled_dpi; int dpi; int window_scale; int cursor_size; gchar *cursor_theme; const char *rgba; const char *hintstyle; } GnomeXftSettings; /* Read GSettings and determine the appropriate Xft settings based on them. */ static void xft_settings_get (GnomeXSettingsManager *manager, GnomeXftSettings *settings) { GSettings *interface_settings; GsdFontAntialiasingMode antialiasing; GsdFontHinting hinting; GsdFontRgbaOrder order; gboolean use_rgba = FALSE; double dpi; int cursor_size; interface_settings = g_hash_table_lookup (manager->priv->settings, INTERFACE_SETTINGS_SCHEMA); antialiasing = g_settings_get_enum (manager->priv->plugin_settings, FONT_ANTIALIASING_KEY); hinting = g_settings_get_enum (manager->priv->plugin_settings, FONT_HINTING_KEY); order = g_settings_get_enum (manager->priv->plugin_settings, FONT_RGBA_ORDER_KEY); settings->antialias = (antialiasing != GSD_FONT_ANTIALIASING_MODE_NONE); settings->hinting = (hinting != GSD_FONT_HINTING_NONE); settings->window_scale = get_window_scale (manager); dpi = get_dpi_from_gsettings (manager); settings->dpi = dpi * 1024; /* Xft wants 1/1024ths of an inch */ settings->scaled_dpi = dpi * settings->window_scale * 1024; cursor_size = g_settings_get_int (interface_settings, CURSOR_SIZE_KEY); settings->cursor_size = cursor_size * settings->window_scale; settings->cursor_theme = g_settings_get_string (interface_settings, CURSOR_THEME_KEY); settings->rgba = "rgb"; settings->hintstyle = "hintfull"; switch (hinting) { case GSD_FONT_HINTING_NONE: settings->hintstyle = "hintnone"; break; case GSD_FONT_HINTING_SLIGHT: settings->hintstyle = "hintslight"; break; case GSD_FONT_HINTING_MEDIUM: settings->hintstyle = "hintmedium"; break; case GSD_FONT_HINTING_FULL: settings->hintstyle = "hintfull"; break; } switch (order) { case GSD_FONT_RGBA_ORDER_RGBA: settings->rgba = "rgba"; break; case GSD_FONT_RGBA_ORDER_RGB: settings->rgba = "rgb"; break; case GSD_FONT_RGBA_ORDER_BGR: settings->rgba = "bgr"; break; case GSD_FONT_RGBA_ORDER_VRGB: settings->rgba = "vrgb"; break; case GSD_FONT_RGBA_ORDER_VBGR: settings->rgba = "vbgr"; break; } switch (antialiasing) { case GSD_FONT_ANTIALIASING_MODE_NONE: settings->antialias = 0; break; case GSD_FONT_ANTIALIASING_MODE_GRAYSCALE: settings->antialias = 1; break; case GSD_FONT_ANTIALIASING_MODE_RGBA: settings->antialias = 1; use_rgba = TRUE; } if (!use_rgba) { settings->rgba = "none"; } } static void xft_settings_clear (GnomeXftSettings *settings) { g_free (settings->cursor_theme); } static void xft_settings_set_xsettings (GnomeXSettingsManager *manager, GnomeXftSettings *settings) { int i; gnome_settings_profile_start (NULL); for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_int (manager->priv->managers [i], "Xft/Antialias", settings->antialias); xsettings_manager_set_int (manager->priv->managers [i], "Xft/Hinting", settings->hinting); xsettings_manager_set_string (manager->priv->managers [i], "Xft/HintStyle", settings->hintstyle); xsettings_manager_set_int (manager->priv->managers [i], "Gdk/WindowScalingFactor", settings->window_scale); xsettings_manager_set_int (manager->priv->managers [i], "Gdk/UnscaledDPI", settings->dpi); xsettings_manager_set_int (manager->priv->managers [i], "Xft/DPI", settings->scaled_dpi); xsettings_manager_set_string (manager->priv->managers [i], "Xft/RGBA", settings->rgba); xsettings_manager_set_int (manager->priv->managers [i], "Gtk/CursorThemeSize", settings->cursor_size); xsettings_manager_set_string (manager->priv->managers [i], "Gtk/CursorThemeName", settings->cursor_theme); } gnome_settings_profile_end (NULL); } static void update_property (GString *props, const gchar* key, const gchar* value) { gchar* needle; size_t needle_len; gchar* found = NULL; /* update an existing property */ needle = g_strconcat (key, ":", NULL); needle_len = strlen (needle); if (g_str_has_prefix (props->str, needle)) found = props->str; else found = strstr (props->str, needle); if (found) { size_t value_index; gchar* end; end = strchr (found, '\n'); value_index = (found - props->str) + needle_len + 1; g_string_erase (props, value_index, end ? (end - found - needle_len) : -1); g_string_insert (props, value_index, "\n"); g_string_insert (props, value_index, value); } else { g_string_append_printf (props, "%s:\t%s\n", key, value); } g_free (needle); } static void xft_settings_set_xresources (GnomeXftSettings *settings) { GString *add_string; char dpibuf[G_ASCII_DTOSTR_BUF_SIZE]; Display *dpy; gnome_settings_profile_start (NULL); /* get existing properties */ dpy = XOpenDisplay (NULL); g_return_if_fail (dpy != NULL); add_string = g_string_new (XResourceManagerString (dpy)); g_debug("xft_settings_set_xresources: orig res '%s'", add_string->str); update_property (add_string, "Xft.dpi", g_ascii_dtostr (dpibuf, sizeof (dpibuf), (double) settings->scaled_dpi / 1024.0)); update_property (add_string, "Xft.antialias", settings->antialias ? "1" : "0"); update_property (add_string, "Xft.hinting", settings->hinting ? "1" : "0"); update_property (add_string, "Xft.hintstyle", settings->hintstyle); update_property (add_string, "Xft.rgba", settings->rgba); update_property (add_string, "Xcursor.size", g_ascii_dtostr (dpibuf, sizeof (dpibuf), (double) settings->cursor_size)); update_property (add_string, "Xcursor.theme", settings->cursor_theme); g_debug("xft_settings_set_xresources: new res '%s'", add_string->str); /* Set the new X property */ XChangeProperty(dpy, RootWindow (dpy, 0), XA_RESOURCE_MANAGER, XA_STRING, 8, PropModeReplace, (const unsigned char *) add_string->str, add_string->len); XCloseDisplay (dpy); g_string_free (add_string, TRUE); gnome_settings_profile_end (NULL); } /* We mirror the Xft properties both through XSETTINGS and through * X resources */ static void update_xft_settings (GnomeXSettingsManager *manager) { GnomeXftSettings settings; gnome_settings_profile_start (NULL); xft_settings_get (manager, &settings); xft_settings_set_xsettings (manager, &settings); xft_settings_set_xresources (&settings); xft_settings_clear (&settings); gnome_settings_profile_end (NULL); } static void xft_callback (GSettings *settings, const gchar *key, GnomeXSettingsManager *manager) { update_xft_settings (manager); queue_notify (manager); } static void override_callback (GSettings *settings, const gchar *key, GnomeXSettingsManager *manager) { GVariant *value; int i; value = g_settings_get_value (settings, XSETTINGS_OVERRIDE_KEY); for (i = 0; manager->priv->managers[i]; i++) { xsettings_manager_set_overrides (manager->priv->managers[i], value); } queue_notify (manager); g_variant_unref (value); } static void plugin_callback (GSettings *settings, const char *key, GnomeXSettingsManager *manager) { if (g_str_equal (key, GTK_MODULES_DISABLED_KEY) || g_str_equal (key, GTK_MODULES_ENABLED_KEY)) { /* Do nothing, as GsdXsettingsGtk will handle it */ } else if (g_str_equal (key, XSETTINGS_OVERRIDE_KEY)) { override_callback (settings, key, manager); } else { xft_callback (settings, key, manager); } } static void gtk_modules_callback (GsdXSettingsGtk *gtk, GParamSpec *spec, GnomeXSettingsManager *manager) { const char *modules = gsd_xsettings_gtk_get_modules (manager->priv->gtk); int i; if (modules == NULL) { for (i = 0; manager->priv->managers [i]; ++i) { xsettings_manager_delete_setting (manager->priv->managers [i], "Gtk/Modules"); } } else { g_debug ("Setting GTK modules '%s'", modules); for (i = 0; manager->priv->managers [i]; ++i) { xsettings_manager_set_string (manager->priv->managers [i], "Gtk/Modules", modules); } } queue_notify (manager); } static void fontconfig_callback (fontconfig_monitor_handle_t *handle, GnomeXSettingsManager *manager) { int i; int timestamp = time (NULL); gnome_settings_profile_start (NULL); for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_int (manager->priv->managers [i], "Fontconfig/Timestamp", timestamp); } queue_notify (manager); gnome_settings_profile_end (NULL); } static gboolean start_fontconfig_monitor_idle_cb (GnomeXSettingsManager *manager) { gnome_settings_profile_start (NULL); manager->priv->fontconfig_handle = fontconfig_monitor_start ((GFunc) fontconfig_callback, manager); gnome_settings_profile_end (NULL); manager->priv->start_idle_id = 0; return FALSE; } static void start_fontconfig_monitor (GnomeXSettingsManager *manager) { gnome_settings_profile_start (NULL); fontconfig_cache_init (); manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_fontconfig_monitor_idle_cb, manager); gnome_settings_profile_end (NULL); } static void stop_fontconfig_monitor (GnomeXSettingsManager *manager) { if (manager->priv->fontconfig_handle) { fontconfig_monitor_stop (manager->priv->fontconfig_handle); manager->priv->fontconfig_handle = NULL; } } static void notify_have_shell (GnomeXSettingsManager *manager) { int i; gnome_settings_profile_start (NULL); for (i = 0; manager->priv->managers [i]; i++) { /* Shell is showing appmenu if either GNOME Shell or Unity is running. */ xsettings_manager_set_int (manager->priv->managers [i], "Gtk/ShellShowsAppMenu", manager->priv->have_shell || manager->priv->have_unity); /* Shell is showing menubar *only* if Unity runs */ xsettings_manager_set_int (manager->priv->managers [i], "Gtk/ShellShowsMenubar", manager->priv->have_unity); } queue_notify (manager); gnome_settings_profile_end (NULL); } static void on_shell_appeared (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GnomeXSettingsManager *manager = user_data; manager->priv->have_shell = TRUE; notify_have_shell (manager); } static void on_shell_disappeared (GDBusConnection *connection, const gchar *name, gpointer user_data) { GnomeXSettingsManager *manager = user_data; manager->priv->have_shell = FALSE; notify_have_shell (manager); } static void on_unity_appeared (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GnomeXSettingsManager *manager = user_data; manager->priv->have_unity = TRUE; notify_have_shell (manager); } static void on_unity_disappeared (GDBusConnection *connection, const gchar *name, gpointer user_data) { GnomeXSettingsManager *manager = user_data; manager->priv->have_unity = FALSE; notify_have_shell (manager); } static void process_value (GnomeXSettingsManager *manager, TranslationEntry *trans, GVariant *value) { (* trans->translate) (manager, trans, value); } static TranslationEntry * find_translation_entry (GSettings *settings, const char *key) { guint i; char *schema; g_object_get (settings, "schema-id", &schema, NULL); for (i = 0; i < G_N_ELEMENTS (translations); i++) { if (g_str_equal (schema, translations[i].gsettings_schema) && g_str_equal (key, translations[i].gsettings_key)) { g_free (schema); return &translations[i]; } } g_free (schema); return NULL; } static void xsettings_callback (GSettings *settings, const char *key, GnomeXSettingsManager *manager) { TranslationEntry *trans; guint i; GVariant *value; if (g_str_equal (key, TEXT_SCALING_FACTOR_KEY) || g_str_equal (key, SCALING_FACTOR_KEY) || g_str_equal (key, CURSOR_SIZE_KEY) || g_str_equal (key, CURSOR_THEME_KEY)) { xft_callback (NULL, key, manager); return; } trans = find_translation_entry (settings, key); if (trans == NULL) { return; } value = g_settings_get_value (settings, key); process_value (manager, trans, value); g_variant_unref (value); for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_string (manager->priv->managers [i], "Net/FallbackIconTheme", "gnome"); } queue_notify (manager); } static void terminate_cb (void *data) { gboolean *terminated = data; if (*terminated) { return; } *terminated = TRUE; g_warning ("X Settings Manager is terminating"); gtk_main_quit (); } static gboolean setup_xsettings_managers (GnomeXSettingsManager *manager) { GdkDisplay *display; int i; int n_screens; gboolean res; gboolean terminated; display = gdk_display_get_default (); n_screens = gdk_display_get_n_screens (display); res = xsettings_manager_check_running (gdk_x11_display_get_xdisplay (display), gdk_screen_get_number (gdk_screen_get_default ())); if (res) { g_warning ("You can only run one xsettings manager at a time; exiting"); return FALSE; } manager->priv->managers = g_new0 (XSettingsManager *, n_screens + 1); terminated = FALSE; for (i = 0; i < n_screens; i++) { GdkScreen *screen; screen = gdk_display_get_screen (display, i); manager->priv->managers [i] = xsettings_manager_new (gdk_x11_display_get_xdisplay (display), gdk_screen_get_number (screen), terminate_cb, &terminated); if (! manager->priv->managers [i]) { g_warning ("Could not create xsettings manager for screen %d!", i); return FALSE; } } return TRUE; } static void start_shell_monitor (GnomeXSettingsManager *manager) { notify_have_shell (manager); manager->priv->have_shell = TRUE; manager->priv->shell_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, "org.gnome.Shell", 0, on_shell_appeared, on_shell_disappeared, manager, NULL); } static void start_unity_monitor (GnomeXSettingsManager *manager) { notify_have_shell (manager); manager->priv->have_unity = TRUE; manager->priv->unity_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, "com.canonical.AppMenu.Registrar", 0, on_unity_appeared, on_unity_disappeared, manager, NULL); } gboolean gnome_xsettings_manager_start (GnomeXSettingsManager *manager, GError **error) { GVariant *overrides; guint i; GList *list, *l; g_debug ("Starting xsettings manager"); gnome_settings_profile_start (NULL); if (!setup_xsettings_managers (manager)) { g_set_error (error, GSD_XSETTINGS_ERROR, GSD_XSETTINGS_ERROR_INIT, "Could not initialize xsettings manager."); return FALSE; } manager->priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_object_unref); g_hash_table_insert (manager->priv->settings, MOUSE_SETTINGS_SCHEMA, g_settings_new (MOUSE_SETTINGS_SCHEMA)); g_hash_table_insert (manager->priv->settings, INTERFACE_SETTINGS_SCHEMA, g_settings_new (INTERFACE_SETTINGS_SCHEMA)); g_hash_table_insert (manager->priv->settings, SOUND_SETTINGS_SCHEMA, g_settings_new (SOUND_SETTINGS_SCHEMA)); g_hash_table_insert (manager->priv->settings, PRIVACY_SETTINGS_SCHEMA, g_settings_new (PRIVACY_SETTINGS_SCHEMA)); g_hash_table_insert (manager->priv->settings, WM_SETTINGS_SCHEMA, g_settings_new (WM_SETTINGS_SCHEMA)); list = g_hash_table_get_values (manager->priv->settings); for (l = list; l != NULL; l = l->next) { g_signal_connect_object (G_OBJECT (l->data), "changed", G_CALLBACK (xsettings_callback), manager, 0); } g_list_free (list); for (i = 0; i < G_N_ELEMENTS (translations); i++) { GVariant *val; GSettings *settings; settings = g_hash_table_lookup (manager->priv->settings, translations[i].gsettings_schema); if (settings == NULL) { g_warning ("Schemas '%s' has not been setup", translations[i].gsettings_schema); continue; } val = g_settings_get_value (settings, translations[i].gsettings_key); process_value (manager, &translations[i], val); g_variant_unref (val); } /* Plugin settings (GTK modules and Xft) */ manager->priv->plugin_settings = g_settings_new (XSETTINGS_PLUGIN_SCHEMA); g_signal_connect_object (manager->priv->plugin_settings, "changed", G_CALLBACK (plugin_callback), manager, 0); manager->priv->gtk = gsd_xsettings_gtk_new (); g_signal_connect (G_OBJECT (manager->priv->gtk), "notify::gtk-modules", G_CALLBACK (gtk_modules_callback), manager); gtk_modules_callback (manager->priv->gtk, NULL, manager); /* Xft settings */ update_xft_settings (manager); start_fontconfig_monitor (manager); start_shell_monitor (manager); start_unity_monitor (manager); for (i = 0; manager->priv->managers [i]; i++) xsettings_manager_set_string (manager->priv->managers [i], "Net/FallbackIconTheme", "gnome"); overrides = g_settings_get_value (manager->priv->plugin_settings, XSETTINGS_OVERRIDE_KEY); for (i = 0; manager->priv->managers [i]; i++) { xsettings_manager_set_overrides (manager->priv->managers [i], overrides); } queue_notify (manager); g_variant_unref (overrides); gnome_settings_profile_end (NULL); return TRUE; } void gnome_xsettings_manager_stop (GnomeXSettingsManager *manager) { GnomeXSettingsManagerPrivate *p = manager->priv; int i; g_debug ("Stopping xsettings manager"); if (p->shell_name_watch_id > 0) g_bus_unwatch_name (p->shell_name_watch_id); if (p->managers != NULL) { for (i = 0; p->managers [i]; ++i) xsettings_manager_destroy (p->managers [i]); g_free (p->managers); p->managers = NULL; } if (p->plugin_settings != NULL) { g_signal_handlers_disconnect_by_data (p->plugin_settings, manager); g_object_unref (p->plugin_settings); p->plugin_settings = NULL; } stop_fontconfig_monitor (manager); if (manager->priv->unity_name_watch_id > 0) g_bus_unwatch_name (manager->priv->unity_name_watch_id); if (p->settings != NULL) { g_hash_table_destroy (p->settings); p->settings = NULL; } if (p->gtk != NULL) { g_object_unref (p->gtk); p->gtk = NULL; } } static void gnome_xsettings_manager_class_init (GnomeXSettingsManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gnome_xsettings_manager_finalize; g_type_class_add_private (klass, sizeof (GnomeXSettingsManagerPrivate)); } static void gnome_xsettings_manager_init (GnomeXSettingsManager *manager) { manager->priv = GNOME_XSETTINGS_MANAGER_GET_PRIVATE (manager); } static void gnome_xsettings_manager_finalize (GObject *object) { GnomeXSettingsManager *xsettings_manager; g_return_if_fail (object != NULL); g_return_if_fail (GNOME_IS_XSETTINGS_MANAGER (object)); xsettings_manager = GNOME_XSETTINGS_MANAGER (object); g_return_if_fail (xsettings_manager->priv != NULL); if (xsettings_manager->priv->start_idle_id != 0) g_source_remove (xsettings_manager->priv->start_idle_id); G_OBJECT_CLASS (gnome_xsettings_manager_parent_class)->finalize (object); } GnomeXSettingsManager * gnome_xsettings_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GNOME_TYPE_XSETTINGS_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GNOME_XSETTINGS_MANAGER (manager_object); } ./plugins/xsettings/xsettings-manager.c0000644000004100000410000002560712735467744020615 0ustar www-datawww-data/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #include #include #include #include #include /* For CARD16 */ #include "xsettings-manager.h" #define XSETTINGS_VARIANT_TYPE_COLOR (G_VARIANT_TYPE ("(qqqq)")) struct _XSettingsManager { Display *display; int screen; Window window; Atom manager_atom; Atom selection_atom; Atom xsettings_atom; XSettingsTerminateFunc terminate; void *cb_data; GHashTable *settings; unsigned long serial; GVariant *overrides; }; typedef struct { Window window; Atom timestamp_prop_atom; } TimeStampInfo; static Bool timestamp_predicate (Display *display, XEvent *xevent, XPointer arg) { TimeStampInfo *info = (TimeStampInfo *)arg; if (xevent->type == PropertyNotify && xevent->xproperty.window == info->window && xevent->xproperty.atom == info->timestamp_prop_atom) return True; return False; } /** * get_server_time: * @display: display from which to get the time * @window: a #Window, used for communication with the server. * The window must have PropertyChangeMask in its * events mask or a hang will result. * * Routine to get the current X server time stamp. * * Return value: the time stamp. **/ static Time get_server_time (Display *display, Window window) { unsigned char c = 'a'; XEvent xevent; TimeStampInfo info; info.timestamp_prop_atom = XInternAtom (display, "_TIMESTAMP_PROP", False); info.window = window; XChangeProperty (display, window, info.timestamp_prop_atom, info.timestamp_prop_atom, 8, PropModeReplace, &c, 1); XIfEvent (display, &xevent, timestamp_predicate, (XPointer)&info); return xevent.xproperty.time; } Bool xsettings_manager_check_running (Display *display, int screen) { char buffer[256]; Atom selection_atom; sprintf(buffer, "_XSETTINGS_S%d", screen); selection_atom = XInternAtom (display, buffer, False); if (XGetSelectionOwner (display, selection_atom)) return True; else return False; } XSettingsManager * xsettings_manager_new (Display *display, int screen, XSettingsTerminateFunc terminate, void *cb_data) { XSettingsManager *manager; Time timestamp; XClientMessageEvent xev; char buffer[256]; manager = g_slice_new (XSettingsManager); manager->display = display; manager->screen = screen; sprintf(buffer, "_XSETTINGS_S%d", screen); manager->selection_atom = XInternAtom (display, buffer, False); manager->xsettings_atom = XInternAtom (display, "_XSETTINGS_SETTINGS", False); manager->manager_atom = XInternAtom (display, "MANAGER", False); manager->terminate = terminate; manager->cb_data = cb_data; manager->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) xsettings_setting_free); manager->serial = 0; manager->overrides = NULL; manager->window = XCreateSimpleWindow (display, RootWindow (display, screen), 0, 0, 10, 10, 0, WhitePixel (display, screen), WhitePixel (display, screen)); XSelectInput (display, manager->window, PropertyChangeMask); timestamp = get_server_time (display, manager->window); XSetSelectionOwner (display, manager->selection_atom, manager->window, timestamp); /* Check to see if we managed to claim the selection. If not, * we treat it as if we got it then immediately lost it */ if (XGetSelectionOwner (display, manager->selection_atom) == manager->window) { xev.type = ClientMessage; xev.window = RootWindow (display, screen); xev.message_type = manager->manager_atom; xev.format = 32; xev.data.l[0] = timestamp; xev.data.l[1] = manager->selection_atom; xev.data.l[2] = manager->window; xev.data.l[3] = 0; /* manager specific data */ xev.data.l[4] = 0; /* manager specific data */ XSendEvent (display, RootWindow (display, screen), False, StructureNotifyMask, (XEvent *)&xev); } else { manager->terminate (manager->cb_data); } return manager; } void xsettings_manager_destroy (XSettingsManager *manager) { XDestroyWindow (manager->display, manager->window); g_hash_table_unref (manager->settings); g_slice_free (XSettingsManager, manager); } static void xsettings_manager_set_setting (XSettingsManager *manager, const gchar *name, gint tier, GVariant *value) { XSettingsSetting *setting; setting = g_hash_table_lookup (manager->settings, name); if (setting == NULL) { setting = xsettings_setting_new (name); setting->last_change_serial = manager->serial; g_hash_table_insert (manager->settings, setting->name, setting); } xsettings_setting_set (setting, tier, value, manager->serial); if (xsettings_setting_get (setting) == NULL) g_hash_table_remove (manager->settings, name); } void xsettings_manager_set_int (XSettingsManager *manager, const char *name, int value) { xsettings_manager_set_setting (manager, name, 0, g_variant_new_int32 (value)); } void xsettings_manager_set_string (XSettingsManager *manager, const char *name, const char *value) { xsettings_manager_set_setting (manager, name, 0, g_variant_new_string (value)); } void xsettings_manager_set_color (XSettingsManager *manager, const char *name, XSettingsColor *value) { GVariant *tmp; tmp = g_variant_new ("(qqqq)", value->red, value->green, value->blue, value->alpha); g_assert (g_variant_is_of_type (tmp, XSETTINGS_VARIANT_TYPE_COLOR)); /* paranoia... */ xsettings_manager_set_setting (manager, name, 0, tmp); } void xsettings_manager_delete_setting (XSettingsManager *manager, const char *name) { xsettings_manager_set_setting (manager, name, 0, NULL); } static gchar xsettings_get_typecode (GVariant *value) { switch (g_variant_classify (value)) { case G_VARIANT_CLASS_INT32: return XSETTINGS_TYPE_INT; case G_VARIANT_CLASS_STRING: return XSETTINGS_TYPE_STRING; case G_VARIANT_CLASS_TUPLE: return XSETTINGS_TYPE_COLOR; default: g_assert_not_reached (); } } static void align_string (GString *string, gint alignment) { /* Adds nul-bytes to the string until its length is an even multiple * of the specified alignment requirement. */ while ((string->len % alignment) != 0) g_string_append_c (string, '\0'); } static void setting_store (XSettingsSetting *setting, GString *buffer) { XSettingsType type; GVariant *value; guint16 len16; value = xsettings_setting_get (setting); type = xsettings_get_typecode (value); g_string_append_c (buffer, type); g_string_append_c (buffer, 0); len16 = strlen (setting->name); g_string_append_len (buffer, (gchar *) &len16, 2); g_string_append (buffer, setting->name); align_string (buffer, 4); g_string_append_len (buffer, (gchar *) &setting->last_change_serial, 4); if (type == XSETTINGS_TYPE_STRING) { const gchar *string; gsize stringlen; guint32 len32; string = g_variant_get_string (value, &stringlen); len32 = stringlen; g_string_append_len (buffer, (gchar *) &len32, 4); g_string_append (buffer, string); align_string (buffer, 4); } else /* GVariant format is the same as XSETTINGS format for the non-string types */ g_string_append_len (buffer, g_variant_get_data (value), g_variant_get_size (value)); } void xsettings_manager_notify (XSettingsManager *manager) { GString *buffer; GHashTableIter iter; int n_settings; gpointer value; n_settings = g_hash_table_size (manager->settings); buffer = g_string_new (NULL); g_string_append_c (buffer, xsettings_byte_order ()); g_string_append_c (buffer, '\0'); g_string_append_c (buffer, '\0'); g_string_append_c (buffer, '\0'); g_string_append_len (buffer, (gchar *) &manager->serial, 4); g_string_append_len (buffer, (gchar *) &n_settings, 4); g_hash_table_iter_init (&iter, manager->settings); while (g_hash_table_iter_next (&iter, NULL, &value)) setting_store (value, buffer); XChangeProperty (manager->display, manager->window, manager->xsettings_atom, manager->xsettings_atom, 8, PropModeReplace, (guchar *) buffer->str, buffer->len); g_string_free (buffer, TRUE); manager->serial++; } void xsettings_manager_set_overrides (XSettingsManager *manager, GVariant *overrides) { GVariantIter iter; const gchar *key; GVariant *value; g_return_if_fail (overrides != NULL && g_variant_is_of_type (overrides, G_VARIANT_TYPE_VARDICT)); if (manager->overrides) { /* unset the existing overrides */ g_variant_iter_init (&iter, manager->overrides); while (g_variant_iter_next (&iter, "{&sv}", &key, NULL)) /* only unset it at this point if it's not in the new list */ if (!g_variant_lookup (overrides, key, "*", NULL)) xsettings_manager_set_setting (manager, key, 1, NULL); g_variant_unref (manager->overrides); } /* save this so we can do the unsets next time */ manager->overrides = g_variant_ref_sink (overrides); /* set the new values */ g_variant_iter_init (&iter, overrides); while (g_variant_iter_loop (&iter, "{&sv}", &key, &value)) { /* only accept recognised types... */ if (!g_variant_is_of_type (value, G_VARIANT_TYPE_STRING) && !g_variant_is_of_type (value, G_VARIANT_TYPE_INT32) && !g_variant_is_of_type (value, XSETTINGS_VARIANT_TYPE_COLOR)) continue; xsettings_manager_set_setting (manager, key, 1, value); } } ./plugins/xsettings/gsd-xsettings-gtk.c0000644000004100000410000003015212735467744020532 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gsd-xsettings-gtk.h" #define XSETTINGS_PLUGIN_SCHEMA "org.gnome.settings-daemon.plugins.xsettings" #define GTK_MODULES_DISABLED_KEY "disabled-gtk-modules" #define GTK_MODULES_ENABLED_KEY "enabled-gtk-modules" enum { PROP_0, PROP_GTK_MODULES }; struct GsdXSettingsGtkPrivate { char *modules; GHashTable *dir_modules; GSettings *settings; guint64 dir_mtime; GFileMonitor *monitor; GList *cond_settings; }; #define GSD_XSETTINGS_GTK_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), GSD_TYPE_XSETTINGS_GTK, GsdXSettingsGtkPrivate)) G_DEFINE_TYPE(GsdXSettingsGtk, gsd_xsettings_gtk, G_TYPE_OBJECT) static void update_gtk_modules (GsdXSettingsGtk *gtk); static void empty_cond_settings_list (GsdXSettingsGtk *gtk) { if (gtk->priv->cond_settings == NULL) return; /* Empty the list of settings */ g_list_foreach (gtk->priv->cond_settings, (GFunc) g_object_unref, NULL); g_list_free (gtk->priv->cond_settings); gtk->priv->cond_settings = NULL; } static void cond_setting_changed (GSettings *settings, const char *key, GsdXSettingsGtk *gtk) { gboolean enabled; const char *module_name; module_name = g_object_get_data (G_OBJECT (settings), "module-name"); enabled = g_settings_get_boolean (settings, key); if (enabled != FALSE) { if (gtk->priv->dir_modules == NULL) gtk->priv->dir_modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); g_hash_table_insert (gtk->priv->dir_modules, g_strdup (module_name), NULL); } else if (gtk->priv->dir_modules != NULL) { g_hash_table_remove (gtk->priv->dir_modules, module_name); } update_gtk_modules (gtk); } static char * process_desktop_file (const char *path, GsdXSettingsGtk *gtk) { GKeyFile *keyfile; char *retval; char *module_name; retval = NULL; if (g_str_has_suffix (path, ".desktop") == FALSE && g_str_has_suffix (path, ".gtk-module") == FALSE) return retval; keyfile = g_key_file_new (); if (g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL) == FALSE) goto bail; if (g_key_file_has_group (keyfile, "GTK Module") == FALSE) goto bail; module_name = g_key_file_get_string (keyfile, "GTK Module", "X-GTK-Module-Name", NULL); if (module_name == NULL) goto bail; if (g_key_file_has_key (keyfile, "GTK Module", "X-GTK-Module-Enabled-Schema", NULL) != FALSE) { char *schema; char *key; gboolean enabled; GSettings *settings; char *signal; schema = g_key_file_get_string (keyfile, "GTK Module", "X-GTK-Module-Enabled-Schema", NULL); key = g_key_file_get_string (keyfile, "GTK Module", "X-GTK-Module-Enabled-Key", NULL); settings = g_settings_new (schema); gtk->priv->cond_settings = g_list_prepend (gtk->priv->cond_settings, settings); g_object_set_data_full (G_OBJECT (settings), "module-name", g_strdup (module_name), (GDestroyNotify) g_free); signal = g_strdup_printf ("changed::%s", key); g_signal_connect_object (G_OBJECT (settings), signal, G_CALLBACK (cond_setting_changed), gtk, 0); enabled = g_settings_get_boolean (settings, key); g_free (signal); g_free (schema); g_free (key); if (enabled != FALSE) retval = g_strdup (module_name); } else { retval = g_strdup (module_name); } g_free (module_name); bail: g_key_file_free (keyfile); return retval; } static void get_gtk_modules_from_dir (GsdXSettingsGtk *gtk) { GFile *file; GFileInfo *info; GHashTable *ht; file = g_file_new_for_path (GTK_MODULES_DIRECTORY); info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (info != NULL) { guint64 dir_mtime; dir_mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED); if (gtk->priv->dir_mtime == 0 || dir_mtime > gtk->priv->dir_mtime) { GDir *dir; const char *name; empty_cond_settings_list (gtk); gtk->priv->dir_mtime = dir_mtime; if (gtk->priv->dir_modules != NULL) { g_hash_table_destroy (gtk->priv->dir_modules); gtk->priv->dir_modules = NULL; } dir = g_dir_open (GTK_MODULES_DIRECTORY, 0, NULL); if (dir == NULL) goto bail; ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); while ((name = g_dir_read_name (dir)) != NULL) { char *path; char *module; path = g_build_filename (GTK_MODULES_DIRECTORY, name, NULL); module = process_desktop_file (path, gtk); if (module != NULL) g_hash_table_insert (ht, module, NULL); g_free (path); } g_dir_close (dir); gtk->priv->dir_modules = ht; } g_object_unref (info); } else { empty_cond_settings_list (gtk); } bail: g_object_unref (file); } static void stringify_gtk_modules (gpointer key, gpointer value, GString *str) { if (str->len != 0) g_string_append_c (str, ':'); g_string_append (str, key); } static void update_gtk_modules (GsdXSettingsGtk *gtk) { char **enabled, **disabled; GHashTable *ht; guint i; GString *str; char *modules; enabled = g_settings_get_strv (gtk->priv->settings, GTK_MODULES_ENABLED_KEY); disabled = g_settings_get_strv (gtk->priv->settings, GTK_MODULES_DISABLED_KEY); ht = g_hash_table_new (g_str_hash, g_str_equal); if (gtk->priv->dir_modules != NULL) { GList *list, *l; list = g_hash_table_get_keys (gtk->priv->dir_modules); for (l = list; l != NULL; l = l->next) { g_hash_table_insert (ht, l->data, NULL); } g_list_free (list); } for (i = 0; enabled[i] != NULL; i++) g_hash_table_insert (ht, enabled[i], NULL); for (i = 0; disabled[i] != NULL; i++) g_hash_table_remove (ht, disabled[i]); str = g_string_new (NULL); g_hash_table_foreach (ht, (GHFunc) stringify_gtk_modules, str); g_hash_table_destroy (ht); modules = g_string_free (str, FALSE); if (modules == NULL || gtk->priv->modules == NULL || g_str_equal (modules, gtk->priv->modules) == FALSE) { g_free (gtk->priv->modules); gtk->priv->modules = modules; g_object_notify (G_OBJECT (gtk), "gtk-modules"); } else { g_free (modules); } g_strfreev (enabled); g_strfreev (disabled); } static void gtk_modules_dir_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, GsdXSettingsGtk *gtk) { get_gtk_modules_from_dir (gtk); update_gtk_modules (gtk); } static void gsd_xsettings_gtk_init (GsdXSettingsGtk *gtk) { GFile *file; gtk->priv = GSD_XSETTINGS_GTK_GET_PRIVATE (gtk); g_debug ("GsdXSettingsGtk initializing"); gtk->priv->settings = g_settings_new (XSETTINGS_PLUGIN_SCHEMA); get_gtk_modules_from_dir (gtk); file = g_file_new_for_path (GTK_MODULES_DIRECTORY); gtk->priv->monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL); g_signal_connect (G_OBJECT (gtk->priv->monitor), "changed", G_CALLBACK (gtk_modules_dir_changed_cb), gtk); g_object_unref (file); update_gtk_modules (gtk); } static void gsd_xsettings_gtk_finalize (GObject *object) { GsdXSettingsGtk *gtk; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_XSETTINGS_GTK (object)); g_debug ("GsdXSettingsGtk finalizing"); gtk = GSD_XSETTINGS_GTK (object); g_return_if_fail (gtk->priv != NULL); g_free (gtk->priv->modules); gtk->priv->modules = NULL; if (gtk->priv->dir_modules != NULL) { g_hash_table_destroy (gtk->priv->dir_modules); gtk->priv->dir_modules = NULL; } g_object_unref (gtk->priv->settings); if (gtk->priv->monitor != NULL) g_object_unref (gtk->priv->monitor); empty_cond_settings_list (gtk); G_OBJECT_CLASS (gsd_xsettings_gtk_parent_class)->finalize (object); } static void gsd_xsettings_gtk_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsdXSettingsGtk *self; self = GSD_XSETTINGS_GTK (object); switch (prop_id) { case PROP_GTK_MODULES: g_value_set_string (value, self->priv->modules); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_xsettings_gtk_class_init (GsdXSettingsGtkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = gsd_xsettings_gtk_get_property; object_class->finalize = gsd_xsettings_gtk_finalize; g_object_class_install_property (object_class, PROP_GTK_MODULES, g_param_spec_string ("gtk-modules", NULL, NULL, NULL, G_PARAM_READABLE)); g_type_class_add_private (klass, sizeof (GsdXSettingsGtkPrivate)); } GsdXSettingsGtk * gsd_xsettings_gtk_new (void) { return GSD_XSETTINGS_GTK (g_object_new (GSD_TYPE_XSETTINGS_GTK, NULL)); } const char * gsd_xsettings_gtk_get_modules (GsdXSettingsGtk *gtk) { return gtk->priv->modules; } ./plugins/xsettings/gsd-xsettings-plugin.c0000644000004100000410000000203512735467744021242 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-xsettings-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GnomeXSettings, gnome_xsettings) ./plugins/xsettings/test-xsettings.c0000644000004100000410000000034112735467744020146 0ustar www-datawww-data#define NEW gnome_xsettings_manager_new #define START gnome_xsettings_manager_start #define STOP gnome_xsettings_manager_stop #define MANAGER GnomeXSettingsManager #include "gsd-xsettings-manager.h" #include "test-plugin.h" ./plugins/xsettings/xsettings-common.c0000644000004100000410000000561412735467744020467 0ustar www-datawww-data/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #include #include "string.h" #include "stdlib.h" #include #include /* For CARD32 */ #include "xsettings-common.h" XSettingsSetting * xsettings_setting_new (const gchar *name) { XSettingsSetting *result; result = g_slice_new0 (XSettingsSetting); result->name = g_strdup (name); return result; } static gboolean xsettings_variant_equal0 (GVariant *a, GVariant *b) { if (a == b) return TRUE; if (!a || !b) return FALSE; return g_variant_equal (a, b); } GVariant * xsettings_setting_get (XSettingsSetting *setting) { gint i; for (i = G_N_ELEMENTS (setting->value) - 1; 0 <= i; i--) if (setting->value[i]) return setting->value[i]; return NULL; } void xsettings_setting_set (XSettingsSetting *setting, gint tier, GVariant *value, guint32 serial) { GVariant *old_value; old_value = xsettings_setting_get (setting); if (old_value) g_variant_ref (old_value); if (setting->value[tier]) g_variant_unref (setting->value[tier]); setting->value[tier] = value ? g_variant_ref_sink (value) : NULL; if (!xsettings_variant_equal0 (old_value, xsettings_setting_get (setting))) setting->last_change_serial = serial; if (old_value) g_variant_unref (old_value); } void xsettings_setting_free (XSettingsSetting *setting) { gint i; for (i = 0; i < G_N_ELEMENTS (setting->value); i++) if (setting->value[i]) g_variant_unref (setting->value[i]); g_free (setting->name); g_slice_free (XSettingsSetting, setting); } char xsettings_byte_order (void) { CARD32 myint = 0x01020304; return (*(char *)&myint == 1) ? MSBFirst : LSBFirst; } ./plugins/xsettings/README.xsettings0000644000004100000410000000257212735467744017714 0ustar www-datawww-dataThis is very simple documentation for the 'override' GSettings key for gnome-setting-daemon's xsettings plugin. The override is given as a dictionary of overrides to be applied on top of the usual values that are exported to the X server as XSETTINGS. The intent of this is to allow users to override values of programmatically determined settings (such as 'Gtk/ShellShowsAppMenu') and to allow developers to introduce new XSETTINGS for testing (without having to kill the gnome-settings-daemon running in the session and run their own patched version). The type of the overrides is 'a{sv}'. The key gives the full XSETTINGS setting name to override (for example, 'Gtk/ShellShowsAppMenu'). The value is one of the following: - a string ('s') for the case of a string XSETTING - an int32 ('i') for the case of an integer XSETTING - a 4-tuple of uint16s ('(qqqq)') for the case of a color XSETTING Dictionary items with a value that is not one of the above types will be ignored. Specifically note that XSETTINGS does not have a concept of booleans -- you must use an integer that is either 0 or 1. An example setting for this key (as expressed in GVariant text format) might be: { 'Gtk/ShellShowsAppMenu': < 0 >, 'Xft/DPI': < 98304 > } Noting that variants must be specified in the usual way (wrapped in <>). Note also that DPI in the above example is expressed in 1024ths of an inch. ./plugins/mouse/0000755000004100000410000000000012735467745014100 5ustar www-datawww-data./plugins/mouse/gsd-mouse-plugin.c0000644000004100000410000000201512735467744017440 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-mouse-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdMouse, gsd_mouse) ./plugins/mouse/gsd-locate-pointer.h0000644000004100000410000000153312735467744017752 0ustar www-datawww-data/* * Copyright 2001 Jonathan Blandford * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * Authors: Jonathan Blandford */ #ifndef LOCATE_POINTER_H #define LOCATE_POINTER_H #include void gsd_locate_pointer (GdkScreen *screen); #endif ./plugins/mouse/Makefile.am0000644000004100000410000000365612735467744016145 0ustar www-datawww-dataplugin_name = mouse plugin_LTLIBRARIES = libmouse.la libmouse_la_SOURCES = \ gsd-mouse-plugin.c \ gsd-mouse-manager.h \ gsd-mouse-manager.c libmouse_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common/ \ -I$(top_srcdir)/data/ \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) libmouse_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(MOUSE_CFLAGS) \ $(AM_CFLAGS) libmouse_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libmouse_la_LIBADD = \ $(MOUSE_LIBS) \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = mouse.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) libexec_PROGRAMS = usd-locate-pointer usd_locate_pointer_SOURCES = \ gsd-locate-pointer.h \ gsd-locate-pointer.c \ gsd-timeline.h \ gsd-timeline.c usd_locate_pointer_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(MOUSE_CFLAGS) \ $(AM_CFLAGS) usd_locate_pointer_LDADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(MOUSE_LIBS) \ -lm libexec_PROGRAMS += usd-test-mouse usd_test_mouse_SOURCES = \ test-mouse.c \ gsd-mouse-manager.c \ gsd-mouse-manager.h usd_test_mouse_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) usd_test_mouse_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(MOUSE_CFLAGS) \ $(AM_CFLAGS) usd_test_mouse_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ $(MOUSE_LIBS) \ -lm EXTRA_DIST = $(plugin_in_files) CLEANFILES = $(plugin_DATA) DISTCLEANFILES = $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/mouse/gsd-timeline.c0000644000004100000410000004730212735467744016632 0ustar www-datawww-data/* gsd-timeline.c * * Copyright (C) 2008 Carlos Garnacho * * 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 #include #include #include "gsd-timeline.h" #define GSD_TIMELINE_GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GSD_TYPE_TIMELINE, GsdTimelinePriv)) #define MSECS_PER_SEC 1000 #define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes) #define DEFAULT_FPS 30 typedef struct GsdTimelinePriv GsdTimelinePriv; struct GsdTimelinePriv { guint duration; guint fps; guint source_id; GTimer *timer; GdkScreen *screen; GsdTimelineProgressType progress_type; GsdTimelineProgressFunc progress_func; guint loop : 1; guint direction : 1; }; enum { PROP_0, PROP_FPS, PROP_DURATION, PROP_LOOP, PROP_DIRECTION, PROP_SCREEN, PROP_PROGRESS_TYPE, }; enum { STARTED, PAUSED, FINISHED, FRAME, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; static void gsd_timeline_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gsd_timeline_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void gsd_timeline_finalize (GObject *object); G_DEFINE_TYPE (GsdTimeline, gsd_timeline, G_TYPE_OBJECT) GType gsd_timeline_direction_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GEnumValue values[] = { { GSD_TIMELINE_DIRECTION_FORWARD, "GSD_TIMELINE_DIRECTION_FORWARD", "forward" }, { GSD_TIMELINE_DIRECTION_BACKWARD, "GSD_TIMELINE_DIRECTION_BACKWARD", "backward" }, { 0, NULL, NULL } }; type = g_enum_register_static (g_intern_static_string ("GsdTimelineDirection"), values); } return type; } GType gsd_timeline_progress_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static const GEnumValue values[] = { { GSD_TIMELINE_PROGRESS_LINEAR, "GSD_TIMELINE_PROGRESS_LINEAR", "linear" }, { GSD_TIMELINE_PROGRESS_SINUSOIDAL, "GSD_TIMELINE_PROGRESS_SINUSOIDAL", "sinusoidal" }, { GSD_TIMELINE_PROGRESS_EXPONENTIAL, "GSD_TIMELINE_PROGRESS_EXPONENTIAL", "exponential" }, { 0, NULL, NULL } }; type = g_enum_register_static (g_intern_static_string ("GsdTimelineProgressType"), values); } return type; } static void gsd_timeline_class_init (GsdTimelineClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); object_class->set_property = gsd_timeline_set_property; object_class->get_property = gsd_timeline_get_property; object_class->finalize = gsd_timeline_finalize; g_object_class_install_property (object_class, PROP_FPS, g_param_spec_uint ("fps", "FPS", "Frames per second for the timeline", 1, G_MAXUINT, DEFAULT_FPS, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_DURATION, g_param_spec_uint ("duration", "Animation Duration", "Animation Duration", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_LOOP, g_param_spec_boolean ("loop", "Loop", "Whether the timeline loops or not", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_DIRECTION, g_param_spec_enum ("direction", "Direction", "Whether the timeline moves forward or backward in time", GSD_TYPE_TIMELINE_DIRECTION, GSD_TIMELINE_DIRECTION_FORWARD, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_DIRECTION, g_param_spec_enum ("progress-type", "Progress type", "Type of progress through the timeline", GSD_TYPE_TIMELINE_PROGRESS_TYPE, GSD_TIMELINE_PROGRESS_LINEAR, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SCREEN, g_param_spec_object ("screen", "Screen", "Screen to get the settings from", GDK_TYPE_SCREEN, G_PARAM_READWRITE)); signals[STARTED] = g_signal_new ("started", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsdTimelineClass, started), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[PAUSED] = g_signal_new ("paused", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsdTimelineClass, paused), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[FINISHED] = g_signal_new ("finished", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsdTimelineClass, finished), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[FRAME] = g_signal_new ("frame", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsdTimelineClass, frame), NULL, NULL, g_cclosure_marshal_VOID__DOUBLE, G_TYPE_NONE, 1, G_TYPE_DOUBLE); g_type_class_add_private (class, sizeof (GsdTimelinePriv)); } static void gsd_timeline_init (GsdTimeline *timeline) { GsdTimelinePriv *priv; priv = GSD_TIMELINE_GET_PRIV (timeline); priv->fps = DEFAULT_FPS; priv->duration = 0; priv->direction = GSD_TIMELINE_DIRECTION_FORWARD; priv->screen = gdk_screen_get_default (); } static void gsd_timeline_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GsdTimeline *timeline; timeline = GSD_TIMELINE (object); switch (prop_id) { case PROP_FPS: gsd_timeline_set_fps (timeline, g_value_get_uint (value)); break; case PROP_DURATION: gsd_timeline_set_duration (timeline, g_value_get_uint (value)); break; case PROP_LOOP: gsd_timeline_set_loop (timeline, g_value_get_boolean (value)); break; case PROP_DIRECTION: gsd_timeline_set_direction (timeline, g_value_get_enum (value)); break; case PROP_SCREEN: gsd_timeline_set_screen (timeline, GDK_SCREEN (g_value_get_object (value))); break; case PROP_PROGRESS_TYPE: gsd_timeline_set_progress_type (timeline, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gsd_timeline_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsdTimeline *timeline; GsdTimelinePriv *priv; timeline = GSD_TIMELINE (object); priv = GSD_TIMELINE_GET_PRIV (timeline); switch (prop_id) { case PROP_FPS: g_value_set_uint (value, priv->fps); break; case PROP_DURATION: g_value_set_uint (value, priv->duration); break; case PROP_LOOP: g_value_set_boolean (value, priv->loop); break; case PROP_DIRECTION: g_value_set_enum (value, priv->direction); break; case PROP_SCREEN: g_value_set_object (value, priv->screen); break; case PROP_PROGRESS_TYPE: g_value_set_enum (value, priv->progress_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gsd_timeline_finalize (GObject *object) { GsdTimelinePriv *priv; priv = GSD_TIMELINE_GET_PRIV (object); if (priv->source_id) { g_source_remove (priv->source_id); priv->source_id = 0; } if (priv->timer) g_timer_destroy (priv->timer); G_OBJECT_CLASS (gsd_timeline_parent_class)->finalize (object); } /* Sinusoidal progress */ static gdouble sinusoidal_progress (gdouble progress) { return (sinf ((progress * G_PI) / 2)); } static gdouble exponential_progress (gdouble progress) { return progress * progress; } static GsdTimelineProgressFunc progress_type_to_func (GsdTimelineProgressType type) { if (type == GSD_TIMELINE_PROGRESS_SINUSOIDAL) return sinusoidal_progress; else if (type == GSD_TIMELINE_PROGRESS_EXPONENTIAL) return exponential_progress; return NULL; } static gboolean gsd_timeline_run_frame (GsdTimeline *timeline, gboolean enable_animations) { GsdTimelinePriv *priv; gdouble linear_progress, progress; guint elapsed_time; GsdTimelineProgressFunc progress_func = NULL; priv = GSD_TIMELINE_GET_PRIV (timeline); if (enable_animations) { elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000); linear_progress = (gdouble) elapsed_time / priv->duration; if (priv->direction == GSD_TIMELINE_DIRECTION_BACKWARD) linear_progress = 1 - linear_progress; linear_progress = CLAMP (linear_progress, 0., 1.); if (priv->progress_func) progress_func = priv->progress_func; else if (priv->progress_type) progress_func = progress_type_to_func (priv->progress_type); if (progress_func) progress = (progress_func) (linear_progress); else progress = linear_progress; } else progress = (priv->direction == GSD_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0; g_signal_emit (timeline, signals [FRAME], 0, CLAMP (progress, 0.0, 1.0)); if ((priv->direction == GSD_TIMELINE_DIRECTION_FORWARD && progress >= 1.0) || (priv->direction == GSD_TIMELINE_DIRECTION_BACKWARD && progress <= 0.0)) { if (!priv->loop) { if (priv->source_id) { g_source_remove (priv->source_id); priv->source_id = 0; } g_signal_emit (timeline, signals [FINISHED], 0); return FALSE; } else gsd_timeline_rewind (timeline); } return TRUE; } static gboolean gsd_timeline_frame_idle_func (GsdTimeline *timeline) { return gsd_timeline_run_frame (timeline, TRUE); } /** * gsd_timeline_new: * @duration: duration in milliseconds for the timeline * * Creates a new #GsdTimeline with the specified number of frames. * * Return Value: the newly created #GsdTimeline **/ GsdTimeline * gsd_timeline_new (guint duration) { return g_object_new (GSD_TYPE_TIMELINE, "duration", duration, NULL); } GsdTimeline * gsd_timeline_new_for_screen (guint duration, GdkScreen *screen) { return g_object_new (GSD_TYPE_TIMELINE, "duration", duration, "screen", screen, NULL); } /** * gsd_timeline_start: * @timeline: A #GsdTimeline * * Runs the timeline from the current frame. **/ void gsd_timeline_start (GsdTimeline *timeline) { GsdTimelinePriv *priv; GtkSettings *settings; gboolean enable_animations = FALSE; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); if (priv->screen) { settings = gtk_settings_get_for_screen (priv->screen); g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL); } if (enable_animations) { if (!priv->source_id) { if (priv->timer) g_timer_continue (priv->timer); else priv->timer = g_timer_new (); /* sanity check */ g_assert (priv->fps > 0); g_signal_emit (timeline, signals [STARTED], 0); priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps), (GSourceFunc) gsd_timeline_frame_idle_func, timeline); } } else { /* If animations are not enabled, only run the last frame, * it take us instantaneously to the last state of the animation. * The only potential flaw happens when people use the ::finished * signal to trigger another animation, or even worse, finally * loop into this animation again. */ g_signal_emit (timeline, signals [STARTED], 0); gsd_timeline_run_frame (timeline, FALSE); } } /** * gsd_timeline_pause: * @timeline: A #GsdTimeline * * Pauses the timeline. **/ void gsd_timeline_pause (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); if (priv->source_id) { g_source_remove (priv->source_id); priv->source_id = 0; g_timer_stop (priv->timer); g_signal_emit (timeline, signals [PAUSED], 0); } } /** * gsd_timeline_rewind: * @timeline: A #GsdTimeline * * Rewinds the timeline. **/ void gsd_timeline_rewind (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); /* destroy and re-create timer if neccesary */ if (priv->timer) { g_timer_destroy (priv->timer); if (gsd_timeline_is_running (timeline)) priv->timer = g_timer_new (); else priv->timer = NULL; } } /** * gsd_timeline_is_running: * @timeline: A #GsdTimeline * * Returns whether the timeline is running or not. * * Return Value: %TRUE if the timeline is running **/ gboolean gsd_timeline_is_running (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), FALSE); priv = GSD_TIMELINE_GET_PRIV (timeline); return (priv->source_id != 0); } /** * gsd_timeline_get_fps: * @timeline: A #GsdTimeline * * Returns the number of frames per second. * * Return Value: frames per second **/ guint gsd_timeline_get_fps (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), 1); priv = GSD_TIMELINE_GET_PRIV (timeline); return priv->fps; } /** * gsd_timeline_set_fps: * @timeline: A #GsdTimeline * @fps: frames per second * * Sets the number of frames per second that * the timeline will play. **/ void gsd_timeline_set_fps (GsdTimeline *timeline, guint fps) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); g_return_if_fail (fps > 0); priv = GSD_TIMELINE_GET_PRIV (timeline); priv->fps = fps; if (gsd_timeline_is_running (timeline)) { g_source_remove (priv->source_id); priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps), (GSourceFunc) gsd_timeline_run_frame, timeline); } g_object_notify (G_OBJECT (timeline), "fps"); } /** * gsd_timeline_get_loop: * @timeline: A #GsdTimeline * * Returns whether the timeline loops to the * beginning when it has reached the end. * * Return Value: %TRUE if the timeline loops **/ gboolean gsd_timeline_get_loop (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), FALSE); priv = GSD_TIMELINE_GET_PRIV (timeline); return priv->loop; } /** * gsd_timeline_set_loop: * @timeline: A #GsdTimeline * @loop: %TRUE to make the timeline loop * * Sets whether the timeline loops to the beginning * when it has reached the end. **/ void gsd_timeline_set_loop (GsdTimeline *timeline, gboolean loop) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); priv->loop = loop; g_object_notify (G_OBJECT (timeline), "loop"); } void gsd_timeline_set_duration (GsdTimeline *timeline, guint duration) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); priv->duration = duration; g_object_notify (G_OBJECT (timeline), "duration"); } guint gsd_timeline_get_duration (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), 0); priv = GSD_TIMELINE_GET_PRIV (timeline); return priv->duration; } /** * gsd_timeline_get_direction: * @timeline: A #GsdTimeline * * Returns the direction of the timeline. * * Return Value: direction **/ GsdTimelineDirection gsd_timeline_get_direction (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), GSD_TIMELINE_DIRECTION_FORWARD); priv = GSD_TIMELINE_GET_PRIV (timeline); return priv->direction; } /** * gsd_timeline_set_direction: * @timeline: A #GsdTimeline * @direction: direction * * Sets the direction of the timeline. **/ void gsd_timeline_set_direction (GsdTimeline *timeline, GsdTimelineDirection direction) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); priv->direction = direction; g_object_notify (G_OBJECT (timeline), "direction"); } GdkScreen * gsd_timeline_get_screen (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), NULL); priv = GSD_TIMELINE_GET_PRIV (timeline); return priv->screen; } void gsd_timeline_set_screen (GsdTimeline *timeline, GdkScreen *screen) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); g_return_if_fail (GDK_IS_SCREEN (screen)); priv = GSD_TIMELINE_GET_PRIV (timeline); if (priv->screen) g_object_unref (priv->screen); priv->screen = g_object_ref (screen); g_object_notify (G_OBJECT (timeline), "screen"); } void gsd_timeline_set_progress_type (GsdTimeline *timeline, GsdTimelineProgressType type) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); priv->progress_type = type; g_object_notify (G_OBJECT (timeline), "progress-type"); } GsdTimelineProgressType gsd_timeline_get_progress_type (GsdTimeline *timeline) { GsdTimelinePriv *priv; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), GSD_TIMELINE_PROGRESS_LINEAR); priv = GSD_TIMELINE_GET_PRIV (timeline); if (priv->progress_func) return GSD_TIMELINE_PROGRESS_LINEAR; return priv->progress_type; } /** * gsd_timeline_set_progress_func: * @timeline: A #GsdTimeline * @progress_func: progress function * * Sets the progress function. This function will be used to calculate * a different progress to pass to the ::frame signal based on the * linear progress through the timeline. Setting progress_func * to %NULL will make the timeline use the default function, * which is just a linear progress. * * All progresses are in the [0.0, 1.0] range. **/ void gsd_timeline_set_progress_func (GsdTimeline *timeline, GsdTimelineProgressFunc progress_func) { GsdTimelinePriv *priv; g_return_if_fail (GSD_IS_TIMELINE (timeline)); priv = GSD_TIMELINE_GET_PRIV (timeline); priv->progress_func = progress_func; } gdouble gsd_timeline_get_progress (GsdTimeline *timeline) { GsdTimelinePriv *priv; GsdTimelineProgressFunc progress_func = NULL; gdouble linear_progress, progress; guint elapsed_time; g_return_val_if_fail (GSD_IS_TIMELINE (timeline), 0.0); priv = GSD_TIMELINE_GET_PRIV (timeline); if (!priv->timer) return 0.; elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000); linear_progress = (gdouble) elapsed_time / priv->duration; if (priv->direction == GSD_TIMELINE_DIRECTION_BACKWARD) linear_progress = 1 - linear_progress; linear_progress = CLAMP (linear_progress, 0., 1.); if (priv->progress_func) progress_func = priv->progress_func; else if (priv->progress_type) progress_func = progress_type_to_func (priv->progress_type); if (progress_func) progress = (progress_func) (linear_progress); else progress = linear_progress; return CLAMP (progress, 0., 1.); } ./plugins/mouse/gsd-mouse-manager.c0000644000004100000410000015126512735467744017570 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include #ifdef __linux #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-profile.h" #include "gsd-mouse-manager.h" #include "gsd-input-helper.h" #include "gsd-enums.h" #include "gsd-settings-migrate.h" #define GSD_MOUSE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_MOUSE_MANAGER, GsdMouseManagerPrivate)) #define GSD_SETTINGS_MOUSE_SCHEMA "org.gnome.settings-daemon.peripherals.mouse" #define GSETTINGS_MOUSE_SCHEMA "org.gnome.desktop.peripherals.mouse" #define GSETTINGS_TOUCHPAD_SCHEMA "org.gnome.desktop.peripherals.touchpad" /* Keys for both touchpad and mouse */ #define KEY_LEFT_HANDED "left-handed" /* a boolean for mouse, an enum for touchpad */ #define KEY_SPEED "speed" /* Touchpad settings */ #define KEY_SCROLL_METHOD "scroll-method" #define KEY_TAP_TO_CLICK "tap-to-click" #define KEY_SEND_EVENTS "send-events" #define KEY_NATURAL_SCROLL_ENABLED "natural-scroll" /* Mouse settings */ #define KEY_LOCATE_POINTER "locate-pointer" #define KEY_DWELL_CLICK_ENABLED "dwell-click-enabled" #define KEY_SECONDARY_CLICK_ENABLED "secondary-click-enabled" struct GsdMouseManagerPrivate { guint start_idle_id; GSettings *touchpad_settings; GSettings *mouse_settings; GSettings *mouse_a11y_settings; GSettings *gsd_mouse_settings; GdkDeviceManager *device_manager; guint device_added_id; guint device_removed_id; GHashTable *blacklist; gboolean mousetweaks_daemon_running; gboolean syndaemon_spawned; GPid syndaemon_pid; gboolean locate_pointer_spawned; GPid locate_pointer_pid; }; static void gsd_mouse_manager_class_init (GsdMouseManagerClass *klass); static void gsd_mouse_manager_init (GsdMouseManager *mouse_manager); static void gsd_mouse_manager_finalize (GObject *object); static void set_tap_to_click (GdkDevice *device, gboolean state, gboolean left_handed); static void set_natural_scroll (GsdMouseManager *manager, GdkDevice *device, gboolean natural_scroll); G_DEFINE_TYPE (GsdMouseManager, gsd_mouse_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static GObject * gsd_mouse_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdMouseManager *mouse_manager; mouse_manager = GSD_MOUSE_MANAGER (G_OBJECT_CLASS (gsd_mouse_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (mouse_manager); } static void gsd_mouse_manager_dispose (GObject *object) { G_OBJECT_CLASS (gsd_mouse_manager_parent_class)->dispose (object); } static void gsd_mouse_manager_class_init (GsdMouseManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_mouse_manager_constructor; object_class->dispose = gsd_mouse_manager_dispose; object_class->finalize = gsd_mouse_manager_finalize; g_type_class_add_private (klass, sizeof (GsdMouseManagerPrivate)); } static XDevice * open_gdk_device (GdkDevice *device) { XDevice *xdevice; int id; g_object_get (G_OBJECT (device), "device-id", &id, NULL); gdk_error_trap_push (); xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id); if (gdk_error_trap_pop () != 0) return NULL; return xdevice; } static gboolean device_is_blacklisted (GsdMouseManager *manager, GdkDevice *device) { int id; g_object_get (G_OBJECT (device), "device-id", &id, NULL); if (g_hash_table_lookup (manager->priv->blacklist, GINT_TO_POINTER (id)) != NULL) { g_debug ("device %s (%d) is blacklisted", gdk_device_get_name (device), id); return TRUE; } return FALSE; } static gboolean device_is_ignored (GsdMouseManager *manager, GdkDevice *device) { GdkInputSource source; const char *name; if (device_is_blacklisted (manager, device)) return TRUE; source = gdk_device_get_source (device); if (source != GDK_SOURCE_MOUSE && source != GDK_SOURCE_TOUCHPAD && source != GDK_SOURCE_CURSOR) return TRUE; name = gdk_device_get_name (device); if (name != NULL && g_str_equal ("Virtual core XTEST pointer", name)) return TRUE; return FALSE; } static void configure_button_layout (guchar *buttons, gint n_buttons, gboolean left_handed) { const gint left_button = 1; gint right_button; gint i; /* if the button is higher than 2 (3rd button) then it's * probably one direction of a scroll wheel or something else * uninteresting */ right_button = MIN (n_buttons, 3); /* If we change things we need to make sure we only swap buttons. * If we end up with multiple physical buttons assigned to the same * logical button the server will complain. This code assumes physical * button 0 is the physical left mouse button, and that the physical * button other than 0 currently assigned left_button or right_button * is the physical right mouse button. */ /* check if the current mapping satisfies the above assumptions */ if (buttons[left_button - 1] != left_button && buttons[left_button - 1] != right_button) /* The current mapping is weird. Swapping buttons is probably not a * good idea. */ return; /* check if we are left_handed and currently not swapped */ if (left_handed && buttons[left_button - 1] == left_button) { /* find the right button */ for (i = 0; i < n_buttons; i++) { if (buttons[i] == right_button) { buttons[i] = left_button; break; } } /* swap the buttons */ buttons[left_button - 1] = right_button; } /* check if we are not left_handed but are swapped */ else if (!left_handed && buttons[left_button - 1] == right_button) { /* find the right button */ for (i = 0; i < n_buttons; i++) { if (buttons[i] == left_button) { buttons[i] = right_button; break; } } /* swap the buttons */ buttons[left_button - 1] = left_button; } } static gboolean xinput_device_has_buttons (GdkDevice *device) { int i; XAnyClassInfo *class_info; /* FIXME can we use the XDevice's classes here instead? */ XDeviceInfo *device_info, *info; gint n_devices; int id; /* Find the XDeviceInfo for the GdkDevice */ g_object_get (G_OBJECT (device), "device-id", &id, NULL); device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) return FALSE; info = NULL; for (i = 0; i < n_devices; i++) { if (device_info[i].id == id) { info = &device_info[i]; break; } } if (info == NULL) goto bail; class_info = info->inputclassinfo; for (i = 0; i < info->num_classes; i++) { if (class_info->class == ButtonClass) { XButtonInfo *button_info; button_info = (XButtonInfo *) class_info; if (button_info->num_buttons > 0) { XFreeDeviceList (device_info); return TRUE; } } class_info = (XAnyClassInfo *) (((guchar *) class_info) + class_info->length); } bail: XFreeDeviceList (device_info); return FALSE; } static gboolean touchpad_has_single_button (XDevice *device) { Atom type, prop; int format; unsigned long nitems, bytes_after; unsigned char *data; gboolean is_single_button = FALSE; int rc; prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Capabilities", False); if (!prop) return FALSE; gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device, prop, 0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, &data); if (rc == Success && type == XA_INTEGER && format == 8 && nitems >= 3) is_single_button = (data[0] == 1 && data[1] == 0 && data[2] == 0); if (rc == Success) XFree (data); gdk_error_trap_pop_ignored (); return is_single_button; } static void set_left_handed (GsdMouseManager *manager, GdkDevice *device, gboolean mouse_left_handed, gboolean touchpad_left_handed) { XDevice *xdevice; guchar *buttons; gsize buttons_capacity = 16; gboolean left_handed; gint n_buttons; if (!xinput_device_has_buttons (device)) return; xdevice = open_gdk_device (device); if (xdevice == NULL) return; g_debug ("setting handedness on %s", gdk_device_get_name (device)); buttons = g_new (guchar, buttons_capacity); /* If the device is a touchpad, swap tap buttons * around too, otherwise a tap would be a right-click */ if (device_is_touchpad (xdevice)) { gboolean tap = g_settings_get_boolean (manager->priv->touchpad_settings, KEY_TAP_TO_CLICK); gboolean single_button = touchpad_has_single_button (xdevice); left_handed = touchpad_left_handed; if (tap && !single_button) set_tap_to_click (device, tap, left_handed); if (single_button) goto out; } else { left_handed = mouse_left_handed; } gdk_error_trap_push (); n_buttons = XGetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, buttons, buttons_capacity); while (n_buttons > buttons_capacity) { buttons_capacity = n_buttons; buttons = (guchar *) g_realloc (buttons, buttons_capacity * sizeof (guchar)); n_buttons = XGetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, buttons, buttons_capacity); } configure_button_layout (buttons, n_buttons, left_handed); XSetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, buttons, n_buttons); gdk_error_trap_pop_ignored (); out: xdevice_close (xdevice); g_free (buttons); } static void set_motion (GsdMouseManager *manager, GdkDevice *device) { XDevice *xdevice; XPtrFeedbackControl feedback; XFeedbackState *states, *state; int num_feedbacks; int numerator, denominator; gfloat motion_acceleration; int motion_threshold; GSettings *settings; gdouble speed; guint i; xdevice = open_gdk_device (device); if (xdevice == NULL) return; g_debug ("setting motion on %s", gdk_device_get_name (device)); if (device_is_touchpad (xdevice)) settings = manager->priv->touchpad_settings; else settings = manager->priv->mouse_settings; speed = g_settings_get_double (settings, KEY_SPEED); /* Calculate acceleration and threshold */ motion_acceleration = (speed + 1) * 5; /* speed is [-1..1], map to [0..10] */ motion_threshold = CLAMP (10 - floor (motion_acceleration), 1, 10); if (motion_acceleration >= 1.0) { /* we want to get the acceleration, with a resolution of 0.5 */ if ((motion_acceleration - floor (motion_acceleration)) < 0.25) { numerator = floor (motion_acceleration); denominator = 1; } else if ((motion_acceleration - floor (motion_acceleration)) < 0.5) { numerator = ceil (2.0 * motion_acceleration); denominator = 2; } else if ((motion_acceleration - floor (motion_acceleration)) < 0.75) { numerator = floor (2.0 *motion_acceleration); denominator = 2; } else { numerator = ceil (motion_acceleration); denominator = 1; } } else if (motion_acceleration < 1.0 && motion_acceleration > 0) { /* This we do to 1/10ths */ numerator = floor (motion_acceleration * 10) + 1; denominator= 10; } else { numerator = -1; denominator = -1; } gdk_error_trap_push (); /* Get the list of feedbacks for the device */ states = XGetFeedbackControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, &num_feedbacks); if (states == NULL) goto out; state = (XFeedbackState *) states; for (i = 0; i < num_feedbacks; i++) { if (state->class == PtrFeedbackClass) { /* And tell the device */ feedback.class = PtrFeedbackClass; feedback.length = sizeof (XPtrFeedbackControl); feedback.id = state->id; feedback.threshold = motion_threshold; feedback.accelNum = numerator; feedback.accelDenom = denominator; g_debug ("Setting accel %d/%d, threshold %d for device '%s'", numerator, denominator, motion_threshold, gdk_device_get_name (device)); XChangeFeedbackControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, DvAccelNum | DvAccelDenom | DvThreshold, (XFeedbackControl *) &feedback); break; } state = (XFeedbackState *) ((char *) state + state->length); } if (gdk_error_trap_pop ()) g_warning ("Error setting acceleration on \"%s\"", gdk_device_get_name (device)); XFreeFeedbackList (states); out: xdevice_close (xdevice); } /* Ensure that syndaemon dies together with us, to avoid running several of * them */ static void setup_syndaemon (gpointer user_data) { #ifdef __linux prctl (PR_SET_PDEATHSIG, SIGHUP); #endif } static gboolean have_program_in_path (const char *name) { gchar *path; gboolean result; path = g_find_program_in_path (name); result = (path != NULL); g_free (path); return result; } static void syndaemon_died (GPid pid, gint status, gpointer user_data) { GsdMouseManager *manager = GSD_MOUSE_MANAGER (user_data); g_debug ("syndaemon stopped with status %i", status); g_spawn_close_pid (pid); manager->priv->syndaemon_spawned = FALSE; } static int set_disable_w_typing (GsdMouseManager *manager, gboolean state) { if (state && touchpad_is_present ()) { GError *error = NULL; GPtrArray *args; if (manager->priv->syndaemon_spawned) return 0; if (!have_program_in_path ("syndaemon")) return 0; args = g_ptr_array_new (); g_ptr_array_add (args, "syndaemon"); g_ptr_array_add (args, "-i"); g_ptr_array_add (args, "1.0"); g_ptr_array_add (args, "-t"); g_ptr_array_add (args, "-K"); g_ptr_array_add (args, "-R"); g_ptr_array_add (args, NULL); /* we must use G_SPAWN_DO_NOT_REAP_CHILD to avoid * double-forking, otherwise syndaemon will immediately get * killed again through (PR_SET_PDEATHSIG when the intermediate * process dies */ g_spawn_async (g_get_home_dir (), (char **) args->pdata, NULL, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, setup_syndaemon, NULL, &manager->priv->syndaemon_pid, &error); manager->priv->syndaemon_spawned = (error == NULL); g_ptr_array_free (args, FALSE); if (error) { g_warning ("Failed to launch syndaemon: %s", error->message); g_error_free (error); } else { g_child_watch_add (manager->priv->syndaemon_pid, syndaemon_died, manager); g_debug ("Launched syndaemon"); } } else if (manager->priv->syndaemon_spawned) { kill (manager->priv->syndaemon_pid, SIGHUP); g_spawn_close_pid (manager->priv->syndaemon_pid); manager->priv->syndaemon_spawned = FALSE; g_debug ("Killed syndaemon"); } return 0; } static void set_tap_to_click (GdkDevice *device, gboolean state, gboolean left_handed) { int format, rc; unsigned long nitems, bytes_after; XDevice *xdevice; unsigned char* data; Atom prop, type; prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Tap Action", False); if (!prop) return; xdevice = open_gdk_device (device); if (xdevice == NULL) return; if (!device_is_touchpad (xdevice)) { xdevice_close (xdevice); return; } g_debug ("setting tap to click on %s", gdk_device_get_name (device)); gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, 0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, &data); if (rc == Success && type == XA_INTEGER && format == 8 && nitems >= 7) { /* Set MR mapping for corner tapping on the right side*/ data[0] = (state) ? 2 : 0; data[1] = (state) ? 3 : 0; /* Set RLM mapping for 1/2/3 fingers*/ data[4] = (state) ? ((left_handed) ? 3 : 1) : 0; data[5] = (state) ? ((left_handed) ? 1 : 3) : 0; data[6] = 0; /* Disable three touch tap so gestures work */ XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, XA_INTEGER, 8, PropModeReplace, data, nitems); } if (rc == Success) XFree (data); if (gdk_error_trap_pop ()) g_warning ("Error in setting tap to click on \"%s\"", gdk_device_get_name (device)); xdevice_close (xdevice); } static void set_horiz_scroll (GdkDevice *device, gboolean state) { int rc; XDevice *xdevice; Atom act_type, prop_edge, prop_twofinger; int act_format; unsigned long nitems, bytes_after; unsigned char *data; prop_edge = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Edge Scrolling", False); prop_twofinger = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Two-Finger Scrolling", False); if (!prop_edge || !prop_twofinger) return; xdevice = open_gdk_device (device); if (xdevice == NULL) return; if (!device_is_touchpad (xdevice)) { xdevice_close (xdevice); return; } g_debug ("setting horiz scroll on %s", gdk_device_get_name (device)); gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_edge, 0, 1, False, XA_INTEGER, &act_type, &act_format, &nitems, &bytes_after, &data); if (rc == Success && act_type == XA_INTEGER && act_format == 8 && nitems >= 2) { data[1] = (state && data[0]); XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_edge, XA_INTEGER, 8, PropModeReplace, data, nitems); } XFree (data); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_twofinger, 0, 1, False, XA_INTEGER, &act_type, &act_format, &nitems, &bytes_after, &data); if (rc == Success && act_type == XA_INTEGER && act_format == 8 && nitems >= 2) { data[1] = (state && data[0]); XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_twofinger, XA_INTEGER, 8, PropModeReplace, data, nitems); } if (gdk_error_trap_pop ()) g_warning ("Error in setting horiz scroll on \"%s\"", gdk_device_get_name (device)); if (rc == Success) XFree (data); xdevice_close (xdevice); } static void set_scroll_method (GsdMouseManager *manager, GdkDevice *device, GsdTouchpadScrollMethod method) { int rc; XDevice *xdevice; Atom act_type, prop, prop_edge, prop_twofinger; int act_format; unsigned long nitems, bytes_after; unsigned char *data; prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Capabilities", True); prop_edge = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Edge Scrolling", False); prop_twofinger = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Two-Finger Scrolling", False); if (!prop_edge || !prop_twofinger || !prop) return; xdevice = open_gdk_device (device); if (xdevice == NULL) return; if (!device_is_touchpad (xdevice)) { xdevice_close (xdevice); return; } g_debug ("setting edge scroll on %s", gdk_device_get_name (device)); gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, 0, 2, False, XA_INTEGER, &act_type, &act_format, &nitems, &bytes_after, &data); if (rc == Success && act_type != None) { if (!(data[3]) && method == GSD_TOUCHPAD_SCROLL_METHOD_TWO_FINGER_SCROLLING) { g_warning ("Two finger scroll is not supported by %s", gdk_device_get_name (device)); method = GSD_TOUCHPAD_SCROLL_METHOD_EDGE_SCROLLING; g_settings_set_enum (manager->priv->touchpad_settings, KEY_SCROLL_METHOD, method); } XFree (data); } rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_edge, 0, 1, False, XA_INTEGER, &act_type, &act_format, &nitems, &bytes_after, &data); if (rc == Success && act_type == XA_INTEGER && act_format == 8 && nitems >= 2) { data[0] = (method == GSD_TOUCHPAD_SCROLL_METHOD_EDGE_SCROLLING) ? 1 : 0; XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_edge, XA_INTEGER, 8, PropModeReplace, data, nitems); } XFree (data); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_twofinger, 0, 1, False, XA_INTEGER, &act_type, &act_format, &nitems, &bytes_after, &data); if (rc == Success && act_type == XA_INTEGER && act_format == 8 && nitems >= 2) { data[0] = (method == GSD_TOUCHPAD_SCROLL_METHOD_TWO_FINGER_SCROLLING) ? 1 : 0; XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_twofinger, XA_INTEGER, 8, PropModeReplace, data, nitems); } if (gdk_error_trap_pop ()) g_warning ("Error in setting edge scroll on \"%s\"", gdk_device_get_name (device)); if (rc == Success) XFree (data); xdevice_close (xdevice); } static void set_touchpad_disabled (GdkDevice *device) { int id; XDevice *xdevice; g_object_get (G_OBJECT (device), "device-id", &id, NULL); g_debug ("Trying to set device disabled for \"%s\" (%d)", gdk_device_get_name (device), id); xdevice = open_gdk_device (device); if (xdevice == NULL) return; if (!device_is_touchpad (xdevice)) { xdevice_close (xdevice); return; } if (set_device_enabled (id, FALSE) == FALSE) g_warning ("Error disabling device \"%s\" (%d)", gdk_device_get_name (device), id); else g_debug ("Disabled device \"%s\" (%d)", gdk_device_get_name (device), id); xdevice_close (xdevice); } static void set_touchpad_enabled (int id) { XDevice *xdevice; g_debug ("Trying to set device enabled for %d", id); gdk_error_trap_push (); xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id); if (gdk_error_trap_pop () != 0) return; if (!device_is_touchpad (xdevice)) { xdevice_close (xdevice); return; } if (set_device_enabled (id, TRUE) == FALSE) g_warning ("Error enabling device \"%d\"", id); else g_debug ("Enabled device %d", id); xdevice_close (xdevice); } static void set_locate_pointer (GsdMouseManager *manager, gboolean state) { if (state) { GError *error = NULL; char *args[2]; if (manager->priv->locate_pointer_spawned) return; args[0] = LIBEXECDIR "/usd-locate-pointer"; args[1] = NULL; g_spawn_async (NULL, args, NULL, 0, NULL, NULL, &manager->priv->locate_pointer_pid, &error); manager->priv->locate_pointer_spawned = (error == NULL); if (error) { g_settings_set_boolean (manager->priv->gsd_mouse_settings, KEY_LOCATE_POINTER, FALSE); g_error_free (error); } } else if (manager->priv->locate_pointer_spawned) { kill (manager->priv->locate_pointer_pid, SIGHUP); g_spawn_close_pid (manager->priv->locate_pointer_pid); manager->priv->locate_pointer_spawned = FALSE; } } static void set_mousetweaks_daemon (GsdMouseManager *manager, gboolean dwell_click_enabled, gboolean secondary_click_enabled) { GError *error = NULL; gchar *comm; gboolean run_daemon = dwell_click_enabled || secondary_click_enabled; if (run_daemon || manager->priv->mousetweaks_daemon_running) comm = g_strdup_printf ("mousetweaks %s", run_daemon ? "" : "-s"); else return; if (run_daemon) manager->priv->mousetweaks_daemon_running = TRUE; if (! g_spawn_command_line_async (comm, &error)) { if (error->code == G_SPAWN_ERROR_NOENT && run_daemon) { GtkWidget *dialog; if (dwell_click_enabled) { g_settings_set_boolean (manager->priv->mouse_a11y_settings, KEY_DWELL_CLICK_ENABLED, FALSE); } else if (secondary_click_enabled) { g_settings_set_boolean (manager->priv->mouse_a11y_settings, KEY_SECONDARY_CLICK_ENABLED, FALSE); } dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not enable mouse accessibility features")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Mouse accessibility requires Mousetweaks " "to be installed on your system.")); gtk_window_set_title (GTK_WINDOW (dialog), _("Universal Access")); gtk_window_set_icon_name (GTK_WINDOW (dialog), "preferences-desktop-accessibility"); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } g_error_free (error); } g_free (comm); } static gboolean get_touchpad_handedness (GsdMouseManager *manager, gboolean mouse_left_handed) { switch (g_settings_get_enum (manager->priv->touchpad_settings, KEY_LEFT_HANDED)) { case GSD_TOUCHPAD_HANDEDNESS_RIGHT: return FALSE; case GSD_TOUCHPAD_HANDEDNESS_LEFT: return TRUE; case GSD_TOUCHPAD_HANDEDNESS_MOUSE: return mouse_left_handed; default: g_assert_not_reached (); } } static gboolean get_touchpad_enabled (GsdMouseManager *manager) { GDesktopDeviceSendEvents send_events; send_events = g_settings_get_enum (manager->priv->touchpad_settings, KEY_SEND_EVENTS); if (send_events == G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) { /* FIXME: mouse_is_present() also finds internal ones... */ return (!mouse_is_present () && !trackball_is_present ()); } return send_events == G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED ? TRUE : FALSE; } static void set_mouse_settings (GsdMouseManager *manager, GdkDevice *device) { gboolean mouse_left_handed, touchpad_left_handed; mouse_left_handed = g_settings_get_boolean (manager->priv->mouse_settings, KEY_LEFT_HANDED); touchpad_left_handed = get_touchpad_handedness (manager, mouse_left_handed); set_left_handed (manager, device, mouse_left_handed, touchpad_left_handed); set_motion (manager, device); set_tap_to_click (device, g_settings_get_boolean (manager->priv->touchpad_settings, KEY_TAP_TO_CLICK), touchpad_left_handed); set_scroll_method (manager, device, g_settings_get_enum (manager->priv->touchpad_settings, KEY_SCROLL_METHOD)); set_horiz_scroll (device, TRUE); set_natural_scroll (manager, device, g_settings_get_boolean (manager->priv->touchpad_settings, KEY_NATURAL_SCROLL_ENABLED)); if (!get_touchpad_enabled (manager)) set_touchpad_disabled (device); } static void set_natural_scroll (GsdMouseManager *manager, GdkDevice *device, gboolean natural_scroll) { XDevice *xdevice; Atom scrolling_distance, act_type; int rc, act_format; unsigned long nitems, bytes_after; unsigned char *data; glong *ptr; xdevice = open_gdk_device (device); if (xdevice == NULL) return; if (!device_is_touchpad (xdevice)) { xdevice_close (xdevice); return; } g_debug ("Trying to set %s for \"%s\"", natural_scroll ? "natural (reverse) scroll" : "normal scroll", gdk_device_get_name (device)); scrolling_distance = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Scrolling Distance", False); gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, scrolling_distance, 0, 2, False, XA_INTEGER, &act_type, &act_format, &nitems, &bytes_after, &data); if (rc == Success && act_type == XA_INTEGER && act_format == 32 && nitems >= 2) { ptr = (glong *) data; if (natural_scroll) { ptr[0] = -abs(ptr[0]); ptr[1] = -abs(ptr[1]); } else { ptr[0] = abs(ptr[0]); ptr[1] = abs(ptr[1]); } XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, scrolling_distance, XA_INTEGER, act_format, PropModeReplace, data, nitems); } if (gdk_error_trap_pop ()) g_warning ("Error setting %s for \"%s\"", natural_scroll ? "natural (reverse) scroll" : "normal scroll", gdk_device_get_name (device)); if (rc == Success) XFree (data); xdevice_close (xdevice); } static void mouse_callback (GSettings *settings, const gchar *key, GsdMouseManager *manager) { GList *devices, *l; if (g_str_equal (key, KEY_DWELL_CLICK_ENABLED) || g_str_equal (key, KEY_SECONDARY_CLICK_ENABLED)) { set_mousetweaks_daemon (manager, g_settings_get_boolean (settings, KEY_DWELL_CLICK_ENABLED), g_settings_get_boolean (settings, KEY_SECONDARY_CLICK_ENABLED)); return; } else if (g_str_equal (key, KEY_LOCATE_POINTER)) { set_locate_pointer (manager, g_settings_get_boolean (settings, KEY_LOCATE_POINTER)); return; } devices = gdk_device_manager_list_devices (manager->priv->device_manager, GDK_DEVICE_TYPE_SLAVE); for (l = devices; l != NULL; l = l->next) { GdkDevice *device = l->data; if (device_is_ignored (manager, device)) continue; if (g_str_equal (key, KEY_LEFT_HANDED)) { gboolean mouse_left_handed; mouse_left_handed = g_settings_get_boolean (settings, KEY_LEFT_HANDED); set_left_handed (manager, device, mouse_left_handed, get_touchpad_handedness (manager, mouse_left_handed)); } else if (g_str_equal (key, KEY_SPEED)) { set_motion (manager, device); } } g_list_free (devices); } static void touchpad_callback (GSettings *settings, const gchar *key, GsdMouseManager *manager) { GList *devices, *l; devices = gdk_device_manager_list_devices (manager->priv->device_manager, GDK_DEVICE_TYPE_SLAVE); for (l = devices; l != NULL; l = l->next) { GdkDevice *device = l->data; if (device_is_ignored (manager, device)) continue; if (g_str_equal (key, KEY_TAP_TO_CLICK)) { gboolean mouse_left_handed; mouse_left_handed = g_settings_get_boolean (manager->priv->mouse_settings, KEY_LEFT_HANDED); set_tap_to_click (device, g_settings_get_boolean (settings, key), get_touchpad_handedness (manager, mouse_left_handed)); } else if (g_str_equal (key, KEY_SCROLL_METHOD)) { set_scroll_method (manager, device, g_settings_get_enum (settings, key)); set_horiz_scroll (device, TRUE); } else if (g_str_equal (key, KEY_SEND_EVENTS)) { if (!get_touchpad_enabled (manager)) set_touchpad_disabled (device); else set_touchpad_enabled (gdk_x11_device_get_id (device)); } else if (g_str_equal (key, KEY_SPEED)) { set_motion (manager, device); } else if (g_str_equal (key, KEY_LEFT_HANDED)) { gboolean mouse_left_handed; mouse_left_handed = g_settings_get_boolean (manager->priv->mouse_settings, KEY_LEFT_HANDED); set_left_handed (manager, device, mouse_left_handed, get_touchpad_handedness (manager, mouse_left_handed)); } else if (g_str_equal (key, KEY_NATURAL_SCROLL_ENABLED)) { set_natural_scroll (manager, device, g_settings_get_boolean (settings, key)); } } g_list_free (devices); if (g_str_equal (key, KEY_SEND_EVENTS) && get_touchpad_enabled (manager)) { devices = get_disabled_devices (manager->priv->device_manager); for (l = devices; l != NULL; l = l->next) { int device_id; device_id = GPOINTER_TO_INT (l->data); set_touchpad_enabled (device_id); } g_list_free (devices); } } /* Re-enable touchpad when any other pointing device isn't present. */ static void ensure_touchpad_active (GsdMouseManager *manager) { GList *devices, *l; if (get_touchpad_enabled (manager)) { devices = get_disabled_devices (manager->priv->device_manager); for (l = devices; l != NULL; l = l->next) { int device_id; device_id = GPOINTER_TO_INT (l->data); set_touchpad_enabled (device_id); } g_list_free (devices); } else { devices = gdk_device_manager_list_devices (manager->priv->device_manager, GDK_DEVICE_TYPE_SLAVE); for (l = devices; l != NULL; l = l->next) { GdkDevice *device = l->data; if (device_is_ignored (manager, device)) continue; if (gdk_device_get_source (device) != GDK_SOURCE_TOUCHPAD) continue; set_touchpad_disabled (device); } g_list_free (devices); } } static void device_added_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdMouseManager *manager) { if (device_is_ignored (manager, device) == FALSE) { if (run_custom_command (device, COMMAND_DEVICE_ADDED) == FALSE) { set_mouse_settings (manager, device); } else { int id; g_object_get (G_OBJECT (device), "device-id", &id, NULL); g_hash_table_insert (manager->priv->blacklist, GINT_TO_POINTER (id), GINT_TO_POINTER (1)); } /* If a touchpad was to appear... */ set_disable_w_typing (manager, TRUE); /* If a mouse was to appear... */ ensure_touchpad_active (manager); } } static void device_removed_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdMouseManager *manager) { int id; /* Remove the device from the hash table so that * device_is_ignored () doesn't check for blacklisted devices */ g_object_get (G_OBJECT (device), "device-id", &id, NULL); g_hash_table_remove (manager->priv->blacklist, GINT_TO_POINTER (id)); if (device_is_ignored (manager, device) == FALSE) { run_custom_command (device, COMMAND_DEVICE_REMOVED); /* If a touchpad was to disappear... */ set_disable_w_typing (manager, TRUE); if (gdk_device_get_source (device) != GDK_SOURCE_TOUCHPAD) ensure_touchpad_active (manager); } } static void set_devicepresence_handler (GsdMouseManager *manager) { GdkDeviceManager *device_manager; device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); manager->priv->device_added_id = g_signal_connect (G_OBJECT (device_manager), "device-added", G_CALLBACK (device_added_cb), manager); manager->priv->device_removed_id = g_signal_connect (G_OBJECT (device_manager), "device-removed", G_CALLBACK (device_removed_cb), manager); manager->priv->device_manager = device_manager; } static void gsd_mouse_manager_init (GsdMouseManager *manager) { manager->priv = GSD_MOUSE_MANAGER_GET_PRIVATE (manager); manager->priv->blacklist = g_hash_table_new (g_direct_hash, g_direct_equal); } static gboolean gsd_mouse_manager_idle_cb (GsdMouseManager *manager) { GList *devices, *l; gnome_settings_profile_start (NULL); set_devicepresence_handler (manager); manager->priv->gsd_mouse_settings = g_settings_new (GSD_SETTINGS_MOUSE_SCHEMA); g_signal_connect (manager->priv->gsd_mouse_settings, "changed", G_CALLBACK (mouse_callback), manager); manager->priv->mouse_a11y_settings = g_settings_new ("org.gnome.desktop.a11y.mouse"); g_signal_connect (manager->priv->mouse_a11y_settings, "changed", G_CALLBACK (mouse_callback), manager); manager->priv->mouse_settings = g_settings_new (GSETTINGS_MOUSE_SCHEMA); g_signal_connect (manager->priv->mouse_settings, "changed", G_CALLBACK (mouse_callback), manager); manager->priv->touchpad_settings = g_settings_new (GSETTINGS_TOUCHPAD_SCHEMA); g_signal_connect (manager->priv->touchpad_settings, "changed", G_CALLBACK (touchpad_callback), manager); manager->priv->syndaemon_spawned = FALSE; set_locate_pointer (manager, g_settings_get_boolean (manager->priv->gsd_mouse_settings, KEY_LOCATE_POINTER)); set_mousetweaks_daemon (manager, g_settings_get_boolean (manager->priv->mouse_a11y_settings, KEY_DWELL_CLICK_ENABLED), g_settings_get_boolean (manager->priv->mouse_a11y_settings, KEY_SECONDARY_CLICK_ENABLED)); set_disable_w_typing (manager, TRUE); devices = gdk_device_manager_list_devices (manager->priv->device_manager, GDK_DEVICE_TYPE_SLAVE); for (l = devices; l != NULL; l = l->next) { GdkDevice *device = l->data; if (device_is_ignored (manager, device)) continue; if (run_custom_command (device, COMMAND_DEVICE_PRESENT) == FALSE) { set_mouse_settings (manager, device); } else { int id; g_object_get (G_OBJECT (device), "device-id", &id, NULL); g_hash_table_insert (manager->priv->blacklist, GINT_TO_POINTER (id), GINT_TO_POINTER (1)); } } g_list_free (devices); ensure_touchpad_active (manager); if (get_touchpad_enabled (manager)) { devices = get_disabled_devices (manager->priv->device_manager); for (l = devices; l != NULL; l = l->next) { int device_id; device_id = GPOINTER_TO_INT (l->data); set_touchpad_enabled (device_id); } g_list_free (devices); } gnome_settings_profile_end (NULL); manager->priv->start_idle_id = 0; return FALSE; } gboolean gsd_mouse_manager_start (GsdMouseManager *manager, GError **error) { gnome_settings_profile_start (NULL); if (!supports_xinput_devices ()) { g_debug ("XInput is not supported, not applying any settings"); return TRUE; } manager->priv->start_idle_id = g_idle_add ((GSourceFunc) gsd_mouse_manager_idle_cb, manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_mouse_manager_stop (GsdMouseManager *manager) { GsdMouseManagerPrivate *p = manager->priv; g_debug ("Stopping mouse manager"); if (manager->priv->start_idle_id != 0) { g_source_remove (manager->priv->start_idle_id); manager->priv->start_idle_id = 0; } if (p->device_manager != NULL) { g_signal_handler_disconnect (p->device_manager, p->device_added_id); g_signal_handler_disconnect (p->device_manager, p->device_removed_id); p->device_manager = NULL; } g_clear_object (&p->mouse_a11y_settings); g_clear_object (&p->mouse_settings); g_clear_object (&p->touchpad_settings); set_locate_pointer (manager, FALSE); } static void gsd_mouse_manager_finalize (GObject *object) { GsdMouseManager *mouse_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_MOUSE_MANAGER (object)); mouse_manager = GSD_MOUSE_MANAGER (object); g_return_if_fail (mouse_manager->priv != NULL); gsd_mouse_manager_stop (mouse_manager); if (mouse_manager->priv->blacklist != NULL) g_hash_table_destroy (mouse_manager->priv->blacklist); G_OBJECT_CLASS (gsd_mouse_manager_parent_class)->finalize (object); } static GVariant * map_speed (GVariant *variant) { gdouble value; value = g_variant_get_double (variant); /* Remap from [0..10] to [-1..1] */ value = (value / 5) - 1; return g_variant_new_double (value); } static GVariant * map_send_events (GVariant *variant) { gboolean enabled; enabled = g_variant_get_boolean (variant); if (enabled) { return g_variant_new_string ("enabled"); } else { return g_variant_new_string ("disabled"); } } static void migrate_mouse_settings (void) { GsdSettingsMigrateEntry trackball_entries[] = { { "scroll-wheel-emulation-button", "scroll-wheel-emulation-button", NULL } }; GsdSettingsMigrateEntry mouse_entries[] = { { "left-handed", "left-handed", NULL }, { "motion-acceleration", "speed", map_speed }, { "motion-threshold", NULL, NULL }, { "middle-button-enabled", NULL, NULL }, }; GsdSettingsMigrateEntry touchpad_entries[] = { { "disable-while-typing", NULL, NULL }, { "horiz-scroll-enabled", NULL, NULL }, { "scroll-method", "scroll-method", NULL }, { "tap-to-click", "tap-to-click", NULL }, { "touchpad-enabled", "send-events", map_send_events }, { "left-handed", "left-handed", NULL }, { "motion-acceleration", "speed", map_speed }, { "motion-threshold", NULL, NULL }, { "natural-scroll", "natural-scroll", NULL } }; gsd_settings_migrate_check ("org.gnome.settings-daemon.peripherals.trackball.deprecated", "/org/gnome/settings-daemon/peripherals/trackball/", "org.gnome.desktop.peripherals.trackball", "/org/gnome/desktop/peripherals/trackball/", trackball_entries, G_N_ELEMENTS (trackball_entries)); gsd_settings_migrate_check ("org.gnome.settings-daemon.peripherals.mouse.deprecated", "/org/gnome/settings-daemon/peripherals/mouse/", "org.gnome.desktop.peripherals.mouse", "/org/gnome/desktop/peripherals/mouse/", mouse_entries, G_N_ELEMENTS (mouse_entries)); gsd_settings_migrate_check ("org.gnome.settings-daemon.peripherals.touchpad.deprecated", "/org/gnome/settings-daemon/peripherals/touchpad/", "org.gnome.desktop.peripherals.touchpad", "/org/gnome/desktop/peripherals/touchpad/", touchpad_entries, G_N_ELEMENTS (touchpad_entries)); } GsdMouseManager * gsd_mouse_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { migrate_mouse_settings (); manager_object = g_object_new (GSD_TYPE_MOUSE_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_MOUSE_MANAGER (manager_object); } ./plugins/mouse/mouse.gnome-settings-plugin.in0000644000004100000410000000021412735467744022012 0ustar www-datawww-data[GNOME Settings Plugin] Module=mouse IAge=0 Priority=7 _Name=Mouse _Description=Mouse plugin Authors= Copyright=Copyright © 2007 Website= ./plugins/mouse/test-mouse.c0000644000004100000410000000030512735467744016346 0ustar www-datawww-data#define NEW gsd_mouse_manager_new #define START gsd_mouse_manager_start #define STOP gsd_mouse_manager_stop #define MANAGER GsdMouseManager #include "gsd-mouse-manager.h" #include "test-plugin.h" ./plugins/mouse/gsd-locate-pointer.c0000644000004100000410000003572012735467744017752 0ustar www-datawww-data/* gsd-locate-pointer.c * * Copyright (C) 2008 Carlos Garnacho * * 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 #include "gsd-timeline.h" #include "gsd-locate-pointer.h" #include #include #include #define ANIMATION_LENGTH 750 #define WINDOW_SIZE 101 #define N_CIRCLES 4 /* All circles are supposed to be moving when progress * reaches 0.5, and each of them are supposed to long * for half of the progress, hence the need of 0.5 to * get the circles interval, and the multiplication * by 2 to know a circle progress */ #define CIRCLES_PROGRESS_INTERVAL (0.5 / N_CIRCLES) #define CIRCLE_PROGRESS(p) (MIN (1., ((gdouble) (p) * 2.))) typedef struct GsdLocatePointerData GsdLocatePointerData; struct GsdLocatePointerData { GsdTimeline *timeline; GtkWidget *widget; GdkWindow *window; gdouble progress; }; static GsdLocatePointerData *data = NULL; static void locate_pointer_paint (GsdLocatePointerData *data, cairo_t *cr, gboolean composite) { GdkRGBA color; gdouble progress, circle_progress; gint width, height, i; GtkStyleContext *context; progress = data->progress; width = gdk_window_get_width (data->window); height = gdk_window_get_height (data->window); context = gtk_widget_get_style_context (data->widget); gtk_style_context_get_background_color (context, GTK_STATE_FLAG_SELECTED, &color); cairo_set_source_rgba (cr, 1., 1., 1., 0.); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); for (i = 0; i <= N_CIRCLES; i++) { if (progress < 0.) break; circle_progress = MIN (1., (progress * 2)); progress -= CIRCLES_PROGRESS_INTERVAL; if (circle_progress >= 1.) continue; if (composite) { cairo_set_source_rgba (cr, color.red, color.green, color.blue, 1 - circle_progress); cairo_arc (cr, width / 2, height / 2, circle_progress * width / 2, 0, 2 * G_PI); cairo_fill (cr); } else { cairo_set_source_rgb (cr, 0., 0., 0.); cairo_set_line_width (cr, 3.); cairo_arc (cr, width / 2, height / 2, circle_progress * width / 2, 0, 2 * G_PI); cairo_stroke (cr); cairo_set_source_rgb (cr, 1., 1., 1.); cairo_set_line_width (cr, 1.); cairo_arc (cr, width / 2, height / 2, circle_progress * width / 2, 0, 2 * G_PI); cairo_stroke (cr); } } } static gboolean locate_pointer_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { GsdLocatePointerData *data = (GsdLocatePointerData *) user_data; if (gtk_cairo_should_draw_window (cr, data->window)) locate_pointer_paint (data, cr, gtk_widget_is_composited (data->widget)); return TRUE; } static void update_shape (GsdLocatePointerData *data) { cairo_t *cr; cairo_region_t *region; cairo_surface_t *mask; mask = cairo_image_surface_create (CAIRO_FORMAT_A1, WINDOW_SIZE, WINDOW_SIZE); cr = cairo_create (mask); locate_pointer_paint (data, cr, FALSE); region = gdk_cairo_region_create_from_surface (mask); gdk_window_shape_combine_region (data->window, region, 0, 0); cairo_region_destroy (region); cairo_destroy (cr); cairo_surface_destroy (mask); } static void timeline_frame_cb (GsdTimeline *timeline, gdouble progress, gpointer user_data) { GsdLocatePointerData *data = (GsdLocatePointerData *) user_data; GdkScreen *screen; gint cursor_x, cursor_y; if (gtk_widget_is_composited (data->widget)) { gdk_window_invalidate_rect (data->window, NULL, FALSE); data->progress = progress; } else if (progress >= data->progress + CIRCLES_PROGRESS_INTERVAL) { /* only invalidate window each circle interval */ update_shape (data); gdk_window_invalidate_rect (data->window, NULL, FALSE); data->progress += CIRCLES_PROGRESS_INTERVAL; } screen = gdk_window_get_screen (data->window); gdk_window_get_pointer (gdk_screen_get_root_window (screen), &cursor_x, &cursor_y, NULL); gdk_window_move (data->window, cursor_x - WINDOW_SIZE / 2, cursor_y - WINDOW_SIZE / 2); } static void set_transparent_shape (GdkWindow *window) { cairo_region_t *region; region = cairo_region_create (); gdk_window_shape_combine_region (data->window, region, 0, 0); cairo_region_destroy (region); } static void unset_transparent_shape (GdkWindow *window) { gdk_window_shape_combine_region (data->window, NULL, 0, 0); } static void composited_changed (GtkWidget *widget, GsdLocatePointerData *data) { if (!gtk_widget_is_composited (widget)) set_transparent_shape (data->window); else unset_transparent_shape (data->window); } static void timeline_finished_cb (GsdTimeline *timeline, gpointer user_data) { GsdLocatePointerData *data = (GsdLocatePointerData *) user_data; /* set transparent shape and hide window */ if (!gtk_widget_is_composited (data->widget)) set_transparent_shape (data->window); gtk_widget_hide (data->widget); gdk_window_hide (data->window); } static void create_window (GsdLocatePointerData *data, GdkScreen *screen) { GdkVisual *visual; GdkWindowAttr attributes; gint attributes_mask; visual = gdk_screen_get_rgba_visual (screen); if (visual == NULL) visual = gdk_screen_get_system_visual (screen); attributes_mask = GDK_WA_X | GDK_WA_Y; if (visual != NULL) attributes_mask = attributes_mask | GDK_WA_VISUAL; attributes.window_type = GDK_WINDOW_TEMP; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = visual; attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK; attributes.width = 1; attributes.height = 1; data->window = gdk_window_new (gdk_screen_get_root_window (screen), &attributes, attributes_mask); gdk_window_set_user_data (data->window, data->widget); } static GsdLocatePointerData * gsd_locate_pointer_data_new (GdkScreen *screen) { GsdLocatePointerData *data; data = g_new0 (GsdLocatePointerData, 1); /* this widget will never be shown, it's * mainly used to get signals/events from */ data->widget = gtk_invisible_new (); gtk_widget_realize (data->widget); g_signal_connect (G_OBJECT (data->widget), "draw", G_CALLBACK (locate_pointer_draw), data); data->timeline = gsd_timeline_new (ANIMATION_LENGTH); g_signal_connect (data->timeline, "frame", G_CALLBACK (timeline_frame_cb), data); g_signal_connect (data->timeline, "finished", G_CALLBACK (timeline_finished_cb), data); create_window (data, screen); return data; } static void move_locate_pointer_window (GsdLocatePointerData *data, GdkScreen *screen) { cairo_region_t *region; gint cursor_x, cursor_y; gdk_window_get_pointer (gdk_screen_get_root_window (screen), &cursor_x, &cursor_y, NULL); gdk_window_move_resize (data->window, cursor_x - WINDOW_SIZE / 2, cursor_y - WINDOW_SIZE / 2, WINDOW_SIZE, WINDOW_SIZE); /* allow events to happen through the window */ region = cairo_region_create (); gdk_window_input_shape_combine_region (data->window, region, 0, 0); cairo_region_destroy (region); } void gsd_locate_pointer (GdkScreen *screen) { if (!data) data = gsd_locate_pointer_data_new (screen); gsd_timeline_pause (data->timeline); gsd_timeline_rewind (data->timeline); /* Create again the window if it is not for the current screen */ if (gdk_screen_get_number (screen) != gdk_screen_get_number (gdk_window_get_screen (data->window))) { gdk_window_set_user_data (data->window, NULL); gdk_window_destroy (data->window); create_window (data, screen); } data->progress = 0.; g_signal_connect (data->widget, "composited-changed", G_CALLBACK (composited_changed), data); move_locate_pointer_window (data, screen); composited_changed (data->widget, data); gdk_window_show (data->window); gtk_widget_show (data->widget); gsd_timeline_start (data->timeline); } #define KEYBOARD_GROUP_SHIFT 13 #define KEYBOARD_GROUP_MASK ((1 << 13) | (1 << 14)) /* Owen magic */ static GdkFilterReturn filter (GdkXEvent *xevent, GdkEvent *event, gpointer data) { XEvent *xev = (XEvent *) xevent; guint keyval; gint group; GdkScreen *screen = (GdkScreen *)data; if (xev->type == ButtonPress) { XAllowEvents (xev->xbutton.display, ReplayPointer, xev->xbutton.time); XUngrabButton (xev->xbutton.display, AnyButton, AnyModifier, xev->xbutton.window); XUngrabKeyboard (xev->xbutton.display, xev->xbutton.time); } if (xev->type == KeyPress || xev->type == KeyRelease) { /* get the keysym */ group = (xev->xkey.state & KEYBOARD_GROUP_MASK) >> KEYBOARD_GROUP_SHIFT; gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), xev->xkey.keycode, xev->xkey.state, group, &keyval, NULL, NULL, NULL); if (keyval == GDK_KEY_Control_L || keyval == GDK_KEY_Control_R) { if (xev->type == KeyPress) { XAllowEvents (xev->xkey.display, SyncKeyboard, xev->xkey.time); XGrabButton (xev->xkey.display, AnyButton, AnyModifier, xev->xkey.window, False, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); } else { XUngrabButton (xev->xkey.display, AnyButton, AnyModifier, xev->xkey.window); XAllowEvents (xev->xkey.display, AsyncKeyboard, xev->xkey.time); gsd_locate_pointer (screen); } } else { XAllowEvents (xev->xkey.display, ReplayKeyboard, xev->xkey.time); XUngrabButton (xev->xkey.display, AnyButton, AnyModifier, xev->xkey.window); XUngrabKeyboard (xev->xkey.display, xev->xkey.time); } } return GDK_FILTER_CONTINUE; } static void set_locate_pointer (void) { GdkKeymapKey *keys; GdkDisplay *display; int n_screens; int n_keys; gboolean has_entries; static const guint keyvals[] = { GDK_KEY_Control_L, GDK_KEY_Control_R }; unsigned j; display = gdk_display_get_default (); n_screens = gdk_display_get_n_screens (display); for (j = 0 ; j < G_N_ELEMENTS (keyvals) ; j++) { has_entries = gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), keyvals[j], &keys, &n_keys); if (has_entries) { gint i, j; for (i = 0; i < n_keys; i++) { for (j = 0; j < n_screens; j++) { GdkScreen *screen; Window xroot; screen = gdk_display_get_screen (display, j); xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)); gdk_x11_display_error_trap_push (display); XGrabKey (GDK_DISPLAY_XDISPLAY (display), keys[i].keycode, 0, xroot, False, GrabModeAsync, GrabModeSync); XGrabKey (GDK_DISPLAY_XDISPLAY (display), keys[i].keycode, LockMask, xroot, False, GrabModeAsync, GrabModeSync); XGrabKey (GDK_DISPLAY_XDISPLAY (display), keys[i].keycode, Mod2Mask, xroot, False, GrabModeAsync, GrabModeSync); XGrabKey (GDK_DISPLAY_XDISPLAY (display), keys[i].keycode, Mod4Mask, xroot, False, GrabModeAsync, GrabModeSync); gdk_x11_display_error_trap_pop_ignored (display); } } g_free (keys); for (i = 0; i < n_screens; i++) { GdkScreen *screen; screen = gdk_display_get_screen (display, i); gdk_window_add_filter (gdk_screen_get_root_window (screen), filter, screen); } } } } int main (int argc, char *argv[]) { gdk_disable_multidevice (); gtk_init (&argc, &argv); set_locate_pointer (); gtk_main (); return 0; } ./plugins/mouse/gsd-mouse-manager.h0000644000004100000410000000437012735467744017567 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_MOUSE_MANAGER_H #define __GSD_MOUSE_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_MOUSE_MANAGER (gsd_mouse_manager_get_type ()) #define GSD_MOUSE_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_MOUSE_MANAGER, GsdMouseManager)) #define GSD_MOUSE_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_MOUSE_MANAGER, GsdMouseManagerClass)) #define GSD_IS_MOUSE_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_MOUSE_MANAGER)) #define GSD_IS_MOUSE_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_MOUSE_MANAGER)) #define GSD_MOUSE_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_MOUSE_MANAGER, GsdMouseManagerClass)) typedef struct GsdMouseManagerPrivate GsdMouseManagerPrivate; typedef struct { GObject parent; GsdMouseManagerPrivate *priv; } GsdMouseManager; typedef struct { GObjectClass parent_class; } GsdMouseManagerClass; GType gsd_mouse_manager_get_type (void); GsdMouseManager * gsd_mouse_manager_new (void); gboolean gsd_mouse_manager_start (GsdMouseManager *manager, GError **error); void gsd_mouse_manager_stop (GsdMouseManager *manager); G_END_DECLS #endif /* __GSD_MOUSE_MANAGER_H */ ./plugins/mouse/gsd-timeline.h0000644000004100000410000001210212735467744016625 0ustar www-datawww-data/* gsdtimeline.c * * Copyright (C) 2008 Carlos Garnacho * * 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. */ #ifndef __GSD_TIMELINE_H__ #define __GSD_TIMELINE_H__ #include #include G_BEGIN_DECLS #define GSD_TYPE_TIMELINE_DIRECTION (gsd_timeline_direction_get_type ()) #define GSD_TYPE_TIMELINE_PROGRESS_TYPE (gsd_timeline_progress_type_get_type ()) #define GSD_TYPE_TIMELINE (gsd_timeline_get_type ()) #define GSD_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_TIMELINE, GsdTimeline)) #define GSD_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_TIMELINE, GsdTimelineClass)) #define GSD_IS_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_TIMELINE)) #define GSD_IS_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_TIMELINE)) #define GSD_TIMELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSD_TYPE_TIMELINE, GsdTimelineClass)) typedef enum { GSD_TIMELINE_DIRECTION_FORWARD, GSD_TIMELINE_DIRECTION_BACKWARD } GsdTimelineDirection; typedef enum { GSD_TIMELINE_PROGRESS_LINEAR, GSD_TIMELINE_PROGRESS_SINUSOIDAL, GSD_TIMELINE_PROGRESS_EXPONENTIAL } GsdTimelineProgressType; typedef struct GsdTimeline GsdTimeline; typedef struct GsdTimelineClass GsdTimelineClass; struct GsdTimeline { GObject parent_instance; }; struct GsdTimelineClass { GObjectClass parent_class; void (* started) (GsdTimeline *timeline); void (* finished) (GsdTimeline *timeline); void (* paused) (GsdTimeline *timeline); void (* frame) (GsdTimeline *timeline, gdouble progress); void (* __gsd_reserved1) (void); void (* __gsd_reserved2) (void); void (* __gsd_reserved3) (void); void (* __gsd_reserved4) (void); }; typedef gdouble (*GsdTimelineProgressFunc) (gdouble progress); GType gsd_timeline_get_type (void) G_GNUC_CONST; GType gsd_timeline_direction_get_type (void) G_GNUC_CONST; GType gsd_timeline_progress_type_get_type (void) G_GNUC_CONST; GsdTimeline *gsd_timeline_new (guint duration); GsdTimeline *gsd_timeline_new_for_screen (guint duration, GdkScreen *screen); void gsd_timeline_start (GsdTimeline *timeline); void gsd_timeline_pause (GsdTimeline *timeline); void gsd_timeline_rewind (GsdTimeline *timeline); gboolean gsd_timeline_is_running (GsdTimeline *timeline); guint gsd_timeline_get_fps (GsdTimeline *timeline); void gsd_timeline_set_fps (GsdTimeline *timeline, guint fps); gboolean gsd_timeline_get_loop (GsdTimeline *timeline); void gsd_timeline_set_loop (GsdTimeline *timeline, gboolean loop); guint gsd_timeline_get_duration (GsdTimeline *timeline); void gsd_timeline_set_duration (GsdTimeline *timeline, guint duration); GdkScreen *gsd_timeline_get_screen (GsdTimeline *timeline); void gsd_timeline_set_screen (GsdTimeline *timeline, GdkScreen *screen); GsdTimelineDirection gsd_timeline_get_direction (GsdTimeline *timeline); void gsd_timeline_set_direction (GsdTimeline *timeline, GsdTimelineDirection direction); GsdTimelineProgressType gsd_timeline_get_progress_type (GsdTimeline *timeline); void gsd_timeline_set_progress_type (GsdTimeline *timeline, GsdTimelineProgressType type); void gsd_timeline_get_progress_func (GsdTimeline *timeline); void gsd_timeline_set_progress_func (GsdTimeline *timeline, GsdTimelineProgressFunc progress_func); gdouble gsd_timeline_get_progress (GsdTimeline *timeline); G_END_DECLS #endif /* __GSD_TIMELINE_H__ */ ./plugins/housekeeping/0000755000004100000410000000000012735467744015435 5ustar www-datawww-data./plugins/housekeeping/gsd-disk-space-helper.c0000644000004100000410000000667212735467744021667 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * vim: set et sw=8 ts=8: * * Copyright (c) 2008, Novell, Inc. * Copyright (c) 2012, Red Hat, Inc. * * Authors: Vincent Untz * Bastien Nocera * * 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 "config.h" #include #include #include "gsd-disk-space-helper.h" gboolean gsd_should_ignore_unix_mount (GUnixMountEntry *mount) { const char *fs, *device; guint i; /* This is borrowed from GLib and used as a way to determine * which mounts we should ignore by default. GLib doesn't * expose this in a way that allows it to be used for this * purpose */ /* We also ignore network filesystems */ const gchar *ignore_fs[] = { "adfs", "afs", "auto", "autofs", "autofs4", "cifs", "cxfs", "devfs", "devpts", "ecryptfs", "fdescfs", "gfs", "gfs2", "kernfs", "linprocfs", "linsysfs", "lustre", "lustre_lite", "ncpfs", "nfs", "nfs4", "nfsd", "ocfs2", "proc", "procfs", "ptyfs", "rpc_pipefs", "selinuxfs", "smbfs", "sysfs", "tmpfs", "usbfs", "zfs", NULL }; const gchar *ignore_devices[] = { "none", "sunrpc", "devpts", "nfsd", "/dev/loop", "/dev/vn", NULL }; fs = g_unix_mount_get_fs_type (mount); device = g_unix_mount_get_device_path (mount); for (i = 0; ignore_fs[i] != NULL; i++) if (g_str_equal (ignore_fs[i], fs)) return TRUE; for (i = 0; ignore_devices[i] != NULL; i++) if (g_str_equal (ignore_devices[i], device)) return TRUE; return FALSE; } gboolean gsd_is_removable_mount (GUnixMountEntry *mount) { const char *mount_path; char *path; mount_path = g_unix_mount_get_mount_path (mount); if (mount_path == NULL) return FALSE; path = g_strdup_printf ("/run/media/%s", g_get_user_name ()); if (g_str_has_prefix (mount_path, path)) { g_free (path); return TRUE; } g_free (path); return FALSE; } ./plugins/housekeeping/Makefile.am0000644000004100000410000000461412735467744017476 0ustar www-datawww-dataplugin_name = housekeeping COMMON_FILES = \ gsd-disk-space.c \ gsd-disk-space.h \ gsd-ldsm-dialog.c \ gsd-ldsm-dialog.h \ gsd-disk-space-helper.h \ gsd-disk-space-helper.c noinst_PROGRAMS = gsd-disk-space-test gsd-empty-trash-test gsd_disk_space_test_SOURCES = \ gsd-disk-space-test.c \ $(COMMON_FILES) gsd_disk_space_test_LDADD = $(SETTINGS_PLUGIN_LIBS) $(GIOUNIX_LIBS) $(LIBNOTIFY_LIBS) gsd_disk_space_test_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(GIOUNIX_CFLAGS) \ $(LIBNOTIFY_CFLAGS) \ $(AM_CFLAGS) gsd_empty_trash_test_SOURCES = \ gsd-empty-trash-test.c \ $(COMMON_FILES) gsd_empty_trash_test_LDADD = $(SETTINGS_PLUGIN_LIBS) $(GIOUNIX_LIBS) $(LIBNOTIFY_LIBS) gsd_empty_trash_test_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(GIOUNIX_CFLAGS) \ $(LIBNOTIFY_CFLAGS) \ $(AM_CFLAGS) libexec_PROGRAMS = usd-test-housekeeping usd_test_housekeeping_SOURCES = \ test-housekeeping.c \ gsd-housekeeping-manager.c \ gsd-housekeeping-manager.h \ $(COMMON_FILES) usd_test_housekeeping_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) usd_test_housekeeping_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(MOUSE_CFLAGS) \ $(AM_CFLAGS) usd_test_housekeeping_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_LTLIBRARIES = libhousekeeping.la libhousekeeping_la_SOURCES = \ $(COMMON_FILES) \ gsd-housekeeping-manager.c \ gsd-housekeeping-manager.h \ gsd-housekeeping-plugin.c libhousekeeping_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libhousekeeping_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(GIOUNIX_CFLAGS) \ $(LIBNOTIFY_CFLAGS) \ $(AM_CFLAGS) libhousekeeping_la_LDFLAGS = $(GSD_PLUGIN_LDFLAGS) libhousekeeping_la_LIBADD = $(SETTINGS_PLUGIN_LIBS) $(GIOUNIX_LIBS) $(LIBNOTIFY_LIBS) plugin_in_files = housekeeping.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = $(plugin_in_files) CLEANFILES = $(plugin_DATA) DISTCLEANFILES = (plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/housekeeping/gsd-ldsm-dialog.c0000644000004100000410000005052312735467744020555 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * gsd-ldsm-dialog.c * Copyright (C) Chris Coulson 2009 * * 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 "config.h" #include #include #include "gsd-ldsm-dialog.h" #define SETTINGS_HOUSEKEEPING_DIR "org.gnome.settings-daemon.plugins.housekeeping" enum { PROP_0, PROP_OTHER_USABLE_PARTITIONS, PROP_OTHER_PARTITIONS, PROP_HAS_TRASH, PROP_SPACE_REMAINING, PROP_PARTITION_NAME, PROP_MOUNT_PATH }; struct GsdLdsmDialogPrivate { GtkWidget *primary_label; GtkWidget *secondary_label; GtkWidget *ignore_check_button; gboolean other_usable_partitions; gboolean other_partitions; gboolean has_trash; gint64 space_remaining; gchar *partition_name; gchar *mount_path; }; #define GSD_LDSM_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_LDSM_DIALOG, GsdLdsmDialogPrivate)) static void gsd_ldsm_dialog_class_init (GsdLdsmDialogClass *klass); static void gsd_ldsm_dialog_init (GsdLdsmDialog *dialog); G_DEFINE_TYPE (GsdLdsmDialog, gsd_ldsm_dialog, GTK_TYPE_DIALOG); static const gchar* gsd_ldsm_dialog_get_checkbutton_text (GsdLdsmDialog *dialog) { g_return_val_if_fail (GSD_IS_LDSM_DIALOG (dialog), NULL); if (dialog->priv->other_partitions) return _("Don't show any warnings again for this file system"); else return _("Don't show any warnings again"); } static gchar* gsd_ldsm_dialog_get_primary_text (GsdLdsmDialog *dialog) { gchar *primary_text, *free_space; g_return_val_if_fail (GSD_IS_LDSM_DIALOG (dialog), NULL); free_space = g_format_size (dialog->priv->space_remaining); if (dialog->priv->other_partitions) { primary_text = g_strdup_printf (_("The volume \"%s\" has only %s disk space remaining."), dialog->priv->partition_name, free_space); } else { primary_text = g_strdup_printf (_("This computer has only %s disk space remaining."), free_space); } g_free (free_space); return primary_text; } static const gchar* gsd_ldsm_dialog_get_secondary_text (GsdLdsmDialog *dialog) { g_return_val_if_fail (GSD_IS_LDSM_DIALOG (dialog), NULL); if (dialog->priv->other_usable_partitions) { if (dialog->priv->has_trash) { return _("You can free up disk space by emptying the Trash, removing " \ "unused programs or files, or moving files to another disk or partition."); } else { return _("You can free up disk space by removing unused programs or files, " \ "or by moving files to another disk or partition."); } } else { if (dialog->priv->has_trash) { return _("You can free up disk space by emptying the Trash, removing unused " \ "programs or files, or moving files to an external disk."); } else { return _("You can free up disk space by removing unused programs or files, " \ "or by moving files to an external disk."); } } } static gint ignore_path_compare (gconstpointer a, gconstpointer b) { return g_strcmp0 ((const gchar *)a, (const gchar *)b); } static gboolean update_ignore_paths (GSList **ignore_paths, const gchar *mount_path, gboolean ignore) { GSList *found; gchar *path_to_remove; found = g_slist_find_custom (*ignore_paths, mount_path, (GCompareFunc) ignore_path_compare); if (ignore && (found == NULL)) { *ignore_paths = g_slist_prepend (*ignore_paths, g_strdup (mount_path)); return TRUE; } if (!ignore && (found != NULL)) { path_to_remove = found->data; *ignore_paths = g_slist_remove (*ignore_paths, path_to_remove); g_free (path_to_remove); return TRUE; } return FALSE; } static void ignore_check_button_toggled_cb (GtkToggleButton *button, gpointer user_data) { GsdLdsmDialog *dialog = (GsdLdsmDialog *)user_data; GSettings *settings; gchar **settings_list; gboolean ignore, updated; gint i; GSList *ignore_paths = NULL; settings = g_settings_new (SETTINGS_HOUSEKEEPING_DIR); settings_list = g_settings_get_strv (settings, "ignore-paths"); for (i = 0; i < G_N_ELEMENTS (settings_list); i++) { if (settings_list[i] != NULL) ignore_paths = g_slist_append (ignore_paths, g_strdup (settings_list[i])); } ignore = gtk_toggle_button_get_active (button); updated = update_ignore_paths (&ignore_paths, dialog->priv->mount_path, ignore); g_strfreev (settings_list); if (updated) { GSList *l; GPtrArray *array = g_ptr_array_new (); for (l = ignore_paths; l != NULL; l = l->next) g_ptr_array_add (array, l->data); g_ptr_array_add (array, NULL); if (!g_settings_set_strv (settings, "ignore-paths", (const gchar **) array->pdata)) { g_warning ("Cannot change ignore preference - failed to commit changes"); } g_ptr_array_free (array, FALSE); } g_slist_foreach (ignore_paths, (GFunc) g_free, NULL); g_slist_free (ignore_paths); g_object_unref (settings); } static void gsd_ldsm_dialog_init (GsdLdsmDialog *dialog) { GtkWidget *main_vbox, *text_vbox, *hbox; GtkWidget *image; dialog->priv = GSD_LDSM_DIALOG_GET_PRIVATE (dialog); main_vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); /* Set up all the window stuff here */ gtk_window_set_title (GTK_WINDOW (dialog), _("Low Disk Space")); gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_DIALOG_WARNING); gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_window_set_urgency_hint (GTK_WINDOW (dialog), TRUE); gtk_window_set_focus_on_map (GTK_WINDOW (dialog), FALSE); gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); /* Create the image */ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); /* Create the labels */ dialog->priv->primary_label = gtk_label_new (NULL); gtk_label_set_line_wrap (GTK_LABEL (dialog->priv->primary_label), TRUE); gtk_label_set_single_line_mode (GTK_LABEL (dialog->priv->primary_label), FALSE); gtk_misc_set_alignment (GTK_MISC (dialog->priv->primary_label), 0.0, 0.0); gtk_label_set_max_width_chars (GTK_LABEL (dialog->priv->primary_label), 72); dialog->priv->secondary_label = gtk_label_new (NULL); gtk_label_set_line_wrap (GTK_LABEL (dialog->priv->secondary_label), TRUE); gtk_label_set_single_line_mode (GTK_LABEL (dialog->priv->secondary_label), FALSE); gtk_misc_set_alignment (GTK_MISC (dialog->priv->secondary_label), 0.0, 0.0); gtk_label_set_max_width_chars (GTK_LABEL (dialog->priv->secondary_label), 72); /* Create the check button to ignore future warnings */ dialog->priv->ignore_check_button = gtk_check_button_new (); /* The button should be inactive if the dialog was just called. * I suppose it could be possible for the user to manually edit the GSettings key between * the mount being checked and the dialog appearing, but I don't think it matters * too much */ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->ignore_check_button), FALSE); g_signal_connect (dialog->priv->ignore_check_button, "toggled", G_CALLBACK (ignore_check_button_toggled_cb), dialog); /* Now set up the dialog's GtkBox's' */ gtk_box_set_spacing (GTK_BOX (main_vbox), 14); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); text_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_box_pack_start (GTK_BOX (text_vbox), dialog->priv->primary_label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (text_vbox), dialog->priv->secondary_label, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (text_vbox), dialog->priv->ignore_check_button, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), text_vbox, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); /* Set up the action area */ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dialog))), 6); gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (dialog))), 5); gtk_widget_show_all (hbox); } static void gsd_ldsm_dialog_finalize (GObject *object) { GsdLdsmDialog *self; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_LDSM_DIALOG (object)); self = GSD_LDSM_DIALOG (object); if (self->priv->partition_name) g_free (self->priv->partition_name); if (self->priv->mount_path) g_free (self->priv->mount_path); G_OBJECT_CLASS (gsd_ldsm_dialog_parent_class)->finalize (object); } static void gsd_ldsm_dialog_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GsdLdsmDialog *self; g_return_if_fail (GSD_IS_LDSM_DIALOG (object)); self = GSD_LDSM_DIALOG (object); switch (prop_id) { case PROP_OTHER_USABLE_PARTITIONS: self->priv->other_usable_partitions = g_value_get_boolean (value); break; case PROP_OTHER_PARTITIONS: self->priv->other_partitions = g_value_get_boolean (value); break; case PROP_HAS_TRASH: self->priv->has_trash = g_value_get_boolean (value); break; case PROP_SPACE_REMAINING: self->priv->space_remaining = g_value_get_int64 (value); break; case PROP_PARTITION_NAME: self->priv->partition_name = g_value_dup_string (value); break; case PROP_MOUNT_PATH: self->priv->mount_path = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_ldsm_dialog_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsdLdsmDialog *self; g_return_if_fail (GSD_IS_LDSM_DIALOG (object)); self = GSD_LDSM_DIALOG (object); switch (prop_id) { case PROP_OTHER_USABLE_PARTITIONS: g_value_set_boolean (value, self->priv->other_usable_partitions); break; case PROP_OTHER_PARTITIONS: g_value_set_boolean (value, self->priv->other_partitions); break; case PROP_HAS_TRASH: g_value_set_boolean (value, self->priv->has_trash); break; case PROP_SPACE_REMAINING: g_value_set_int64 (value, self->priv->space_remaining); break; case PROP_PARTITION_NAME: g_value_set_string (value, self->priv->partition_name); break; case PROP_MOUNT_PATH: g_value_set_string (value, self->priv->mount_path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_ldsm_dialog_class_init (GsdLdsmDialogClass *klass) { GObjectClass* object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_ldsm_dialog_finalize; object_class->set_property = gsd_ldsm_dialog_set_property; object_class->get_property = gsd_ldsm_dialog_get_property; g_object_class_install_property (object_class, PROP_OTHER_USABLE_PARTITIONS, g_param_spec_boolean ("other-usable-partitions", "other-usable-partitions", "Set to TRUE if there are other usable partitions on the system", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_OTHER_PARTITIONS, g_param_spec_boolean ("other-partitions", "other-partitions", "Set to TRUE if there are other partitions on the system", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_HAS_TRASH, g_param_spec_boolean ("has-trash", "has-trash", "Set to TRUE if the partition has files in it's trash folder that can be deleted", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SPACE_REMAINING, g_param_spec_int64 ("space-remaining", "space-remaining", "Specify how much space is remaining in bytes", G_MININT64, G_MAXINT64, 0, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_PARTITION_NAME, g_param_spec_string ("partition-name", "partition-name", "Specify the name of the partition", "Unknown", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_MOUNT_PATH, g_param_spec_string ("mount-path", "mount-path", "Specify the mount path for the partition", "Unknown", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (GsdLdsmDialogPrivate)); } GsdLdsmDialog* gsd_ldsm_dialog_new (gboolean other_usable_partitions, gboolean other_partitions, gboolean display_baobab, gboolean display_empty_trash, gint64 space_remaining, const gchar *partition_name, const gchar *mount_path) { GsdLdsmDialog *dialog; GtkWidget *button_empty_trash, *button_ignore, *button_analyze; GtkWidget *empty_trash_image, *analyze_image, *ignore_image; gchar *primary_text, *primary_text_markup; const gchar *secondary_text, *checkbutton_text; dialog = GSD_LDSM_DIALOG (g_object_new (GSD_TYPE_LDSM_DIALOG, "other-usable-partitions", other_usable_partitions, "other-partitions", other_partitions, "has-trash", display_empty_trash, "space-remaining", space_remaining, "partition-name", partition_name, "mount-path", mount_path, NULL)); /* Add some buttons */ if (dialog->priv->has_trash) { button_empty_trash = gtk_dialog_add_button (GTK_DIALOG (dialog), _("Empty Trash"), GSD_LDSM_DIALOG_RESPONSE_EMPTY_TRASH); empty_trash_image = gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (button_empty_trash), empty_trash_image); } if (display_baobab) { button_analyze = gtk_dialog_add_button (GTK_DIALOG (dialog), _("Examine…"), GSD_LDSM_DIALOG_RESPONSE_ANALYZE); analyze_image = gtk_image_new_from_icon_name ("baobab", GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (button_analyze), analyze_image); } button_ignore = gtk_dialog_add_button (GTK_DIALOG (dialog), _("Ignore"), GTK_RESPONSE_CANCEL); ignore_image = gtk_image_new_from_stock (GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (button_ignore), ignore_image); gtk_widget_grab_default (button_ignore); /* Set the label text */ primary_text = gsd_ldsm_dialog_get_primary_text (dialog); primary_text_markup = g_markup_printf_escaped ("%s", primary_text); gtk_label_set_markup (GTK_LABEL (dialog->priv->primary_label), primary_text_markup); secondary_text = gsd_ldsm_dialog_get_secondary_text (dialog); gtk_label_set_text (GTK_LABEL (dialog->priv->secondary_label), secondary_text); checkbutton_text = gsd_ldsm_dialog_get_checkbutton_text (dialog); gtk_button_set_label (GTK_BUTTON (dialog->priv->ignore_check_button), checkbutton_text); g_free (primary_text); g_free (primary_text_markup); return dialog; } ./plugins/housekeeping/gsd-disk-space.c0000644000004100000410000011470612735467744020410 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * vim: set et sw=8 ts=8: * * Copyright (c) 2008, Novell, Inc. * * Authors: Vincent Untz * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include "gsd-disk-space.h" #include "gsd-ldsm-dialog.h" #include "gsd-disk-space-helper.h" #define GIGABYTE 1024 * 1024 * 1024 #define CHECK_EVERY_X_SECONDS 60 #define DISK_SPACE_ANALYZER "baobab" #define SETTINGS_HOUSEKEEPING_DIR "org.gnome.settings-daemon.plugins.housekeeping" #define SETTINGS_FREE_PC_NOTIFY_KEY "free-percent-notify" #define SETTINGS_FREE_PC_NOTIFY_AGAIN_KEY "free-percent-notify-again" #define SETTINGS_FREE_SIZE_NO_NOTIFY "free-size-gb-no-notify" #define SETTINGS_MIN_NOTIFY_PERIOD "min-notify-period" #define SETTINGS_IGNORE_PATHS "ignore-paths" #define PRIVACY_SETTINGS "org.gnome.desktop.privacy" #define SETTINGS_PURGE_TRASH "remove-old-trash-files" #define SETTINGS_PURGE_TEMP_FILES "remove-old-temp-files" #define SETTINGS_PURGE_AFTER "old-files-age" typedef struct { GUnixMountEntry *mount; struct statvfs buf; time_t notify_time; } LdsmMountInfo; static GHashTable *ldsm_notified_hash = NULL; static unsigned int ldsm_timeout_id = 0; static GUnixMountMonitor *ldsm_monitor = NULL; static double free_percent_notify = 0.05; static double free_percent_notify_again = 0.01; static unsigned int free_size_gb_no_notify = 2; static unsigned int min_notify_period = 10; static GSList *ignore_paths = NULL; static GSettings *settings = NULL; static GSettings *privacy_settings = NULL; static GsdLdsmDialog *dialog = NULL; static NotifyNotification *notification = NULL; static guint64 *time_read; static gboolean purge_trash; static gboolean purge_temp_files; static guint purge_after; static guint purge_trash_id = 0; static guint purge_temp_id = 0; static gchar* ldsm_get_fs_id_for_path (const gchar *path) { GFile *file; GFileInfo *fileinfo; gchar *attr_id_fs; file = g_file_new_for_path (path); fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_ID_FILESYSTEM, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); if (fileinfo) { attr_id_fs = g_strdup (g_file_info_get_attribute_string (fileinfo, G_FILE_ATTRIBUTE_ID_FILESYSTEM)); g_object_unref (fileinfo); } else { attr_id_fs = NULL; } g_object_unref (file); return attr_id_fs; } static gboolean ldsm_mount_has_trash (LdsmMountInfo *mount) { const gchar *user_data_dir; gchar *user_data_attr_id_fs; gchar *path_attr_id_fs; gboolean mount_uses_user_trash = FALSE; gchar *trash_files_dir; gboolean has_trash = FALSE; GDir *dir; const gchar *path; user_data_dir = g_get_user_data_dir (); user_data_attr_id_fs = ldsm_get_fs_id_for_path (user_data_dir); path = g_unix_mount_get_mount_path (mount->mount); path_attr_id_fs = ldsm_get_fs_id_for_path (path); if (g_strcmp0 (user_data_attr_id_fs, path_attr_id_fs) == 0) { /* The volume that is low on space is on the same volume as our home * directory. This means the trash is at $XDG_DATA_HOME/Trash, * not at the root of the volume which is full. */ mount_uses_user_trash = TRUE; } g_free (user_data_attr_id_fs); g_free (path_attr_id_fs); /* I can't think of a better way to find out if a volume has any trash. Any suggestions? */ if (mount_uses_user_trash) { trash_files_dir = g_build_filename (g_get_user_data_dir (), "Trash", "files", NULL); } else { gchar *uid; uid = g_strdup_printf ("%d", getuid ()); trash_files_dir = g_build_filename (path, ".Trash", uid, "files", NULL); if (!g_file_test (trash_files_dir, G_FILE_TEST_IS_DIR)) { gchar *trash_dir; g_free (trash_files_dir); trash_dir = g_strdup_printf (".Trash-%s", uid); trash_files_dir = g_build_filename (path, trash_dir, "files", NULL); g_free (trash_dir); if (!g_file_test (trash_files_dir, G_FILE_TEST_IS_DIR)) { g_free (trash_files_dir); g_free (uid); return has_trash; } } g_free (uid); } dir = g_dir_open (trash_files_dir, 0, NULL); if (dir) { if (g_dir_read_name (dir)) has_trash = TRUE; g_dir_close (dir); } g_free (trash_files_dir); return has_trash; } static void ldsm_analyze_path (const gchar *path) { const gchar *argv[] = { DISK_SPACE_ANALYZER, path, NULL }; g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); } static gboolean server_has_actions (void) { gboolean has; GList *caps; GList *l; caps = notify_get_server_caps (); if (caps == NULL) { fprintf (stderr, "Failed to receive server caps.\n"); return FALSE; } l = g_list_find_custom (caps, "actions", (GCompareFunc)strcmp); has = l != NULL; g_list_foreach (caps, (GFunc) g_free, NULL); g_list_free (caps); return has; } static void ignore_callback (NotifyNotification *n, const char *action) { g_assert (action != NULL); g_assert (strcmp (action, "ignore") == 0); /* Do nothing */ notify_notification_close (n, NULL); } static void examine_callback (NotifyNotification *n, const char *action, const char *path) { g_assert (action != NULL); g_assert (strcmp (action, "examine") == 0); ldsm_analyze_path (path); notify_notification_close (n, NULL); } static gboolean should_purge_file (GFile *file, GCancellable *cancellable, GDateTime *old) { GFileInfo *info; GDateTime *date; gboolean should_purge; should_purge = FALSE; info = g_file_query_info (file, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE "," G_FILE_ATTRIBUTE_UNIX_UID "," G_FILE_ATTRIBUTE_TIME_CHANGED, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, NULL); date = g_file_info_get_deletion_date (info); if (date == NULL) { guint uid; guint64 ctime; uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID); if (uid != getuid ()) { should_purge = FALSE; goto out; } ctime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED); date = g_date_time_new_from_unix_local ((gint64) ctime); } should_purge = g_date_time_difference (old, date) >= 0; g_date_time_unref (date); out: g_object_unref (info); return should_purge; } DeleteData * delete_data_new (GFile *file, GCancellable *cancellable, GDateTime *old, gboolean dry_run, gboolean trash, gint depth) { DeleteData *data; data = g_new (DeleteData, 1); data->ref_count = 1; data->file = g_object_ref (file); data->cancellable = cancellable ? g_object_ref (cancellable) : NULL; data->old = g_date_time_ref (old); data->dry_run = dry_run; data->trash = trash; data->depth = depth; data->name = g_file_get_parse_name (data->file); return data; } static DeleteData * delete_data_ref (DeleteData *data) { data->ref_count += 1; return data; } void delete_data_unref (DeleteData *data) { data->ref_count -= 1; if (data->ref_count > 0) return; g_object_unref (data->file); if (data->cancellable) g_object_unref (data->cancellable); g_date_time_unref (data->old); g_free (data->name); g_free (data); } static void delete_batch (GObject *source, GAsyncResult *res, gpointer user_data) { GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source); DeleteData *data = user_data; GList *files, *f; GFile *child_file; DeleteData *child; GFileInfo *info; GError *error = NULL; files = g_file_enumerator_next_files_finish (enumerator, res, &error); g_debug ("GsdHousekeeping: purging %d children of %s", g_list_length (files), data->name); if (files) { for (f = files; f; f = f->next) { if (g_cancellable_is_cancelled (data->cancellable)) break; info = f->data; child_file = g_file_get_child (data->file, g_file_info_get_name (info)); child = delete_data_new (child_file, data->cancellable, data->old, data->dry_run, data->trash, data->depth + 1); delete_recursively_by_age (child); delete_data_unref (child); g_object_unref (child_file); } g_list_free_full (files, g_object_unref); if (!g_cancellable_is_cancelled (data->cancellable)) { g_file_enumerator_next_files_async (enumerator, 20, 0, data->cancellable, delete_batch, data); return; } } g_file_enumerator_close (enumerator, data->cancellable, NULL); g_object_unref (enumerator); if (data->depth > 0 && !g_cancellable_is_cancelled (data->cancellable)) { if ((data->trash && data->depth > 1) || should_purge_file (data->file, data->cancellable, data->old)) { g_debug ("GsdHousekeeping: purging %s\n", data->name); if (!data->dry_run) { g_file_delete (data->file, data->cancellable, NULL); } } } delete_data_unref (data); } static void delete_subdir (GObject *source, GAsyncResult *res, gpointer user_data) { GFile *file = G_FILE (source); DeleteData *data = user_data; GFileEnumerator *enumerator; GError *error = NULL; g_debug ("GsdHousekeeping: purging %s in %s\n", data->trash ? "trash" : "temporary files", data->name); enumerator = g_file_enumerate_children_finish (file, res, &error); if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)) g_warning ("Failed to enumerate children of %s: %s\n", data->name, error->message); } if (enumerator) { g_file_enumerator_next_files_async (enumerator, 20, 0, data->cancellable, delete_batch, delete_data_ref (data)); } else if (data->depth > 0 && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)) { if ((data->trash && data->depth > 1) || should_purge_file (data->file, data->cancellable, data->old)) { if (!data->dry_run) { g_file_delete (data->file, data->cancellable, NULL); } } } if (error) g_error_free (error); delete_data_unref (data); } static void delete_subdir_check_symlink (GObject *source, GAsyncResult *res, gpointer user_data) { GFile *file = G_FILE (source); DeleteData *data = user_data; GFileInfo *info; GFileType type; info = g_file_query_info_finish (file, res, NULL); if (!info) { delete_data_unref (data); return; } type = g_file_info_get_file_type (info); g_object_unref (info); if (type == G_FILE_TYPE_SYMBOLIC_LINK) { if (should_purge_file (data->file, data->cancellable, data->old)) { g_debug ("Purging %s leaf node", data->name); if (!data->dry_run) { g_file_delete (data->file, data->cancellable, NULL); } } } else { g_file_enumerate_children_async (data->file, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, 0, data->cancellable, delete_subdir, delete_data_ref (data)); } delete_data_unref (data); } void delete_recursively_by_age (DeleteData *data) { if (data->trash && (data->depth == 1) && !should_purge_file (data->file, data->cancellable, data->old)) { /* no need to recurse into trashed directories */ return; } g_file_query_info_async (data->file, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, 0, data->cancellable, delete_subdir_check_symlink, delete_data_ref (data)); } void gsd_ldsm_purge_trash (GDateTime *old) { GFile *file; DeleteData *data; file = g_file_new_for_uri ("trash:"); data = delete_data_new (file, NULL, old, FALSE, TRUE, 0); delete_recursively_by_age (data); delete_data_unref (data); g_object_unref (file); } void gsd_ldsm_purge_temp_files (GDateTime *old) { DeleteData *data; GFile *file; file = g_file_new_for_path (g_get_tmp_dir ()); data = delete_data_new (file, NULL, old, FALSE, FALSE, 0); delete_recursively_by_age (data); delete_data_unref (data); g_object_unref (file); if (g_strcmp0 (g_get_tmp_dir (), "/var/tmp") != 0) { file = g_file_new_for_path ("/var/tmp"); data = delete_data_new (file, NULL, old, FALSE, FALSE, 0); delete_recursively_by_age (data); delete_data_unref (data); g_object_unref (file); } if (g_strcmp0 (g_get_tmp_dir (), "/tmp") != 0) { file = g_file_new_for_path ("/tmp"); data = delete_data_new (file, NULL, old, FALSE, FALSE, 0); delete_recursively_by_age (data); delete_data_unref (data); g_object_unref (file); } } void gsd_ldsm_show_empty_trash (void) { GFile *file; GDateTime *old; DeleteData *data; old = g_date_time_new_now_local (); file = g_file_new_for_uri ("trash:"); data = delete_data_new (file, NULL, old, TRUE, TRUE, 0); g_object_unref (file); g_date_time_unref (old); delete_recursively_by_age (data); delete_data_unref (data); } static gboolean ldsm_purge_trash_and_temp (gpointer data) { GDateTime *now, *old; now = g_date_time_new_now_local (); old = g_date_time_add_days (now, - purge_after); if (purge_trash) { g_debug ("housekeeping: purge trash older than %u days", purge_after); gsd_ldsm_purge_trash (old); } if (purge_temp_files) { g_debug ("housekeeping: purge temp files older than %u days", purge_after); gsd_ldsm_purge_temp_files (old); } g_date_time_unref (old); g_date_time_unref (now); return G_SOURCE_CONTINUE; } static void empty_trash_callback (NotifyNotification *n, const char *action) { GDateTime *old; g_assert (action != NULL); g_assert (strcmp (action, "empty-trash") == 0); old = g_date_time_new_now_local (); gsd_ldsm_purge_trash (old); g_date_time_unref (old); notify_notification_close (n, NULL); } static void on_notification_closed (NotifyNotification *n) { g_object_unref (notification); notification = NULL; } static gboolean ldsm_notify_for_mount (LdsmMountInfo *mount, gboolean multiple_volumes, gboolean other_usable_volumes) { gchar *name, *program; gint64 free_space; gint response; gboolean has_trash; gboolean has_disk_analyzer; gboolean retval = TRUE; gchar *path; /* Don't show a notice if one is already displayed */ if (dialog != NULL || notification != NULL) return retval; name = g_unix_mount_guess_name (mount->mount); free_space = (gint64) mount->buf.f_frsize * (gint64) mount->buf.f_bavail; has_trash = ldsm_mount_has_trash (mount); path = g_strdup (g_unix_mount_get_mount_path (mount->mount)); program = g_find_program_in_path (DISK_SPACE_ANALYZER); has_disk_analyzer = (program != NULL); g_free (program); if (server_has_actions ()) { char *free_space_str; char *summary; char *body; free_space_str = g_format_size (free_space); if (multiple_volumes) { summary = g_strdup_printf (_("Low Disk Space on \"%s\""), name); if (has_trash) { body = g_strdup_printf (_("The volume \"%s\" has only %s disk space remaining. You may free up some space by emptying the trash."), name, free_space_str); } else { body = g_strdup_printf (_("The volume \"%s\" has only %s disk space remaining."), name, free_space_str); } } else { summary = g_strdup (_("Low Disk Space")); if (has_trash) { body = g_strdup_printf (_("This computer has only %s disk space remaining. You may free up some space by emptying the trash."), free_space_str); } else { body = g_strdup_printf (_("This computer has only %s disk space remaining."), free_space_str); } } g_free (free_space_str); notification = notify_notification_new (summary, body, "drive-harddisk-symbolic"); g_free (summary); g_free (body); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); notify_notification_set_app_name (notification, _("Disk space")); notify_notification_set_hint (notification, "transient", g_variant_new_boolean (TRUE)); notify_notification_set_urgency (notification, NOTIFY_URGENCY_CRITICAL); notify_notification_set_timeout (notification, NOTIFY_EXPIRES_DEFAULT); if (has_disk_analyzer) { notify_notification_add_action (notification, "examine", _("Examine"), (NotifyActionCallback) examine_callback, g_strdup (path), g_free); } if (has_trash) { notify_notification_add_action (notification, "empty-trash", _("Empty Trash"), (NotifyActionCallback) empty_trash_callback, NULL, NULL); } notify_notification_add_action (notification, "ignore", _("Ignore"), (NotifyActionCallback) ignore_callback, NULL, NULL); notify_notification_set_category (notification, "device"); if (!notify_notification_show (notification, NULL)) { g_warning ("failed to send disk space notification\n"); } } else { dialog = gsd_ldsm_dialog_new (other_usable_volumes, multiple_volumes, has_disk_analyzer, has_trash, free_space, name, path); g_object_ref (G_OBJECT (dialog)); response = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (GTK_WIDGET (dialog)); dialog = NULL; switch (response) { case GTK_RESPONSE_CANCEL: retval = FALSE; break; case GSD_LDSM_DIALOG_RESPONSE_ANALYZE: retval = FALSE; ldsm_analyze_path (path); break; case GSD_LDSM_DIALOG_RESPONSE_EMPTY_TRASH: retval = TRUE; gsd_ldsm_show_empty_trash (); break; case GTK_RESPONSE_NONE: case GTK_RESPONSE_DELETE_EVENT: retval = TRUE; break; default: g_assert_not_reached (); } } g_free (name); g_free (path); return retval; } static gboolean ldsm_mount_has_space (LdsmMountInfo *mount) { gdouble free_space; free_space = (double) mount->buf.f_bavail / (double) mount->buf.f_blocks; /* enough free space, nothing to do */ if (free_space > free_percent_notify) return TRUE; if (((gint64) mount->buf.f_frsize * (gint64) mount->buf.f_bavail) > ((gint64) free_size_gb_no_notify * GIGABYTE)) return TRUE; /* If we got here, then this volume is low on space */ return FALSE; } static gboolean ldsm_mount_is_virtual (LdsmMountInfo *mount) { if (mount->buf.f_blocks == 0) { /* Filesystems with zero blocks are virtual */ return TRUE; } return FALSE; } static gint ldsm_ignore_path_compare (gconstpointer a, gconstpointer b) { return g_strcmp0 ((const gchar *)a, (const gchar *)b); } static gboolean ldsm_mount_is_user_ignore (const gchar *path) { if (g_slist_find_custom (ignore_paths, path, (GCompareFunc) ldsm_ignore_path_compare) != NULL) return TRUE; else return FALSE; } static void ldsm_free_mount_info (gpointer data) { LdsmMountInfo *mount = data; g_return_if_fail (mount != NULL); g_unix_mount_free (mount->mount); g_free (mount); } static void ldsm_maybe_warn_mounts (GList *mounts, gboolean multiple_volumes, gboolean other_usable_volumes) { GList *l; gboolean done = FALSE; for (l = mounts; l != NULL; l = l->next) { LdsmMountInfo *mount_info = l->data; LdsmMountInfo *previous_mount_info; gdouble free_space; gdouble previous_free_space; time_t curr_time; const gchar *path; gboolean show_notify; if (done) { /* Don't show any more dialogs if the user took action with the last one. The user action * might free up space on multiple volumes, making the next dialog redundant. */ ldsm_free_mount_info (mount_info); continue; } path = g_unix_mount_get_mount_path (mount_info->mount); previous_mount_info = g_hash_table_lookup (ldsm_notified_hash, path); if (previous_mount_info != NULL) previous_free_space = (gdouble) previous_mount_info->buf.f_bavail / (gdouble) previous_mount_info->buf.f_blocks; free_space = (gdouble) mount_info->buf.f_bavail / (gdouble) mount_info->buf.f_blocks; if (previous_mount_info == NULL) { /* We haven't notified for this mount yet */ show_notify = TRUE; mount_info->notify_time = time (NULL); g_hash_table_replace (ldsm_notified_hash, g_strdup (path), mount_info); } else if ((previous_free_space - free_space) > free_percent_notify_again) { /* We've notified for this mount before and free space has decreased sufficiently since last time to notify again */ curr_time = time (NULL); if (difftime (curr_time, previous_mount_info->notify_time) > (gdouble)(min_notify_period * 60)) { show_notify = TRUE; mount_info->notify_time = curr_time; } else { /* It's too soon to show the dialog again. However, we still replace the LdsmMountInfo * struct in the hash table, but give it the notfiy time from the previous dialog. * This will stop the notification from reappearing unnecessarily as soon as the timeout expires. */ show_notify = FALSE; mount_info->notify_time = previous_mount_info->notify_time; } g_hash_table_replace (ldsm_notified_hash, g_strdup (path), mount_info); } else { /* We've notified for this mount before, but the free space hasn't decreased sufficiently to notify again */ ldsm_free_mount_info (mount_info); show_notify = FALSE; } if (show_notify) { if (ldsm_notify_for_mount (mount_info, multiple_volumes, other_usable_volumes)) done = TRUE; } } } static gboolean ldsm_check_all_mounts (gpointer data) { GList *mounts; GList *l; GList *check_mounts = NULL; GList *full_mounts = NULL; guint number_of_mounts; guint number_of_full_mounts; gboolean multiple_volumes = FALSE; gboolean other_usable_volumes = FALSE; /* We iterate through the static mounts in /etc/fstab first, seeing if * they're mounted by checking if the GUnixMountPoint has a corresponding GUnixMountEntry. * Iterating through the static mounts means we automatically ignore dynamically mounted media. */ mounts = g_unix_mount_points_get (time_read); for (l = mounts; l != NULL; l = l->next) { GUnixMountPoint *mount_point = l->data; GUnixMountEntry *mount; LdsmMountInfo *mount_info; const gchar *path; path = g_unix_mount_point_get_mount_path (mount_point); mount = g_unix_mount_at (path, time_read); g_unix_mount_point_free (mount_point); if (mount == NULL) { /* The GUnixMountPoint is not mounted */ continue; } mount_info = g_new0 (LdsmMountInfo, 1); mount_info->mount = mount; path = g_unix_mount_get_mount_path (mount); if (g_unix_mount_is_readonly (mount)) { ldsm_free_mount_info (mount_info); continue; } if (ldsm_mount_is_user_ignore (g_unix_mount_get_mount_path (mount))) { ldsm_free_mount_info (mount_info); continue; } if (gsd_should_ignore_unix_mount (mount)) { ldsm_free_mount_info (mount_info); continue; } if (statvfs (path, &mount_info->buf) != 0) { ldsm_free_mount_info (mount_info); continue; } if (ldsm_mount_is_virtual (mount_info)) { ldsm_free_mount_info (mount_info); continue; } check_mounts = g_list_prepend (check_mounts, mount_info); } g_list_free (mounts); number_of_mounts = g_list_length (check_mounts); if (number_of_mounts > 1) multiple_volumes = TRUE; for (l = check_mounts; l != NULL; l = l->next) { LdsmMountInfo *mount_info = l->data; if (!ldsm_mount_has_space (mount_info)) { full_mounts = g_list_prepend (full_mounts, mount_info); } else { g_hash_table_remove (ldsm_notified_hash, g_unix_mount_get_mount_path (mount_info->mount)); ldsm_free_mount_info (mount_info); } } number_of_full_mounts = g_list_length (full_mounts); if (number_of_mounts > number_of_full_mounts) other_usable_volumes = TRUE; ldsm_maybe_warn_mounts (full_mounts, multiple_volumes, other_usable_volumes); g_list_free (check_mounts); g_list_free (full_mounts); return TRUE; } static gboolean ldsm_is_hash_item_not_in_mounts (gpointer key, gpointer value, gpointer user_data) { GList *l; for (l = (GList *) user_data; l != NULL; l = l->next) { GUnixMountEntry *mount = l->data; const char *path; path = g_unix_mount_get_mount_path (mount); if (strcmp (path, key) == 0) return FALSE; } return TRUE; } static void ldsm_mounts_changed (GObject *monitor, gpointer data) { GList *mounts; /* remove the saved data for mounts that got removed */ mounts = g_unix_mounts_get (time_read); g_hash_table_foreach_remove (ldsm_notified_hash, ldsm_is_hash_item_not_in_mounts, mounts); g_list_free_full (mounts, (GDestroyNotify) g_unix_mount_free); /* check the status now, for the new mounts */ ldsm_check_all_mounts (NULL); /* and reset the timeout */ if (ldsm_timeout_id) g_source_remove (ldsm_timeout_id); ldsm_timeout_id = g_timeout_add_seconds (CHECK_EVERY_X_SECONDS, ldsm_check_all_mounts, NULL); } static gboolean ldsm_is_hash_item_in_ignore_paths (gpointer key, gpointer value, gpointer user_data) { return ldsm_mount_is_user_ignore (key); } static void gsd_ldsm_get_config (void) { gchar **settings_list; free_percent_notify = g_settings_get_double (settings, SETTINGS_FREE_PC_NOTIFY_KEY); free_percent_notify_again = g_settings_get_double (settings, SETTINGS_FREE_PC_NOTIFY_AGAIN_KEY); free_size_gb_no_notify = g_settings_get_int (settings, SETTINGS_FREE_SIZE_NO_NOTIFY); min_notify_period = g_settings_get_int (settings, SETTINGS_MIN_NOTIFY_PERIOD); if (ignore_paths != NULL) { g_slist_foreach (ignore_paths, (GFunc) g_free, NULL); g_clear_pointer (&ignore_paths, g_slist_free); } settings_list = g_settings_get_strv (settings, SETTINGS_IGNORE_PATHS); if (settings_list != NULL) { guint i; for (i = 0; settings_list[i] != NULL; i++) ignore_paths = g_slist_prepend (ignore_paths, g_strdup (settings_list[i])); /* Make sure we dont leave stale entries in ldsm_notified_hash */ g_hash_table_foreach_remove (ldsm_notified_hash, ldsm_is_hash_item_in_ignore_paths, NULL); g_strfreev (settings_list); } purge_trash = g_settings_get_boolean (privacy_settings, SETTINGS_PURGE_TRASH); purge_temp_files = g_settings_get_boolean (privacy_settings, SETTINGS_PURGE_TEMP_FILES); purge_after = g_settings_get_uint (privacy_settings, SETTINGS_PURGE_AFTER); } static void gsd_ldsm_update_config (GSettings *settings, const gchar *key, gpointer user_data) { gsd_ldsm_get_config (); } void gsd_ldsm_setup (gboolean check_now) { if (ldsm_notified_hash || ldsm_timeout_id || ldsm_monitor) { g_warning ("Low disk space monitor already initialized."); return; } ldsm_notified_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ldsm_free_mount_info); settings = g_settings_new (SETTINGS_HOUSEKEEPING_DIR); privacy_settings = g_settings_new (PRIVACY_SETTINGS); gsd_ldsm_get_config (); g_signal_connect (G_OBJECT (settings), "changed", G_CALLBACK (gsd_ldsm_update_config), NULL); ldsm_monitor = g_unix_mount_monitor_new (); g_unix_mount_monitor_set_rate_limit (ldsm_monitor, 1000); g_signal_connect (ldsm_monitor, "mounts-changed", G_CALLBACK (ldsm_mounts_changed), NULL); if (check_now) ldsm_check_all_mounts (NULL); ldsm_timeout_id = g_timeout_add_seconds (CHECK_EVERY_X_SECONDS, ldsm_check_all_mounts, NULL); purge_trash_id = g_timeout_add_seconds (3600, ldsm_purge_trash_and_temp, NULL); } void gsd_ldsm_clean (void) { if (purge_trash_id) g_source_remove (purge_trash_id); purge_trash_id = 0; if (purge_temp_id) g_source_remove (purge_temp_id); purge_temp_id = 0; if (ldsm_timeout_id) g_source_remove (ldsm_timeout_id); ldsm_timeout_id = 0; if (ldsm_notified_hash) g_hash_table_destroy (ldsm_notified_hash); ldsm_notified_hash = NULL; if (ldsm_monitor) g_object_unref (ldsm_monitor); ldsm_monitor = NULL; if (settings != NULL) { g_object_unref (settings); } g_clear_object (&privacy_settings); if (dialog) { gtk_widget_destroy (GTK_WIDGET (dialog)); dialog = NULL; } if (notification != NULL) { notify_notification_close (notification, NULL); notification = NULL; } if (ignore_paths) { g_slist_foreach (ignore_paths, (GFunc) g_free, NULL); g_slist_free (ignore_paths); } } ./plugins/housekeeping/gsd-disk-space.h0000644000004100000410000000364612735467744020415 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * vim: set et sw=8 ts=8: * * Copyright (c) 2008, Novell, Inc. * * Authors: Vincent Untz * * 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. * */ #ifndef __GSD_DISK_SPACE_H #define __GSD_DISK_SPACE_H #include G_BEGIN_DECLS typedef struct { gint ref_count; GFile *file; GCancellable *cancellable; GDateTime *old; gboolean dry_run; gboolean trash; gchar *name; gint depth; } DeleteData; void delete_data_unref (DeleteData *data); DeleteData *delete_data_new (GFile *file, GCancellable *cancellable, GDateTime *old, gboolean dry_run, gboolean trash, gint depth); void delete_recursively_by_age (DeleteData *data); void gsd_ldsm_setup (gboolean check_now); void gsd_ldsm_clean (void); /* for the test */ void gsd_ldsm_show_empty_trash (void); void gsd_ldsm_purge_trash (GDateTime *old); void gsd_ldsm_purge_temp_files (GDateTime *old); G_END_DECLS #endif /* __GSD_DISK_SPACE_H */ ./plugins/housekeeping/gsd-disk-space-test.c0000644000004100000410000000251312735467744021355 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * vim: set et sw=8 ts=8: * * Copyright (c) 2008, Novell, Inc. * * Authors: Vincent Untz * * 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 "config.h" #include #include #include "gsd-disk-space.h" int main (int argc, char **argv) { GMainLoop *loop; gtk_init (&argc, &argv); notify_init ("gsd-disk-space-test"); loop = g_main_loop_new (NULL, FALSE); gsd_ldsm_setup (TRUE); g_main_loop_run (loop); gsd_ldsm_clean (); g_main_loop_unref (loop); return 0; } ./plugins/housekeeping/test-housekeeping.c0000644000004100000410000000035012735467744021242 0ustar www-datawww-data#define NEW gsd_housekeeping_manager_new #define START gsd_housekeeping_manager_start #define STOP gsd_housekeeping_manager_stop #define MANAGER GsdHousekeepingManager #include "gsd-housekeeping-manager.h" #include "test-plugin.h" ./plugins/housekeeping/housekeeping.gnome-settings-plugin.in0000644000004100000410000000042512735467744024712 0ustar www-datawww-data[GNOME Settings Plugin] Module=housekeeping IAge=0 Priority=1 _Name=Housekeeping _Description=Automatically prunes thumbnail caches and other transient files, and warns about low disk space Authors=Michael J. Chudobiak Copyright=Copyright © 2008 Michael J. Chudobiak Website= ./plugins/housekeeping/gsd-housekeeping-plugin.c0000644000004100000410000000205112735467744022334 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Michael J. Chudobiak * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-housekeeping-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdHousekeeping, gsd_housekeeping) ./plugins/housekeeping/gsd-housekeeping-manager.c0000644000004100000410000003736512735467744022470 0ustar www-datawww-data/* * Copyright (C) 2008 Michael J. Chudobiak * * 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 "config.h" #include #include #include #include "gnome-settings-profile.h" #include "gsd-housekeeping-manager.h" #include "gsd-disk-space.h" /* General */ #define INTERVAL_ONCE_A_DAY 24*60*60 #define INTERVAL_TWO_MINUTES 2*60 /* Thumbnail cleaner */ #define THUMB_PREFIX "org.gnome.desktop.thumbnail-cache" #define THUMB_AGE_KEY "maximum-age" #define THUMB_SIZE_KEY "maximum-size" #define GSD_HOUSEKEEPING_DBUS_PATH "/org/gnome/SettingsDaemon/Housekeeping" static const gchar introspection_xml[] = "" " " " " " " " " ""; struct GsdHousekeepingManagerPrivate { GSettings *settings; guint long_term_cb; guint short_term_cb; GDBusNodeInfo *introspection_data; GDBusConnection *connection; GCancellable *bus_cancellable; }; #define GSD_HOUSEKEEPING_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_HOUSEKEEPING_MANAGER, GsdHousekeepingManagerPrivate)) static void gsd_housekeeping_manager_class_init (GsdHousekeepingManagerClass *klass); static void gsd_housekeeping_manager_init (GsdHousekeepingManager *housekeeping_manager); G_DEFINE_TYPE (GsdHousekeepingManager, gsd_housekeeping_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; typedef struct { glong now; glong max_age; goffset total_size; goffset max_size; } PurgeData; typedef struct { time_t mtime; char *path; glong size; } ThumbData; static void thumb_data_free (gpointer data) { ThumbData *info = data; if (info) { g_free (info->path); g_free (info); } } static GList * read_dir_for_purge (const char *path, GList *files) { GFile *read_path; GFileEnumerator *enum_dir; read_path = g_file_new_for_path (path); enum_dir = g_file_enumerate_children (read_path, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (enum_dir != NULL) { GFileInfo *info; while ((info = g_file_enumerator_next_file (enum_dir, NULL, NULL)) != NULL) { const char *name; name = g_file_info_get_name (info); if (strlen (name) == 36 && strcmp (name + 32, ".png") == 0) { ThumbData *td; GFile *entry; char *entry_path; GTimeVal mod_time; entry = g_file_get_child (read_path, name); entry_path = g_file_get_path (entry); g_object_unref (entry); g_file_info_get_modification_time (info, &mod_time); td = g_new0 (ThumbData, 1); td->path = entry_path; td->mtime = mod_time.tv_sec; td->size = g_file_info_get_size (info); files = g_list_prepend (files, td); } g_object_unref (info); } g_object_unref (enum_dir); } g_object_unref (read_path); return files; } static void purge_old_thumbnails (ThumbData *info, PurgeData *purge_data) { if ((purge_data->now - info->mtime) > purge_data->max_age) { g_unlink (info->path); info->size = 0; } else { purge_data->total_size += info->size; } } static int sort_file_mtime (ThumbData *file1, ThumbData *file2) { return file1->mtime - file2->mtime; } static char ** get_thumbnail_dirs (void) { GPtrArray *array; char *path; array = g_ptr_array_new (); /* check new XDG cache */ path = g_build_filename (g_get_user_cache_dir (), "thumbnails", "normal", NULL); g_ptr_array_add (array, path); path = g_build_filename (g_get_user_cache_dir (), "thumbnails", "large", NULL); g_ptr_array_add (array, path); path = g_build_filename (g_get_user_cache_dir (), "thumbnails", "fail", "gnome-thumbnail-factory", NULL); g_ptr_array_add (array, path); /* cleanup obsolete locations too */ path = g_build_filename (g_get_home_dir (), ".thumbnails", "normal", NULL); g_ptr_array_add (array, path); path = g_build_filename (g_get_home_dir (), ".thumbnails", "large", NULL); g_ptr_array_add (array, path); path = g_build_filename (g_get_home_dir (), ".thumbnails", "fail", "gnome-thumbnail-factory", NULL); g_ptr_array_add (array, path); g_ptr_array_add (array, NULL); return (char **) g_ptr_array_free (array, FALSE); } static void purge_thumbnail_cache (GsdHousekeepingManager *manager) { char **paths; GList *files; PurgeData purge_data; GTimeVal current_time; guint i; g_debug ("housekeeping: checking thumbnail cache size and freshness"); purge_data.max_age = g_settings_get_int (manager->priv->settings, THUMB_AGE_KEY) * 24 * 60 * 60; purge_data.max_size = g_settings_get_int (manager->priv->settings, THUMB_SIZE_KEY) * 1024 * 1024; /* if both are set to -1, we don't need to read anything */ if ((purge_data.max_age < 0) && (purge_data.max_size < 0)) return; paths = get_thumbnail_dirs (); files = NULL; for (i = 0; paths[i] != NULL; i++) files = read_dir_for_purge (paths[i], files); g_strfreev (paths); g_get_current_time (¤t_time); purge_data.now = current_time.tv_sec; purge_data.total_size = 0; if (purge_data.max_age >= 0) g_list_foreach (files, (GFunc) purge_old_thumbnails, &purge_data); if ((purge_data.total_size > purge_data.max_size) && (purge_data.max_size >= 0)) { GList *scan; files = g_list_sort (files, (GCompareFunc) sort_file_mtime); for (scan = files; scan && (purge_data.total_size > purge_data.max_size); scan = scan->next) { ThumbData *info = scan->data; g_unlink (info->path); purge_data.total_size -= info->size; } } g_list_foreach (files, (GFunc) thumb_data_free, NULL); g_list_free (files); } static gboolean do_cleanup (GsdHousekeepingManager *manager) { purge_thumbnail_cache (manager); return TRUE; } static gboolean do_cleanup_once (GsdHousekeepingManager *manager) { do_cleanup (manager); manager->priv->short_term_cb = 0; return FALSE; } static void do_cleanup_soon (GsdHousekeepingManager *manager) { if (manager->priv->short_term_cb == 0) { g_debug ("housekeeping: will tidy up in 2 minutes"); manager->priv->short_term_cb = g_timeout_add_seconds (INTERVAL_TWO_MINUTES, (GSourceFunc) do_cleanup_once, manager); } } static void settings_changed_callback (GSettings *settings, const char *key, GsdHousekeepingManager *manager) { do_cleanup_soon (manager); } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GDateTime *now; now = g_date_time_new_now_local (); if (g_strcmp0 (method_name, "EmptyTrash") == 0) { gsd_ldsm_purge_trash (now); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "RemoveTempFiles") == 0) { gsd_ldsm_purge_temp_files (now); g_dbus_method_invocation_return_value (invocation, NULL); } g_date_time_unref (now); } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, NULL, /* Get Property */ NULL, /* Set Property */ }; static void on_bus_gotten (GObject *source_object, GAsyncResult *res, GsdHousekeepingManager *manager) { GDBusConnection *connection; GError *error = NULL; GDBusInterfaceInfo **infos; int i; if (manager->priv->bus_cancellable == NULL || g_cancellable_is_cancelled (manager->priv->bus_cancellable)) { g_warning ("Operation has been cancelled, so not retrieving session bus"); return; } connection = g_bus_get_finish (res, &error); if (connection == NULL) { g_warning ("Could not get session bus: %s", error->message); g_error_free (error); return; } manager->priv->connection = connection; infos = manager->priv->introspection_data->interfaces; for (i = 0; infos[i] != NULL; i++) { g_dbus_connection_register_object (connection, GSD_HOUSEKEEPING_DBUS_PATH, infos[i], &interface_vtable, manager, NULL, NULL); } } static void register_manager_dbus (GsdHousekeepingManager *manager) { manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (manager->priv->introspection_data != NULL); manager->priv->bus_cancellable = g_cancellable_new (); g_bus_get (G_BUS_TYPE_SESSION, manager->priv->bus_cancellable, (GAsyncReadyCallback) on_bus_gotten, manager); } gboolean gsd_housekeeping_manager_start (GsdHousekeepingManager *manager, GError **error) { gchar *dir; g_debug ("Starting housekeeping manager"); gnome_settings_profile_start (NULL); /* Create ~/.local/ as early as possible */ g_mkdir_with_parents(g_get_user_data_dir (), 0700); /* Create ~/.local/share/applications/, see * https://bugzilla.gnome.org/show_bug.cgi?id=703048 */ dir = g_build_filename (g_get_user_data_dir (), "applications", NULL); g_mkdir (dir, 0700); g_free (dir); gsd_ldsm_setup (FALSE); manager->priv->settings = g_settings_new (THUMB_PREFIX); g_signal_connect (G_OBJECT (manager->priv->settings), "changed", G_CALLBACK (settings_changed_callback), manager); /* Clean once, a few minutes after start-up */ do_cleanup_soon (manager); /* Clean periodically, on a daily basis. */ manager->priv->long_term_cb = g_timeout_add_seconds (INTERVAL_ONCE_A_DAY, (GSourceFunc) do_cleanup, manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_housekeeping_manager_stop (GsdHousekeepingManager *manager) { GsdHousekeepingManagerPrivate *p = manager->priv; g_debug ("Stopping housekeeping manager"); if (manager->priv->bus_cancellable != NULL) { g_cancellable_cancel (manager->priv->bus_cancellable); g_object_unref (manager->priv->bus_cancellable); manager->priv->bus_cancellable = NULL; } if (manager->priv->introspection_data) { g_dbus_node_info_unref (manager->priv->introspection_data); manager->priv->introspection_data = NULL; } if (manager->priv->connection != NULL) { g_object_unref (manager->priv->connection); manager->priv->connection = NULL; } if (p->short_term_cb) { g_source_remove (p->short_term_cb); p->short_term_cb = 0; } if (p->long_term_cb) { g_source_remove (p->long_term_cb); p->long_term_cb = 0; /* Do a clean-up on shutdown if and only if the size or age limits have been set to paranoid levels (zero) */ if ((g_settings_get_int (p->settings, THUMB_AGE_KEY) == 0) || (g_settings_get_int (p->settings, THUMB_SIZE_KEY) == 0)) { do_cleanup (manager); } g_object_unref (p->settings); p->settings = NULL; } gsd_ldsm_clean (); } static void gsd_housekeeping_manager_class_init (GsdHousekeepingManagerClass *klass) { g_type_class_add_private (klass, sizeof (GsdHousekeepingManagerPrivate)); } static void gsd_housekeeping_manager_init (GsdHousekeepingManager *manager) { manager->priv = GSD_HOUSEKEEPING_MANAGER_GET_PRIVATE (manager); } GsdHousekeepingManager * gsd_housekeeping_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_HOUSEKEEPING_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); register_manager_dbus (manager_object); } return GSD_HOUSEKEEPING_MANAGER (manager_object); } ./plugins/housekeeping/gsd-ldsm-dialog.h0000644000004100000410000000507712735467744020566 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * gsd-ldsm-dialog.c * Copyright (C) Chris Coulson 2009 * * 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. */ #ifndef _GSD_LDSM_DIALOG_H_ #define _GSD_LDSM_DIALOG_H_ #include #include G_BEGIN_DECLS #define GSD_TYPE_LDSM_DIALOG (gsd_ldsm_dialog_get_type ()) #define GSD_LDSM_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_LDSM_DIALOG, GsdLdsmDialog)) #define GSD_LDSM_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_LDSM_DIALOG, GsdLdsmDialogClass)) #define GSD_IS_LDSM_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_LDSM_DIALOG)) #define GSD_IS_LDSM_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_LDSM_DIALOG)) #define GSD_LDSM_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSD_TYPE_LDSM_DIALOG, GsdLdsmDialogClass)) enum { GSD_LDSM_DIALOG_RESPONSE_EMPTY_TRASH = -20, GSD_LDSM_DIALOG_RESPONSE_ANALYZE = -21 }; typedef struct GsdLdsmDialogPrivate GsdLdsmDialogPrivate; typedef struct _GsdLdsmDialogClass GsdLdsmDialogClass; typedef struct _GsdLdsmDialog GsdLdsmDialog; struct _GsdLdsmDialogClass { GtkDialogClass parent_class; }; struct _GsdLdsmDialog { GtkDialog parent_instance; GsdLdsmDialogPrivate *priv; }; GType gsd_ldsm_dialog_get_type (void) G_GNUC_CONST; GsdLdsmDialog * gsd_ldsm_dialog_new (gboolean other_usable_partitions, gboolean other_partitions, gboolean display_baobab, gboolean display_empty_trash, gint64 space_remaining, const gchar *partition_name, const gchar *mount_path); G_END_DECLS #endif /* _GSD_LDSM_DIALOG_H_ */ ./plugins/housekeeping/gsd-housekeeping-manager.h0000644000004100000410000000470512735467744022465 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Michael J. Chudobiak * * 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. * */ #ifndef __GSD_HOUSEKEEPING_MANAGER_H #define __GSD_HOUSEKEEPING_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_HOUSEKEEPING_MANAGER (gsd_housekeeping_manager_get_type ()) #define GSD_HOUSEKEEPING_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_HOUSEKEEPING_MANAGER, GsdHousekeepingManager)) #define GSD_HOUSEKEEPING_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_HOUSEKEEPING_MANAGER, GsdHousekeepingManagerClass)) #define GSD_IS_HOUSEKEEPING_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_HOUSEKEEPING_MANAGER)) #define GSD_IS_HOUSEKEEPING_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_HOUSEKEEPING_MANAGER)) #define GSD_HOUSEKEEPING_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_HOUSEKEEPING_MANAGER, GsdHousekeepingManagerClass)) typedef struct GsdHousekeepingManagerPrivate GsdHousekeepingManagerPrivate; typedef struct { GObject parent; GsdHousekeepingManagerPrivate *priv; } GsdHousekeepingManager; typedef struct { GObjectClass parent_class; } GsdHousekeepingManagerClass; GType gsd_housekeeping_manager_get_type (void); GsdHousekeepingManager * gsd_housekeeping_manager_new (void); gboolean gsd_housekeeping_manager_start (GsdHousekeepingManager *manager, GError **error); void gsd_housekeeping_manager_stop (GsdHousekeepingManager *manager); G_END_DECLS #endif /* __GSD_HOUSEKEEPING_MANAGER_H */ ./plugins/housekeeping/gsd-disk-space-helper.h0000644000004100000410000000242312735467744021662 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * vim: set et sw=8 ts=8: * * Copyright (c) 2008, Novell, Inc. * Copyright (c) 2012, Red Hat, Inc. * * Authors: Vincent Untz * Bastien Nocera * * 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. * */ #ifndef __GSD_DISK_SPACE_HELPER_H #define __GSD_DISK_SPACE_HELPER_H #include #include G_BEGIN_DECLS gboolean gsd_should_ignore_unix_mount (GUnixMountEntry *mount); gboolean gsd_is_removable_mount (GUnixMountEntry *mount); G_END_DECLS #endif /* __GSD_DISK_SPACE_HELPER_H */ ./plugins/housekeeping/gsd-empty-trash-test.c0000644000004100000410000000236012735467744021607 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * vim: set et sw=8 ts=8: * * Copyright (c) 2011, Red Hat, Inc. * * Authors: Cosimo Cecchi * * 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 "config.h" #include #include "gsd-disk-space.h" int main (int argc, char **argv) { GMainLoop *loop; gtk_init (&argc, &argv); loop = g_main_loop_new (NULL, FALSE); gsd_ldsm_show_empty_trash (); g_main_loop_run (loop); g_main_loop_unref (loop); return 0; } ./plugins/orientation/0000755000004100000410000000000012735467745015303 5ustar www-datawww-data./plugins/orientation/Makefile.am0000644000004100000410000000312012735467744017332 0ustar www-datawww-dataplugin_name = orientation libexec_PROGRAMS = usd-test-orientation usd_test_orientation_SOURCES = \ gsd-orientation-manager.h \ gsd-orientation-manager.c \ test-orientation.c usd_test_orientation_CFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(ORIENTATION_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_orientation_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(ORIENTATION_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_LTLIBRARIES = liborientation.la liborientation_la_SOURCES = \ gsd-orientation-plugin.c \ gsd-orientation-manager.h \ gsd-orientation-manager.c liborientation_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common/ \ -I$(top_srcdir)/data/ \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) liborientation_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(ORIENTATION_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) liborientation_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) liborientation_la_LIBADD = \ $(top_builddir)/plugins/common/libcommon.la \ $(ORIENTATION_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = orientation.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = $(plugin_in_files) CLEANFILES = $(plugin_DATA) DISTCLEANFILES = $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/orientation/gsd-orientation-plugin.c0000644000004100000410000000210312735467744022044 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2010 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-orientation-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdOrientation, gsd_orientation) ./plugins/orientation/test-orientation.c0000644000004100000410000000034312735467744020756 0ustar www-datawww-data#define NEW gsd_orientation_manager_new #define START gsd_orientation_manager_start #define STOP gsd_orientation_manager_stop #define MANAGER GsdOrientationManager #include "gsd-orientation-manager.h" #include "test-plugin.h" ./plugins/orientation/gsd-orientation-manager.h0000644000004100000410000000472012735467744022174 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2010 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 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. * */ #ifndef __GSD_ORIENTATION_MANAGER_H #define __GSD_ORIENTATION_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_ORIENTATION_MANAGER (gsd_orientation_manager_get_type ()) #define GSD_ORIENTATION_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_ORIENTATION_MANAGER, GsdOrientationManager)) #define GSD_ORIENTATION_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_ORIENTATION_MANAGER, GsdOrientationManagerClass)) #define GSD_IS_ORIENTATION_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_ORIENTATION_MANAGER)) #define GSD_IS_ORIENTATION_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_ORIENTATION_MANAGER)) #define GSD_ORIENTATION_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_ORIENTATION_MANAGER, GsdOrientationManagerClass)) typedef struct GsdOrientationManagerPrivate GsdOrientationManagerPrivate; typedef struct { GObject parent; GsdOrientationManagerPrivate *priv; } GsdOrientationManager; typedef struct { GObjectClass parent_class; } GsdOrientationManagerClass; GType gsd_orientation_manager_get_type (void); GsdOrientationManager * gsd_orientation_manager_new (void); gboolean gsd_orientation_manager_start (GsdOrientationManager *manager, GError **error); void gsd_orientation_manager_stop (GsdOrientationManager *manager); G_END_DECLS #endif /* __GSD_ORIENTATION_MANAGER_H */ ./plugins/orientation/gsd-orientation-manager.c0000644000004100000410000004765512735467744022205 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2010,2011 Red Hat, Inc. * * Author: Bastien Nocera * * 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 "config.h" #include #include #include #include #include #include #include "gsd-input-helper.h" #include "gnome-settings-plugin.h" #include "gnome-settings-profile.h" #include "gsd-orientation-manager.h" #include "gsd-rr.h" typedef enum { ORIENTATION_UNDEFINED, ORIENTATION_NORMAL, ORIENTATION_BOTTOM_UP, ORIENTATION_LEFT_UP, ORIENTATION_RIGHT_UP } OrientationUp; #define GSD_ORIENTATION_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_ORIENTATION_MANAGER, GsdOrientationManagerPrivate)) struct GsdOrientationManagerPrivate { guint start_idle_id; guint name_id; /* Accelerometer */ char *sysfs_path; OrientationUp prev_orientation; /* DBus */ GDBusNodeInfo *introspection_data; GDBusConnection *connection; GDBusProxy *xrandr_proxy; GCancellable *cancellable; /* Notifications */ GUdevClient *client; GSettings *settings; gboolean orientation_lock; }; #define CONF_SCHEMA "org.gnome.settings-daemon.peripherals.touchscreen" #define ORIENTATION_LOCK_KEY "orientation-lock" #define GSD_ORIENTATION_DBUS_NAME GSD_DBUS_NAME ".Orientation" #define GSD_ORIENTATION_DBUS_PATH GSD_DBUS_PATH "/Orientation" static const gchar introspection_xml[] = "" " " " " " " ""; static void gsd_orientation_manager_class_init (GsdOrientationManagerClass *klass); static void gsd_orientation_manager_init (GsdOrientationManager *orientation_manager); static void gsd_orientation_manager_finalize (GObject *object); G_DEFINE_TYPE (GsdOrientationManager, gsd_orientation_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; #define MPU_THRESHOLD 12000 #define MPU_POLL_INTERVAL 1 static gboolean is_mpu6050 = FALSE; static char *mpu6050_accel_x = NULL; static char *mpu6050_accel_y = NULL; static gboolean mpu_timer(GsdOrientationManager *manager); static GObject * gsd_orientation_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdOrientationManager *orientation_manager; orientation_manager = GSD_ORIENTATION_MANAGER (G_OBJECT_CLASS (gsd_orientation_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (orientation_manager); } static void gsd_orientation_manager_dispose (GObject *object) { G_OBJECT_CLASS (gsd_orientation_manager_parent_class)->dispose (object); } static void gsd_orientation_manager_class_init (GsdOrientationManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_orientation_manager_constructor; object_class->dispose = gsd_orientation_manager_dispose; object_class->finalize = gsd_orientation_manager_finalize; g_type_class_add_private (klass, sizeof (GsdOrientationManagerPrivate)); } static void gsd_orientation_manager_init (GsdOrientationManager *manager) { manager->priv = GSD_ORIENTATION_MANAGER_GET_PRIVATE (manager); manager->priv->prev_orientation = ORIENTATION_UNDEFINED; } static GsdRRRotation orientation_to_rotation (OrientationUp orientation) { switch (orientation) { case ORIENTATION_NORMAL: return GSD_RR_ROTATION_0; case ORIENTATION_BOTTOM_UP: return GSD_RR_ROTATION_180; case ORIENTATION_LEFT_UP: return GSD_RR_ROTATION_90; case ORIENTATION_RIGHT_UP: return GSD_RR_ROTATION_270; default: g_assert_not_reached (); } } static OrientationUp orientation_from_string (const char *orientation) { if (g_strcmp0 (orientation, "normal") == 0) return ORIENTATION_NORMAL; if (g_strcmp0 (orientation, "bottom-up") == 0) return ORIENTATION_BOTTOM_UP; if (g_strcmp0 (orientation, "left-up") == 0) return ORIENTATION_LEFT_UP; if (g_strcmp0 (orientation, "right-up") == 0) return ORIENTATION_RIGHT_UP; return ORIENTATION_UNDEFINED; } static const char * orientation_to_string (OrientationUp o) { switch (o) { case ORIENTATION_UNDEFINED: return "undefined"; case ORIENTATION_NORMAL: return "normal"; case ORIENTATION_BOTTOM_UP: return "bottom-up"; case ORIENTATION_LEFT_UP: return "left-up"; case ORIENTATION_RIGHT_UP: return "right-up"; default: g_assert_not_reached (); } } static OrientationUp get_orientation_from_device (GUdevDevice *dev) { const char *value; value = g_udev_device_get_property (dev, "ID_INPUT_ACCELEROMETER_ORIENTATION"); if (value == NULL) { g_debug ("Couldn't find orientation for accelerometer %s", g_udev_device_get_sysfs_path (dev)); return ORIENTATION_UNDEFINED; } g_debug ("Found orientation '%s' for accelerometer %s", value, g_udev_device_get_sysfs_path (dev)); return orientation_from_string (value); } static void on_xrandr_action_call_finished (GObject *source_object, GAsyncResult *res, GsdOrientationManager *manager) { GError *error = NULL; GVariant *variant; variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); g_object_unref (manager->priv->cancellable); manager->priv->cancellable = NULL; if (error != NULL) { g_warning ("Unable to call 'RotateTo': %s", error->message); g_error_free (error); } else { g_variant_unref (variant); } } static void do_xrandr_action (GsdOrientationManager *manager, GsdRRRotation rotation) { GsdOrientationManagerPrivate *priv = manager->priv; GTimeVal tv; gint64 timestamp; if (priv->connection == NULL || priv->xrandr_proxy == NULL) { g_warning ("No existing D-Bus connection trying to handle XRANDR keys"); return; } if (priv->cancellable != NULL) { g_debug ("xrandr action already in flight"); return; } g_get_current_time (&tv); timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000; priv->cancellable = g_cancellable_new (); g_dbus_proxy_call (priv->xrandr_proxy, "RotateTo", g_variant_new ("(ix)", rotation, timestamp), G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, (GAsyncReadyCallback) on_xrandr_action_call_finished, manager); } static void do_rotation (GsdOrientationManager *manager) { GsdRRRotation rotation; if (manager->priv->orientation_lock) { g_debug ("Orientation changed, but we are locked"); return; } if (manager->priv->prev_orientation == ORIENTATION_UNDEFINED) { g_debug ("Not trying to rotate, orientation is undefined"); return; } rotation = orientation_to_rotation (manager->priv->prev_orientation); do_xrandr_action (manager, rotation); } static void client_uevent_cb (GUdevClient *client, gchar *action, GUdevDevice *device, GsdOrientationManager *manager) { const char *sysfs_path; OrientationUp orientation; sysfs_path = g_udev_device_get_sysfs_path (device); g_debug ("Received uevent '%s' from '%s'", action, sysfs_path); if (manager->priv->orientation_lock) return; if (g_str_equal (action, "change") == FALSE) return; if (g_strcmp0 (manager->priv->sysfs_path, sysfs_path) != 0) return; g_debug ("Received an event from the accelerometer"); orientation = get_orientation_from_device (device); if (orientation != manager->priv->prev_orientation) { manager->priv->prev_orientation = orientation; g_debug ("Orientation changed to '%s', switching screen rotation", orientation_to_string (manager->priv->prev_orientation)); do_rotation (manager); } } static void orientation_lock_changed_cb (GSettings *settings, gchar *key, GsdOrientationManager *manager) { gboolean new; new = g_settings_get_boolean (settings, key); if (new == manager->priv->orientation_lock) return; manager->priv->orientation_lock = new; if (new == FALSE) { if (is_mpu6050) { g_timeout_add_seconds(MPU_POLL_INTERVAL, (GSourceFunc) mpu_timer, manager); } /* Handle the rotations that could have occurred while * we were locked */ do_rotation (manager); } } static void xrandr_ready_cb (GObject *source_object, GAsyncResult *res, GsdOrientationManager *manager) { GError *error = NULL; manager->priv->xrandr_proxy = g_dbus_proxy_new_finish (res, &error); if (manager->priv->xrandr_proxy == NULL) { g_warning ("Failed to get proxy for XRandR operations: %s", error->message); g_error_free (error); } } static void on_bus_gotten (GObject *source_object, GAsyncResult *res, GsdOrientationManager *manager) { GDBusConnection *connection; GError *error = NULL; connection = g_bus_get_finish (res, &error); if (connection == NULL) { g_warning ("Could not get session bus: %s", error->message); g_error_free (error); return; } manager->priv->connection = connection; g_dbus_connection_register_object (connection, GSD_ORIENTATION_DBUS_PATH, manager->priv->introspection_data->interfaces[0], NULL, NULL, NULL, NULL); g_dbus_proxy_new (manager->priv->connection, G_DBUS_PROXY_FLAGS_NONE, NULL, GSD_DBUS_NAME ".XRANDR", GSD_DBUS_PATH "/XRANDR", GSD_DBUS_BASE_INTERFACE ".XRANDR_2", NULL, (GAsyncReadyCallback) xrandr_ready_cb, manager); manager->priv->name_id = g_bus_own_name_on_connection (connection, GSD_ORIENTATION_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, NULL, NULL, NULL); } static GUdevDevice * get_accelerometer (GUdevClient *client) { GList *list, *listiio, *l; GUdevDevice *ret, *parent; /* Look for a device with the ID_INPUT_ACCELEROMETER=1 property */ ret = NULL; list = g_udev_client_query_by_subsystem (client, "input"); listiio = g_udev_client_query_by_subsystem (client, "iio"); list = g_list_concat(list, listiio); for (l = list; l != NULL; l = l->next) { GUdevDevice *dev; dev = l->data; if (g_udev_device_get_property_as_boolean (dev, "ID_INPUT_ACCELEROMETER")) { ret = dev; continue; } g_object_unref (dev); } g_list_free (list); if (ret == NULL) return NULL; /* Now walk up to the parent */ parent = g_udev_device_get_parent (ret); if (parent == NULL) return ret; if (g_udev_device_get_property_as_boolean (parent, "ID_INPUT_ACCELEROMETER")) { g_object_unref (ret); ret = parent; } else { g_object_unref (parent); } return ret; } static int read_sysfs_attr_as_int(const char *filename) { int i, c; char buf[40]; int fd = open(filename, O_RDONLY); if (fd < 0) return 0; c = read(fd, buf, 40); if (c < 0) return 0; close(fd); sscanf(buf, "%d", &i); return i; } static gboolean mpu_timer(GsdOrientationManager *manager) { int x, y; static gboolean first = TRUE; OrientationUp orientation = manager->priv->prev_orientation; if (manager->priv->xrandr_proxy == NULL) return TRUE; x = read_sysfs_attr_as_int(mpu6050_accel_x); y = read_sysfs_attr_as_int(mpu6050_accel_y); if (x > MPU_THRESHOLD) orientation = ORIENTATION_NORMAL; if (x < -MPU_THRESHOLD) orientation = ORIENTATION_BOTTOM_UP; if (y > MPU_THRESHOLD) orientation = ORIENTATION_RIGHT_UP; if (y < -MPU_THRESHOLD) orientation = ORIENTATION_LEFT_UP; if (orientation != manager->priv->prev_orientation || first) { first = FALSE; manager->priv->prev_orientation = orientation; g_debug ("Orientation changed to '%s', switching screen rotation", orientation_to_string (manager->priv->prev_orientation)); do_rotation (manager); } return !manager->priv->orientation_lock; } static gboolean gsd_orientation_manager_idle_cb (GsdOrientationManager *manager) { const char * const subsystems[] = { "input", NULL }; GUdevDevice *dev; gnome_settings_profile_start (NULL); manager->priv->start_idle_id = 0; manager->priv->settings = g_settings_new (CONF_SCHEMA); g_signal_connect (G_OBJECT (manager->priv->settings), "changed::orientation-lock", G_CALLBACK (orientation_lock_changed_cb), manager); manager->priv->orientation_lock = g_settings_get_boolean (manager->priv->settings, ORIENTATION_LOCK_KEY); manager->priv->client = g_udev_client_new (subsystems); dev = get_accelerometer (manager->priv->client); if (dev == NULL) { g_debug ("Did not find an accelerometer"); gnome_settings_profile_end (NULL); return FALSE; } manager->priv->sysfs_path = g_strdup (g_udev_device_get_sysfs_path (dev)); g_debug ("Found accelerometer at sysfs path '%s'", manager->priv->sysfs_path); manager->priv->prev_orientation = get_orientation_from_device (dev); /* Poll the sysfs attributes exposed by MPU6050 as it is not an uevent based input driver */ if (g_strcmp0 (g_udev_device_get_sysfs_attr (dev, "name"), "mpu6050") == 0) { manager->priv->prev_orientation = ORIENTATION_NORMAL; g_timeout_add_seconds(MPU_POLL_INTERVAL, (GSourceFunc) mpu_timer, manager); mpu6050_accel_x = g_build_filename(manager->priv->sysfs_path, "in_accel_x_raw", NULL); mpu6050_accel_y = g_build_filename(manager->priv->sysfs_path, "in_accel_y_raw", NULL); is_mpu6050 = TRUE; } g_object_unref (dev); /* Start process of owning a D-Bus name */ g_bus_get (G_BUS_TYPE_SESSION, NULL, (GAsyncReadyCallback) on_bus_gotten, manager); g_signal_connect (G_OBJECT (manager->priv->client), "uevent", G_CALLBACK (client_uevent_cb), manager); gnome_settings_profile_end (NULL); return FALSE; } gboolean gsd_orientation_manager_start (GsdOrientationManager *manager, GError **error) { gnome_settings_profile_start (NULL); manager->priv->start_idle_id = g_idle_add ((GSourceFunc) gsd_orientation_manager_idle_cb, manager); manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (manager->priv->introspection_data != NULL); gnome_settings_profile_end (NULL); return TRUE; } void gsd_orientation_manager_stop (GsdOrientationManager *manager) { GsdOrientationManagerPrivate *p = manager->priv; g_debug ("Stopping orientation manager"); if (p->settings) { g_object_unref (p->settings); p->settings = NULL; } if (p->sysfs_path) { g_free (p->sysfs_path); p->sysfs_path = NULL; } if (p->introspection_data) { g_dbus_node_info_unref (p->introspection_data); p->introspection_data = NULL; } if (p->client) { g_object_unref (p->client); p->client = NULL; } } static void gsd_orientation_manager_finalize (GObject *object) { GsdOrientationManager *orientation_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_ORIENTATION_MANAGER (object)); orientation_manager = GSD_ORIENTATION_MANAGER (object); g_return_if_fail (orientation_manager->priv != NULL); if (orientation_manager->priv->start_idle_id != 0) g_source_remove (orientation_manager->priv->start_idle_id); if (orientation_manager->priv->name_id != 0) g_bus_unown_name (orientation_manager->priv->name_id); G_OBJECT_CLASS (gsd_orientation_manager_parent_class)->finalize (object); } GsdOrientationManager * gsd_orientation_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_ORIENTATION_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_ORIENTATION_MANAGER (manager_object); } ./plugins/orientation/orientation.gnome-settings-plugin.in0000644000004100000410000000030212735467744024416 0ustar www-datawww-data[GNOME Settings Plugin] Module=orientation IAge=0 # Default Priority # Priority=100 _Name=Orientation _Description=Orientation plugin Authors=Peter Hutterer Copyright=Copyright © 2010 Website= ./plugins/a11y-keyboard/0000755000004100000410000000000012735467744015320 5ustar www-datawww-data./plugins/a11y-keyboard/gsd-a11y-keyboard-plugin.c0000644000004100000410000000204412735467744022104 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-a11y-keyboard-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdA11yKeyboard, gsd_a11y_keyboard) ./plugins/a11y-keyboard/gsd-a11y-keyboard-manager.h0000644000004100000410000000474512735467744022237 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_A11Y_KEYBOARD_MANAGER_H #define __GSD_A11Y_KEYBOARD_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_A11Y_KEYBOARD_MANAGER (gsd_a11y_keyboard_manager_get_type ()) #define GSD_A11Y_KEYBOARD_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_A11Y_KEYBOARD_MANAGER, GsdA11yKeyboardManager)) #define GSD_A11Y_KEYBOARD_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_A11Y_KEYBOARD_MANAGER, GsdA11yKeyboardManagerClass)) #define GSD_IS_A11Y_KEYBOARD_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_A11Y_KEYBOARD_MANAGER)) #define GSD_IS_A11Y_KEYBOARD_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_A11Y_KEYBOARD_MANAGER)) #define GSD_A11Y_KEYBOARD_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_A11Y_KEYBOARD_MANAGER, GsdA11yKeyboardManagerClass)) typedef struct GsdA11yKeyboardManagerPrivate GsdA11yKeyboardManagerPrivate; typedef struct { GObject parent; GsdA11yKeyboardManagerPrivate *priv; } GsdA11yKeyboardManager; typedef struct { GObjectClass parent_class; } GsdA11yKeyboardManagerClass; GType gsd_a11y_keyboard_manager_get_type (void); GsdA11yKeyboardManager *gsd_a11y_keyboard_manager_new (void); gboolean gsd_a11y_keyboard_manager_start (GsdA11yKeyboardManager *manager, GError **error); void gsd_a11y_keyboard_manager_stop (GsdA11yKeyboardManager *manager); G_END_DECLS #endif /* __GSD_A11Y_KEYBOARD_MANAGER_H */ ./plugins/a11y-keyboard/test-a11y-keyboard.c0000644000004100000410000000035412735467744021014 0ustar www-datawww-data#define NEW gsd_a11y_keyboard_manager_new #define START gsd_a11y_keyboard_manager_start #define STOP gsd_a11y_keyboard_manager_stop #define MANAGER GsdA11yKeyboardManager #include "gsd-a11y-keyboard-manager.h" #include "test-plugin.h" ./plugins/a11y-keyboard/Makefile.am0000644000004100000410000000327012735467744017356 0ustar www-datawww-dataNULL = plugin_name = a11y-keyboard libexec_PROGRAMS = usd-test-a11y-keyboard usd_test_a11y_keyboard_SOURCES = \ gsd-a11y-keyboard-manager.h \ gsd-a11y-keyboard-manager.c \ test-a11y-keyboard.c usd_test_a11y_keyboard_CFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DGTKBUILDERDIR=\""$(pkgdatadir)"\" \ $(PLUGIN_CFLAGS) \ $(LIBNOTIFY_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_a11y_keyboard_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(LIBNOTIFY_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_LTLIBRARIES = \ liba11y-keyboard.la \ $(NULL) liba11y_keyboard_la_SOURCES = \ gsd-a11y-keyboard-plugin.c \ gsd-a11y-keyboard-manager.h \ gsd-a11y-keyboard-manager.c \ $(NULL) liba11y_keyboard_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DGTKBUILDERDIR=\""$(gtkbuilderdir)"\" \ $(AM_CPPFLAGS) liba11y_keyboard_la_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(LIBNOTIFY_CFLAGS) \ $(AM_CFLAGS) liba11y_keyboard_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) \ $(NULL) liba11y_keyboard_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(XF86MISC_LIBS) \ $(LIBNOTIFY_LIBS) \ $(NULL) plugin_in_files = \ a11y-keyboard.gnome-settings-plugin.in \ $(NULL) plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) \ $(gtkbuilder_DATA) \ $(NULL) CLEANFILES = \ $(plugin_DATA) \ $(NULL) DISTCLEANFILES = \ $(plugin_DATA) \ $(NULL) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/a11y-keyboard/gsd-a11y-keyboard-manager.c0000644000004100000410000010142112735467744022217 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright © 2001 Ximian, Inc. * Copyright (C) 2007 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-profile.h" #include "gsd-a11y-keyboard-manager.h" #define KEYBOARD_A11Y_SCHEMA "org.gnome.desktop.a11y.keyboard" #define NOTIFICATION_TIMEOUT 30 #define GSD_A11Y_KEYBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_A11Y_KEYBOARD_MANAGER, GsdA11yKeyboardManagerPrivate)) struct GsdA11yKeyboardManagerPrivate { guint start_idle_id; int xkbEventBase; GdkDeviceManager *device_manager; guint device_added_id; gboolean stickykeys_shortcut_val; gboolean slowkeys_shortcut_val; XkbDescRec *desc; GSettings *settings; NotifyNotification *notification; }; #define DEFAULT_XKB_SET_CONTROLS_MASK XkbSlowKeysMask | \ XkbBounceKeysMask | \ XkbStickyKeysMask | \ XkbMouseKeysMask | \ XkbMouseKeysAccelMask | \ XkbAccessXKeysMask | \ XkbAccessXTimeoutMask | \ XkbAccessXFeedbackMask | \ XkbControlsEnabledMask static void gsd_a11y_keyboard_manager_class_init (GsdA11yKeyboardManagerClass *klass); static void gsd_a11y_keyboard_manager_init (GsdA11yKeyboardManager *a11y_keyboard_manager); static void gsd_a11y_keyboard_manager_finalize (GObject *object); static void set_server_from_gsettings (GsdA11yKeyboardManager *manager); G_DEFINE_TYPE (GsdA11yKeyboardManager, gsd_a11y_keyboard_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static void device_added_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdA11yKeyboardManager *manager) { if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) set_server_from_gsettings (manager); } static void set_devicepresence_handler (GsdA11yKeyboardManager *manager) { GdkDeviceManager *device_manager; device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); if (device_manager == NULL) return; manager->priv->device_manager = device_manager; manager->priv->device_added_id = g_signal_connect (G_OBJECT (device_manager), "device-added", G_CALLBACK (device_added_cb), manager); } static gboolean xkb_enabled (GsdA11yKeyboardManager *manager) { int opcode, errorBase, major, minor; if (!XkbQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &opcode, &manager->priv->xkbEventBase, &errorBase, &major, &minor)) return FALSE; if (!XkbUseExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &major, &minor)) return FALSE; return TRUE; } static XkbDescRec * get_xkb_desc_rec (GsdA11yKeyboardManager *manager) { XkbDescRec *desc; Status status = Success; gdk_error_trap_push (); desc = XkbGetMap (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XkbAllMapComponentsMask, XkbUseCoreKbd); if (desc != NULL) { desc->ctrls = NULL; status = XkbGetControls (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XkbAllControlsMask, desc); } gdk_error_trap_pop_ignored (); g_return_val_if_fail (desc != NULL, NULL); g_return_val_if_fail (desc->ctrls != NULL, NULL); g_return_val_if_fail (status == Success, NULL); return desc; } static int get_int (GSettings *settings, char const *key) { int res = g_settings_get_int (settings, key); if (res <= 0) { res = 1; } return res; } static gboolean set_int (GSettings *settings, char const *key, int val) { int prev_val; prev_val = g_settings_get_int (settings, key); g_settings_set_int (settings, key, val); if (val != prev_val) { g_debug ("%s changed", key); } return val != prev_val; } static gboolean set_bool (GSettings *settings, char const *key, int val) { gboolean bval = (val != 0); gboolean prev_val; prev_val = g_settings_get_boolean (settings, key); g_settings_set_boolean (settings, key, bval ? TRUE : FALSE); if (bval != prev_val) { g_debug ("%s changed", key); return TRUE; } return (bval != prev_val); } static unsigned long set_clear (gboolean flag, unsigned long value, unsigned long mask) { if (flag) { return value | mask; } return value & ~mask; } static gboolean set_ctrl_from_gsettings (XkbDescRec *desc, GSettings *settings, char const *key, unsigned long mask) { gboolean result = g_settings_get_boolean (settings, key); desc->ctrls->enabled_ctrls = set_clear (result, desc->ctrls->enabled_ctrls, mask); return result; } static void set_server_from_gsettings (GsdA11yKeyboardManager *manager) { XkbDescRec *desc; gboolean enable_accessX; GSettings *settings; gnome_settings_profile_start (NULL); desc = get_xkb_desc_rec (manager); if (!desc) { return; } settings = manager->priv->settings; /* general */ enable_accessX = g_settings_get_boolean (settings, "enable"); desc->ctrls->enabled_ctrls = set_clear (enable_accessX, desc->ctrls->enabled_ctrls, XkbAccessXKeysMask); if (set_ctrl_from_gsettings (desc, settings, "timeout-enable", XkbAccessXTimeoutMask)) { desc->ctrls->ax_timeout = get_int (settings, "disable-timeout"); /* disable only the master flag via the server we will disable * the rest on the rebound without affecting GSettings state * don't change the option flags at all. */ desc->ctrls->axt_ctrls_mask = XkbAccessXKeysMask | XkbAccessXFeedbackMask; desc->ctrls->axt_ctrls_values = 0; desc->ctrls->axt_opts_mask = 0; } desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "feature-state-change-beep"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask); /* bounce keys */ if (set_ctrl_from_gsettings (desc, settings, "bouncekeys-enable", XkbBounceKeysMask)) { desc->ctrls->debounce_delay = get_int (settings, "bouncekeys-delay"); desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "bouncekeys-beep-reject"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_BKRejectFBMask); } /* mouse keys */ if (set_ctrl_from_gsettings (desc, settings, "mousekeys-enable", XkbMouseKeysMask | XkbMouseKeysAccelMask)) { desc->ctrls->mk_interval = 100; /* msec between mousekey events */ desc->ctrls->mk_curve = 50; /* We store pixels / sec, XKB wants pixels / event */ desc->ctrls->mk_max_speed = get_int (settings, "mousekeys-max-speed") / (1000 / desc->ctrls->mk_interval); if (desc->ctrls->mk_max_speed <= 0) desc->ctrls->mk_max_speed = 1; desc->ctrls->mk_time_to_max = get_int (settings, /* events before max */ "mousekeys-accel-time") / desc->ctrls->mk_interval; if (desc->ctrls->mk_time_to_max <= 0) desc->ctrls->mk_time_to_max = 1; desc->ctrls->mk_delay = get_int (settings, /* ms before 1st event */ "mousekeys-init-delay"); } /* slow keys */ if (set_ctrl_from_gsettings (desc, settings, "slowkeys-enable", XkbSlowKeysMask)) { desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "slowkeys-beep-press"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_SKPressFBMask); desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "slowkeys-beep-accept"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_SKAcceptFBMask); desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "slowkeys-beep-reject"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_SKRejectFBMask); desc->ctrls->slow_keys_delay = get_int (settings, "slowkeys-delay"); /* anything larger than 500 seems to loose all keyboard input */ if (desc->ctrls->slow_keys_delay > 500) desc->ctrls->slow_keys_delay = 500; } /* sticky keys */ if (set_ctrl_from_gsettings (desc, settings, "stickykeys-enable", XkbStickyKeysMask)) { desc->ctrls->ax_options |= XkbAX_LatchToLockMask; desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "stickykeys-two-key-off"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_TwoKeysMask); desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "stickykeys-modifier-beep"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_StickyKeysFBMask); } /* toggle keys */ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "togglekeys-enable"), desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_IndicatorFBMask); /* g_debug ("CHANGE to : 0x%x", desc->ctrls->enabled_ctrls); g_debug ("CHANGE to : 0x%x (2)", desc->ctrls->ax_options); */ gdk_error_trap_push (); XkbSetControls (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), DEFAULT_XKB_SET_CONTROLS_MASK, desc); XkbFreeKeyboard (desc, XkbAllComponentsMask, True); XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); gdk_error_trap_pop_ignored (); gnome_settings_profile_end (NULL); } static void ax_response_callback (GsdA11yKeyboardManager *manager, const char *action, guint revert_controls_mask, gboolean enabled) { if (g_strcmp0 (action, "reject") == 0) { /* we're reverting, so we invert sense of 'enabled' flag */ g_debug ("cancelling AccessX request"); if (revert_controls_mask == XkbStickyKeysMask) { g_settings_set_boolean (manager->priv->settings, "stickykeys-enable", !enabled); } else if (revert_controls_mask == XkbSlowKeysMask) { g_settings_set_boolean (manager->priv->settings, "slowkeys-enable", !enabled); } set_server_from_gsettings (manager); } } static void on_notification_closed (NotifyNotification *notification, GsdA11yKeyboardManager *manager) { g_object_unref (manager->priv->notification); manager->priv->notification = NULL; } static void on_slow_keys_action (NotifyNotification *notification, const char *action, GsdA11yKeyboardManager *manager) { g_assert (action != NULL); ax_response_callback (manager, action, XkbSlowKeysMask, manager->priv->slowkeys_shortcut_val); notify_notification_close (manager->priv->notification, NULL); } static void on_sticky_keys_action (NotifyNotification *notification, const char *action, GsdA11yKeyboardManager *manager) { g_assert (action != NULL); ax_response_callback (manager, action, XkbStickyKeysMask, manager->priv->stickykeys_shortcut_val); notify_notification_close (manager->priv->notification, NULL); } static gboolean ax_slowkeys_warning_post_bubble (GsdA11yKeyboardManager *manager, gboolean enabled) { gboolean res; const char *title; const char *message; GError *error; title = enabled ? _("Slow Keys Turned On") : _("Slow Keys Turned Off"); message = _("You just held down the Shift key for 8 seconds. This is the shortcut " "for the Slow Keys feature, which affects the way your keyboard works."); if (manager->priv->notification != NULL) { notify_notification_close (manager->priv->notification, NULL); } manager->priv->notification = notify_notification_new (title, message, "preferences-desktop-accessibility-symbolic"); notify_notification_set_app_name (manager->priv->notification, _("Universal Access")); notify_notification_set_timeout (manager->priv->notification, NOTIFICATION_TIMEOUT * 1000); notify_notification_set_hint (manager->priv->notification, "transient", g_variant_new_boolean (TRUE)); notify_notification_add_action (manager->priv->notification, "reject", enabled ? _("Turn Off") : _("Turn On"), (NotifyActionCallback) on_slow_keys_action, manager, NULL); notify_notification_add_action (manager->priv->notification, "accept", enabled ? _("Leave On") : _("Leave Off"), (NotifyActionCallback) on_slow_keys_action, manager, NULL); g_signal_connect (manager->priv->notification, "closed", G_CALLBACK (on_notification_closed), manager); error = NULL; res = notify_notification_show (manager->priv->notification, &error); if (! res) { g_warning ("GsdA11yKeyboardManager: unable to show notification: %s", error->message); g_error_free (error); notify_notification_close (manager->priv->notification, NULL); } return res; } static void ax_slowkeys_warning_post (GsdA11yKeyboardManager *manager, gboolean enabled) { manager->priv->slowkeys_shortcut_val = enabled; ax_slowkeys_warning_post_bubble (manager, enabled); } static gboolean ax_stickykeys_warning_post_bubble (GsdA11yKeyboardManager *manager, gboolean enabled) { gboolean res; const char *title; const char *message; GError *error; title = enabled ? _("Sticky Keys Turned On") : _("Sticky Keys Turned Off"); message = enabled ? _("You just pressed the Shift key 5 times in a row. This is the shortcut " "for the Sticky Keys feature, which affects the way your keyboard works.") : _("You just pressed two keys at once, or pressed the Shift key 5 times in a row. " "This turns off the Sticky Keys feature, which affects the way your keyboard works."); if (manager->priv->notification != NULL) { notify_notification_close (manager->priv->notification, NULL); } manager->priv->notification = notify_notification_new (title, message, "preferences-desktop-accessibility-symbolic"); notify_notification_set_app_name (manager->priv->notification, _("Universal Access")); notify_notification_set_timeout (manager->priv->notification, NOTIFICATION_TIMEOUT * 1000); notify_notification_set_hint (manager->priv->notification, "transient", g_variant_new_boolean (TRUE)); notify_notification_add_action (manager->priv->notification, "reject", enabled ? _("Turn Off") : _("Turn On"), (NotifyActionCallback) on_sticky_keys_action, manager, NULL); notify_notification_add_action (manager->priv->notification, "accept", enabled ? _("Leave On") : _("Leave Off"), (NotifyActionCallback) on_sticky_keys_action, manager, NULL); g_signal_connect (manager->priv->notification, "closed", G_CALLBACK (on_notification_closed), manager); error = NULL; res = notify_notification_show (manager->priv->notification, &error); if (! res) { g_warning ("GsdA11yKeyboardManager: unable to show notification: %s", error->message); g_error_free (error); notify_notification_close (manager->priv->notification, NULL); } return res; } static void ax_stickykeys_warning_post (GsdA11yKeyboardManager *manager, gboolean enabled) { manager->priv->stickykeys_shortcut_val = enabled; ax_stickykeys_warning_post_bubble (manager, enabled); } static void set_gsettings_from_server (GsdA11yKeyboardManager *manager) { XkbDescRec *desc; gboolean changed = FALSE; gboolean slowkeys_changed; gboolean stickykeys_changed; GSettings *settings; desc = get_xkb_desc_rec (manager); if (! desc) { return; } /* Create a new one, so that only those settings * are delayed */ settings = g_settings_new (KEYBOARD_A11Y_SCHEMA); g_settings_delay (settings); /* fprintf (stderr, "changed to : 0x%x\n", desc->ctrls->enabled_ctrls); fprintf (stderr, "changed to : 0x%x (2)\n", desc->ctrls->ax_options); */ changed |= set_bool (settings, "enable", desc->ctrls->enabled_ctrls & XkbAccessXKeysMask); changed |= set_bool (settings, "feature-state-change-beep", desc->ctrls->ax_options & (XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask)); changed |= set_bool (settings, "timeout-enable", desc->ctrls->enabled_ctrls & XkbAccessXTimeoutMask); changed |= set_int (settings, "disable-timeout", desc->ctrls->ax_timeout); changed |= set_bool (settings, "bouncekeys-enable", desc->ctrls->enabled_ctrls & XkbBounceKeysMask); changed |= set_int (settings, "bouncekeys-delay", desc->ctrls->debounce_delay); changed |= set_bool (settings, "bouncekeys-beep-reject", desc->ctrls->ax_options & XkbAX_BKRejectFBMask); changed |= set_bool (settings, "mousekeys-enable", desc->ctrls->enabled_ctrls & XkbMouseKeysMask); changed |= set_int (settings, "mousekeys-max-speed", desc->ctrls->mk_max_speed * (1000 / desc->ctrls->mk_interval)); /* NOTE : mk_time_to_max is measured in events not time */ changed |= set_int (settings, "mousekeys-accel-time", desc->ctrls->mk_time_to_max * desc->ctrls->mk_interval); changed |= set_int (settings, "mousekeys-init-delay", desc->ctrls->mk_delay); slowkeys_changed = set_bool (settings, "slowkeys-enable", desc->ctrls->enabled_ctrls & XkbSlowKeysMask); changed |= set_bool (settings, "slowkeys-beep-press", desc->ctrls->ax_options & XkbAX_SKPressFBMask); changed |= set_bool (settings, "slowkeys-beep-accept", desc->ctrls->ax_options & XkbAX_SKAcceptFBMask); changed |= set_bool (settings, "slowkeys-beep-reject", desc->ctrls->ax_options & XkbAX_SKRejectFBMask); changed |= set_int (settings, "slowkeys-delay", desc->ctrls->slow_keys_delay); stickykeys_changed = set_bool (settings, "stickykeys-enable", desc->ctrls->enabled_ctrls & XkbStickyKeysMask); changed |= set_bool (settings, "stickykeys-two-key-off", desc->ctrls->ax_options & XkbAX_TwoKeysMask); changed |= set_bool (settings, "stickykeys-modifier-beep", desc->ctrls->ax_options & XkbAX_StickyKeysFBMask); changed |= set_bool (settings, "togglekeys-enable", desc->ctrls->ax_options & XkbAX_IndicatorFBMask); if (!changed && stickykeys_changed ^ slowkeys_changed) { /* * sticky or slowkeys has changed, singly, without our intervention. * 99% chance this is due to a keyboard shortcut being used. * we need to detect via this hack until we get * XkbAXN_AXKWarning notifications working (probable XKB bug), * at which time we can directly intercept such shortcuts instead. * See cb_xkb_event_filter () below. */ /* sanity check: are keyboard shortcuts available? */ if (desc->ctrls->enabled_ctrls & XkbAccessXKeysMask) { if (slowkeys_changed) { ax_slowkeys_warning_post (manager, desc->ctrls->enabled_ctrls & XkbSlowKeysMask); } else { ax_stickykeys_warning_post (manager, desc->ctrls->enabled_ctrls & XkbStickyKeysMask); } } } XkbFreeKeyboard (desc, XkbAllComponentsMask, True); g_settings_apply (settings); g_object_unref (settings); } static GdkFilterReturn cb_xkb_event_filter (GdkXEvent *xevent, GdkEvent *ignored1, GsdA11yKeyboardManager *manager) { XEvent *xev = (XEvent *) xevent; XkbEvent *xkbEv = (XkbEvent *) xevent; /* 'event_type' is set to zero on notifying us of updates in * response to client requests (including our own) and non-zero * to notify us of key/mouse events causing changes (like * pressing shift 5 times to enable sticky keys). * * We only want to update GSettings when it's in response to an * explicit user input event, so require a non-zero event_type. */ if (xev->xany.type == (manager->priv->xkbEventBase + XkbEventCode) && xkbEv->any.xkb_type == XkbControlsNotify && xkbEv->ctrls.event_type != 0) { g_debug ("XKB state changed"); set_gsettings_from_server (manager); } else if (xev->xany.type == (manager->priv->xkbEventBase + XkbEventCode) && xkbEv->any.xkb_type == XkbAccessXNotify) { if (xkbEv->accessx.detail == XkbAXN_AXKWarning) { g_debug ("About to turn on an AccessX feature from the keyboard!"); /* * TODO: when XkbAXN_AXKWarnings start working, we need to * invoke ax_keys_warning_dialog_run here instead of in * set_gsettings_from_server(). */ } } return GDK_FILTER_CONTINUE; } static void keyboard_callback (GSettings *settings, const char *key, GsdA11yKeyboardManager *manager) { set_server_from_gsettings (manager); } static gboolean start_a11y_keyboard_idle_cb (GsdA11yKeyboardManager *manager) { guint event_mask; g_debug ("Starting a11y_keyboard manager"); gnome_settings_profile_start (NULL); if (!xkb_enabled (manager)) goto out; manager->priv->settings = g_settings_new (KEYBOARD_A11Y_SCHEMA); g_signal_connect (G_OBJECT (manager->priv->settings), "changed", G_CALLBACK (keyboard_callback), manager); set_devicepresence_handler (manager); /* Get the original configuration from the server */ manager->priv->desc = get_xkb_desc_rec (manager); event_mask = XkbControlsNotifyMask; event_mask |= XkbAccessXNotifyMask; /* make default when AXN_AXKWarning works */ /* be sure to init before starting to monitor the server */ set_server_from_gsettings (manager); XkbSelectEvents (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XkbUseCoreKbd, event_mask, event_mask); gdk_window_add_filter (NULL, (GdkFilterFunc) cb_xkb_event_filter, manager); out: gnome_settings_profile_end (NULL); manager->priv->start_idle_id = 0; return FALSE; } gboolean gsd_a11y_keyboard_manager_start (GsdA11yKeyboardManager *manager, GError **error) { gnome_settings_profile_start (NULL); manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_a11y_keyboard_idle_cb, manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_a11y_keyboard_manager_stop (GsdA11yKeyboardManager *manager) { GsdA11yKeyboardManagerPrivate *p = manager->priv; g_debug ("Stopping a11y_keyboard manager"); if (p->desc != NULL) { XkbDescRec *desc; desc = get_xkb_desc_rec (manager); if (desc != NULL) { if (p->desc->ctrls->enabled_ctrls != desc->ctrls->enabled_ctrls) { gdk_error_trap_push (); XkbSetControls (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), DEFAULT_XKB_SET_CONTROLS_MASK, p->desc); XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); gdk_error_trap_pop_ignored (); } XkbFreeKeyboard (desc, XkbAllComponentsMask, True); } XkbFreeKeyboard (p->desc, XkbAllComponentsMask, True); p->desc = NULL; } if (p->start_idle_id != 0) { g_source_remove (p->start_idle_id); p->start_idle_id = 0; } if (p->device_manager != NULL) { g_signal_handler_disconnect (p->device_manager, p->device_added_id); p->device_manager = NULL; } if (p->settings != NULL) { g_signal_handlers_disconnect_by_func (p->settings, keyboard_callback, manager); g_object_unref (p->settings); p->settings = NULL; } gdk_window_remove_filter (NULL, (GdkFilterFunc) cb_xkb_event_filter, manager); p->slowkeys_shortcut_val = FALSE; p->stickykeys_shortcut_val = FALSE; } static void gsd_a11y_keyboard_manager_class_init (GsdA11yKeyboardManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_a11y_keyboard_manager_finalize; g_type_class_add_private (klass, sizeof (GsdA11yKeyboardManagerPrivate)); } static void gsd_a11y_keyboard_manager_init (GsdA11yKeyboardManager *manager) { manager->priv = GSD_A11Y_KEYBOARD_MANAGER_GET_PRIVATE (manager); } static void gsd_a11y_keyboard_manager_finalize (GObject *object) { GsdA11yKeyboardManager *a11y_keyboard_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_A11Y_KEYBOARD_MANAGER (object)); a11y_keyboard_manager = GSD_A11Y_KEYBOARD_MANAGER (object); g_return_if_fail (a11y_keyboard_manager->priv != NULL); gsd_a11y_keyboard_manager_stop (a11y_keyboard_manager); G_OBJECT_CLASS (gsd_a11y_keyboard_manager_parent_class)->finalize (object); } GsdA11yKeyboardManager * gsd_a11y_keyboard_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_A11Y_KEYBOARD_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_A11Y_KEYBOARD_MANAGER (manager_object); } ./plugins/a11y-keyboard/a11y-keyboard.gnome-settings-plugin.in0000644000004100000410000000031712735467744024460 0ustar www-datawww-data[GNOME Settings Plugin] Module=a11y-keyboard IAge=0 Priority=7 _Name=Accessibility Keyboard _Description=Accessibility keyboard plugin Authors=Jody Goldberg Copyright=Copyright © 2001 Ximian, Inc. Website= ./plugins/sound/0000755000004100000410000000000012735467744014077 5ustar www-datawww-data./plugins/sound/test-sound.c0000644000004100000410000000030512735467744016346 0ustar www-datawww-data#define NEW gsd_sound_manager_new #define START gsd_sound_manager_start #define STOP gsd_sound_manager_stop #define MANAGER GsdSoundManager #include "gsd-sound-manager.h" #include "test-plugin.h" ./plugins/sound/Makefile.am0000644000004100000410000000246712735467744016144 0ustar www-datawww-dataplugin_name = sound libexec_PROGRAMS = usd-test-sound usd_test_sound_SOURCES = \ gsd-sound-manager.h \ gsd-sound-manager.c \ test-sound.c usd_test_sound_CFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(SOUND_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_sound_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(SOUND_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_LTLIBRARIES = \ libsound.la libsound_la_SOURCES = \ gsd-sound-plugin.c \ gsd-sound-manager.h \ gsd-sound-manager.c libsound_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libsound_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(SOUND_CFLAGS) \ $(AM_CFLAGS) libsound_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libsound_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(SOUND_LIBS) plugin_in_files = \ sound.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/sound/sound.gnome-settings-plugin.in0000644000004100000410000000025212735467744022014 0ustar www-datawww-data[GNOME Settings Plugin] Module=sound IAge=0 Priority=5 _Name=Sound _Description=Sound Sample Cache plugin Authors=Lennart Poettering Copyright=Copyright © 2008 Website= ./plugins/sound/gsd-sound-plugin.c0000644000004100000410000000202512735467744017441 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Lennart Poettering * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-sound-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdSound, gsd_sound) ./plugins/sound/gsd-sound-manager.c0000644000004100000410000002725612735467744017572 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Lennart Poettering * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gsd-sound-manager.h" #include "gnome-settings-profile.h" #define GSD_SOUND_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_SOUND_MANAGER, GsdSoundManagerPrivate)) struct GsdSoundManagerPrivate { GSettings *settings; GList *monitors; guint timeout; }; static void gsd_sound_manager_class_init (GsdSoundManagerClass *klass); static void gsd_sound_manager_init (GsdSoundManager *sound_manager); static void gsd_sound_manager_finalize (GObject *object); G_DEFINE_TYPE (GsdSoundManager, gsd_sound_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static void sample_info_cb (pa_context *c, const pa_sample_info *i, int eol, void *userdata) { pa_operation *o; if (!i) return; g_debug ("Found sample %s", i->name); /* We only flush those samples which have an XDG sound name * attached, because only those originate from themeing */ if (!(pa_proplist_gets (i->proplist, PA_PROP_EVENT_ID))) return; g_debug ("Dropping sample %s from cache", i->name); if (!(o = pa_context_remove_sample (c, i->name, NULL, NULL))) { g_debug ("pa_context_remove_sample (): %s", pa_strerror (pa_context_errno (c))); return; } pa_operation_unref (o); /* We won't wait until the operation is actually executed to * speed things up a bit.*/ } static void flush_cache (void) { pa_mainloop *ml = NULL; pa_context *c = NULL; pa_proplist *pl = NULL; pa_operation *o = NULL; g_debug ("Flushing sample cache"); if (!(ml = pa_mainloop_new ())) { g_debug ("Failed to allocate pa_mainloop"); goto fail; } if (!(pl = pa_proplist_new ())) { g_debug ("Failed to allocate pa_proplist"); goto fail; } pa_proplist_sets (pl, PA_PROP_APPLICATION_NAME, PACKAGE_NAME); pa_proplist_sets (pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION); pa_proplist_sets (pl, PA_PROP_APPLICATION_ID, "org.gnome.SettingsDaemon"); if (!(c = pa_context_new_with_proplist (pa_mainloop_get_api (ml), PACKAGE_NAME, pl))) { g_debug ("Failed to allocate pa_context"); goto fail; } pa_proplist_free (pl); pl = NULL; if (pa_context_connect (c, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { g_debug ("pa_context_connect(): %s", pa_strerror (pa_context_errno (c))); goto fail; } /* Wait until the connection is established */ while (pa_context_get_state (c) != PA_CONTEXT_READY) { if (!PA_CONTEXT_IS_GOOD (pa_context_get_state (c))) { g_debug ("Connection failed: %s", pa_strerror (pa_context_errno (c))); goto fail; } if (pa_mainloop_iterate (ml, TRUE, NULL) < 0) { g_debug ("pa_mainloop_iterate() failed"); goto fail; } } /* Enumerate all cached samples */ if (!(o = pa_context_get_sample_info_list (c, sample_info_cb, NULL))) { g_debug ("pa_context_get_sample_info_list(): %s", pa_strerror (pa_context_errno (c))); goto fail; } /* Wait until our operation is finished and there's nothing * more queued to send to the server */ while (pa_operation_get_state (o) == PA_OPERATION_RUNNING || pa_context_is_pending (c)) { if (!PA_CONTEXT_IS_GOOD (pa_context_get_state (c))) { g_debug ("Connection failed: %s", pa_strerror (pa_context_errno (c))); goto fail; } if (pa_mainloop_iterate (ml, TRUE, NULL) < 0) { g_debug ("pa_mainloop_iterate() failed"); goto fail; } } g_debug ("Sample cache flushed"); fail: if (o) { pa_operation_cancel (o); pa_operation_unref (o); } if (c) { pa_context_disconnect (c); pa_context_unref (c); } if (pl) pa_proplist_free (pl); if (ml) pa_mainloop_free (ml); } static gboolean flush_cb (GsdSoundManager *manager) { flush_cache (); manager->priv->timeout = 0; return FALSE; } static void trigger_flush (GsdSoundManager *manager) { if (manager->priv->timeout) g_source_remove (manager->priv->timeout); /* We delay the flushing a bit so that we can coalesce * multiple changes into a single cache flush */ manager->priv->timeout = g_timeout_add (500, (GSourceFunc) flush_cb, manager); } static void settings_changed_cb (GSettings *settings, const char *key, GsdSoundManager *manager) { trigger_flush (manager); } static void register_config_callback (GsdSoundManager *manager) { manager->priv->settings = g_settings_new ("org.gnome.desktop.sound"); g_signal_connect (G_OBJECT (manager->priv->settings), "changed", G_CALLBACK (settings_changed_cb), manager); } static void file_monitor_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event, GsdSoundManager *manager) { g_debug ("Theme dir changed"); trigger_flush (manager); } static gboolean register_directory_callback (GsdSoundManager *manager, const char *path, GError **error) { GFile *f; GFileMonitor *m; gboolean succ = FALSE; g_debug ("Registering directory monitor for %s", path); f = g_file_new_for_path (path); m = g_file_monitor_directory (f, 0, NULL, error); if (m != NULL) { g_signal_connect (m, "changed", G_CALLBACK (file_monitor_changed_cb), manager); manager->priv->monitors = g_list_prepend (manager->priv->monitors, m); succ = TRUE; } g_object_unref (f); return succ; } gboolean gsd_sound_manager_start (GsdSoundManager *manager, GError **error) { guint i; const gchar * const * dirs; char *p; g_debug ("Starting sound manager"); gnome_settings_profile_start (NULL); /* We listen for change of the selected theme ... */ register_config_callback (manager); /* ... and we listen to changes of the theme base directories * in $HOME ...*/ p = g_build_filename (g_get_user_data_dir (), "sounds", NULL); /* See bug #694134 - the initial commit had the permissions below wrong * so users may have the directory without the right permissions. Fix * them up if so. */ if (!g_access (p, F_OK) && g_access (p, R_OK | W_OK | X_OK) != 0) { g_debug ("Permissions on %s wrong; resetting", p); g_chmod (p, S_IRUSR | S_IWUSR | S_IXUSR); } if (g_mkdir_with_parents(p, 0700) == 0) register_directory_callback (manager, p, NULL); g_free (p); /* ... and globally. */ dirs = g_get_system_data_dirs (); for (i = 0; dirs[i] != NULL; i++) { p = g_build_filename (dirs[i], "sounds", NULL); if (g_file_test (p, G_FILE_TEST_IS_DIR)) register_directory_callback (manager, p, NULL); g_free (p); } gnome_settings_profile_end (NULL); return TRUE; } void gsd_sound_manager_stop (GsdSoundManager *manager) { g_debug ("Stopping sound manager"); if (manager->priv->settings != NULL) { g_object_unref (manager->priv->settings); manager->priv->settings = NULL; } if (manager->priv->timeout) { g_source_remove (manager->priv->timeout); manager->priv->timeout = 0; } while (manager->priv->monitors) { g_file_monitor_cancel (G_FILE_MONITOR (manager->priv->monitors->data)); g_object_unref (manager->priv->monitors->data); manager->priv->monitors = g_list_delete_link (manager->priv->monitors, manager->priv->monitors); } } static GObject * gsd_sound_manager_constructor ( GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdSoundManager *m; m = GSD_SOUND_MANAGER (G_OBJECT_CLASS (gsd_sound_manager_parent_class)->constructor ( type, n_construct_properties, construct_properties)); return G_OBJECT (m); } static void gsd_sound_manager_dispose (GObject *object) { GsdSoundManager *manager; manager = GSD_SOUND_MANAGER (object); gsd_sound_manager_stop (manager); G_OBJECT_CLASS (gsd_sound_manager_parent_class)->dispose (object); } static void gsd_sound_manager_class_init (GsdSoundManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_sound_manager_constructor; object_class->dispose = gsd_sound_manager_dispose; object_class->finalize = gsd_sound_manager_finalize; g_type_class_add_private (klass, sizeof (GsdSoundManagerPrivate)); } static void gsd_sound_manager_init (GsdSoundManager *manager) { manager->priv = GSD_SOUND_MANAGER_GET_PRIVATE (manager); } static void gsd_sound_manager_finalize (GObject *object) { GsdSoundManager *sound_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_SOUND_MANAGER (object)); sound_manager = GSD_SOUND_MANAGER (object); g_return_if_fail (sound_manager->priv); G_OBJECT_CLASS (gsd_sound_manager_parent_class)->finalize (object); } GsdSoundManager * gsd_sound_manager_new (void) { if (manager_object) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_SOUND_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_SOUND_MANAGER (manager_object); } ./plugins/sound/gsd-sound-manager.h0000644000004100000410000000412012735467744017560 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Lennart Poettering * * 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. * */ #ifndef __GSD_SOUND_MANAGER_H #define __GSD_SOUND_MANAGER_H #include #include G_BEGIN_DECLS #define GSD_TYPE_SOUND_MANAGER (gsd_sound_manager_get_type ()) #define GSD_SOUND_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_SOUND_MANAGER, GsdSoundManager)) #define GSD_SOUND_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSD_TYPE_SOUND_MANAGER, GsdSoundManagerClass)) #define GSD_IS_SOUND_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_SOUND_MANAGER)) #define GSD_IS_SOUND_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_SOUND_MANAGER)) #define GSD_SOUND_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_SOUND_MANAGER, GsdSoundManagerClass)) typedef struct GsdSoundManagerPrivate GsdSoundManagerPrivate; typedef struct { GObject parent; GsdSoundManagerPrivate *priv; } GsdSoundManager; typedef struct { GObjectClass parent_class; } GsdSoundManagerClass; GType gsd_sound_manager_get_type (void) G_GNUC_CONST; GsdSoundManager *gsd_sound_manager_new (void); gboolean gsd_sound_manager_start (GsdSoundManager *manager, GError **error); void gsd_sound_manager_stop (GsdSoundManager *manager); G_END_DECLS #endif /* __GSD_SOUND_MANAGER_H */ ./plugins/dummy/0000755000004100000410000000000012735467744014102 5ustar www-datawww-data./plugins/dummy/Makefile.am0000644000004100000410000000155612735467744016145 0ustar www-datawww-dataplugin_name = dummy plugin_LTLIBRARIES = \ libdummy.la libdummy_la_SOURCES = \ gsd-dummy-manager.c \ gsd-dummy-manager.h \ gsd-dummy-plugin.c libdummy_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libdummy_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) libdummy_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libdummy_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = \ dummy.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ # override to _not_ install the test plugin # do not copy into your plugin install-pluginDATA: install-pluginLTLIBRARIES: ./plugins/dummy/gsd-dummy-manager.c0000644000004100000410000001221312735467744017563 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-profile.h" #include "gsd-dummy-manager.h" #define GSD_DUMMY_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_DUMMY_MANAGER, GsdDummyManagerPrivate)) struct GsdDummyManagerPrivate { gboolean padding; }; enum { PROP_0, }; static void gsd_dummy_manager_class_init (GsdDummyManagerClass *klass); static void gsd_dummy_manager_init (GsdDummyManager *dummy_manager); static void gsd_dummy_manager_finalize (GObject *object); G_DEFINE_TYPE (GsdDummyManager, gsd_dummy_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; gboolean gsd_dummy_manager_start (GsdDummyManager *manager, GError **error) { g_debug ("Starting dummy manager"); gnome_settings_profile_start (NULL); gnome_settings_profile_end (NULL); return TRUE; } void gsd_dummy_manager_stop (GsdDummyManager *manager) { g_debug ("Stopping dummy manager"); } static void gsd_dummy_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_dummy_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static GObject * gsd_dummy_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdDummyManager *dummy_manager; dummy_manager = GSD_DUMMY_MANAGER (G_OBJECT_CLASS (gsd_dummy_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (dummy_manager); } static void gsd_dummy_manager_dispose (GObject *object) { G_OBJECT_CLASS (gsd_dummy_manager_parent_class)->dispose (object); } static void gsd_dummy_manager_class_init (GsdDummyManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = gsd_dummy_manager_get_property; object_class->set_property = gsd_dummy_manager_set_property; object_class->constructor = gsd_dummy_manager_constructor; object_class->dispose = gsd_dummy_manager_dispose; object_class->finalize = gsd_dummy_manager_finalize; g_type_class_add_private (klass, sizeof (GsdDummyManagerPrivate)); } static void gsd_dummy_manager_init (GsdDummyManager *manager) { manager->priv = GSD_DUMMY_MANAGER_GET_PRIVATE (manager); } static void gsd_dummy_manager_finalize (GObject *object) { GsdDummyManager *dummy_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_DUMMY_MANAGER (object)); dummy_manager = GSD_DUMMY_MANAGER (object); g_return_if_fail (dummy_manager->priv != NULL); G_OBJECT_CLASS (gsd_dummy_manager_parent_class)->finalize (object); } GsdDummyManager * gsd_dummy_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_DUMMY_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_DUMMY_MANAGER (manager_object); } ./plugins/dummy/gsd-dummy-manager.h0000644000004100000410000000437012735467744017575 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_DUMMY_MANAGER_H #define __GSD_DUMMY_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_DUMMY_MANAGER (gsd_dummy_manager_get_type ()) #define GSD_DUMMY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_DUMMY_MANAGER, GsdDummyManager)) #define GSD_DUMMY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_DUMMY_MANAGER, GsdDummyManagerClass)) #define GSD_IS_DUMMY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_DUMMY_MANAGER)) #define GSD_IS_DUMMY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_DUMMY_MANAGER)) #define GSD_DUMMY_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_DUMMY_MANAGER, GsdDummyManagerClass)) typedef struct GsdDummyManagerPrivate GsdDummyManagerPrivate; typedef struct { GObject parent; GsdDummyManagerPrivate *priv; } GsdDummyManager; typedef struct { GObjectClass parent_class; } GsdDummyManagerClass; GType gsd_dummy_manager_get_type (void); GsdDummyManager * gsd_dummy_manager_new (void); gboolean gsd_dummy_manager_start (GsdDummyManager *manager, GError **error); void gsd_dummy_manager_stop (GsdDummyManager *manager); G_END_DECLS #endif /* __GSD_DUMMY_MANAGER_H */ ./plugins/dummy/dummy.gnome-settings-plugin.in0000644000004100000410000000027512735467744022027 0ustar www-datawww-data[GNOME Settings Plugin] Module=dummy IAge=0 # 100 is the default load Priority Priority=100 _Name=Dummy _Description=Dummy plugin Authors=AUTHOR Copyright=Copyright © 2007 AUTHOR Website= ./plugins/dummy/gsd-dummy-plugin.c0000644000004100000410000000201512735467744017446 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-dummy-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdDummy, gsd_dummy) ./plugins/remote-display/0000755000004100000410000000000012735467763015706 5ustar www-datawww-data./plugins/remote-display/test-remote-display.c0000644000004100000410000000036112735467744021764 0ustar www-datawww-data#define NEW gsd_remote_display_manager_new #define START gsd_remote_display_manager_start #define STOP gsd_remote_display_manager_stop #define MANAGER GsdRemoteDisplayManager #include "gsd-remote-display-manager.h" #include "test-plugin.h" ./plugins/remote-display/gsd-remote-display-manager.h0000644000004100000410000000503512735467744023202 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 Bastien Nocera * * 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. * */ #ifndef __GSD_REMOTE_DISPLAY_MANAGER_H #define __GSD_REMOTE_DISPLAY_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_REMOTE_DISPLAY_MANAGER (gsd_remote_display_manager_get_type ()) #define GSD_REMOTE_DISPLAY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_REMOTE_DISPLAY_MANAGER, GsdRemoteDisplayManager)) #define GSD_REMOTE_DISPLAY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_REMOTE_DISPLAY_MANAGER, GsdRemoteDisplayManagerClass)) #define GSD_IS_REMOTE_DISPLAY_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_REMOTE_DISPLAY_MANAGER)) #define GSD_IS_REMOTE_DISPLAY_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_REMOTE_DISPLAY_MANAGER)) #define GSD_REMOTE_DISPLAY_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_REMOTE_DISPLAY_MANAGER, GsdRemoteDisplayManagerClass)) typedef struct GsdRemoteDisplayManagerPrivate GsdRemoteDisplayManagerPrivate; typedef struct { GObject parent; GsdRemoteDisplayManagerPrivate *priv; } GsdRemoteDisplayManager; typedef struct { GObjectClass parent_class; } GsdRemoteDisplayManagerClass; GType gsd_remote_display_manager_get_type (void); GsdRemoteDisplayManager *gsd_remote_display_manager_new (void); gboolean gsd_remote_display_manager_start (GsdRemoteDisplayManager *manager, GError **error); void gsd_remote_display_manager_stop (GsdRemoteDisplayManager *manager); G_END_DECLS #endif /* __GSD_REMOTE_DISPLAY_MANAGER_H */ ./plugins/remote-display/Makefile.am0000644000004100000410000000301512735467744017740 0ustar www-datawww-dataplugin_name = remote-display plugin_LTLIBRARIES = libremote-display.la libremote_display_la_SOURCES = \ gsd-remote-display-manager.c \ gsd-remote-display-manager.h \ gsd-remote-display-plugin.c libremote_display_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libremote_display_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) libremote_display_la_LDFLAGS = $(GSD_PLUGIN_LDFLAGS) libremote_display_la_LIBADD = $(SETTINGS_PLUGIN_LIBS) libexec_PROGRAMS = usd-test-remote-display usd_test_remote_display_SOURCES = \ test-remote-display.c \ gsd-remote-display-manager.c \ gsd-remote-display-manager.h usd_test_remote_display_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) usd_test_remote_display_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_remote_display_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_DAEMON_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = remote-display.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = $(plugin_in_files) CLEANFILES = $(plugin_DATA) DISTCLEANFILES = $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/remote-display/gsd-remote-display-plugin.c0000644000004100000410000000217312735467744023061 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 Bastien Nocera * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-remote-display-manager.h" struct GsdRemoteDisplayPluginPrivate { GsdRemoteDisplayManager *manager; }; GNOME_SETTINGS_PLUGIN_REGISTER (GsdRemoteDisplay, gsd_remote_display) ./plugins/remote-display/remote-display.gnome-settings-plugin.in0000644000004100000410000000032312735467744025427 0ustar www-datawww-data[GNOME Settings Plugin] Module=remote-display IAge=0 Priority=8 _Name=Remote Display _Description=Disable animations on remote displays Authors=Bastien Nocera Copyright=Copyright © 2012 Bastien Nocera Website= ./plugins/remote-display/gsd-remote-display-manager.c0000644000004100000410000001642112735467763023177 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 Bastien Nocera * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome-settings-bus.h" #include "gnome-settings-profile.h" #include "gsd-remote-display-manager.h" #define GSD_REMOTE_DISPLAY_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_REMOTE_DISPLAY_MANAGER, GsdRemoteDisplayManagerPrivate)) struct GsdRemoteDisplayManagerPrivate { GSettings *desktop_settings; GDBusProxy *vino_proxy; GCancellable *cancellable; guint vino_watch_id; gboolean vnc_in_use; }; static void gsd_remote_display_manager_class_init (GsdRemoteDisplayManagerClass *klass); static void gsd_remote_display_manager_init (GsdRemoteDisplayManager *remote_display_manager); G_DEFINE_TYPE (GsdRemoteDisplayManager, gsd_remote_display_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; static void update_settings_from_variant (GsdRemoteDisplayManager *manager, GVariant *variant) { manager->priv->vnc_in_use = g_variant_get_boolean (variant); g_debug ("%s because of remote display status (vnc: %d)", !manager->priv->vnc_in_use ? "Enabling" : "Disabling", manager->priv->vnc_in_use); g_settings_set_boolean (manager->priv->desktop_settings, "enable-animations", !manager->priv->vnc_in_use); } static void props_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, GsdRemoteDisplayManager *manager) { GVariant *v; v = g_variant_lookup_value (changed_properties, "Connected", G_VARIANT_TYPE_BOOLEAN); if (v) { g_debug ("Received connected change"); update_settings_from_variant (manager, v); g_variant_unref (v); } } static void got_vino_proxy (GObject *source_object, GAsyncResult *res, GsdRemoteDisplayManager *manager) { GError *error = NULL; GVariant *v; manager->priv->vino_proxy = g_dbus_proxy_new_finish (res, &error); if (manager->priv->vino_proxy == NULL) { g_warning ("Failed to get Vino's D-Bus proxy: %s", error->message); g_error_free (error); return; } g_signal_connect (manager->priv->vino_proxy, "g-properties-changed", G_CALLBACK (props_changed), manager); v = g_dbus_proxy_get_cached_property (manager->priv->vino_proxy, "Connected"); if (v) { g_debug ("Setting original state"); update_settings_from_variant (manager, v); g_variant_unref (v); } } static void vino_appeared_cb (GDBusConnection *connection, const gchar *name, const gchar *name_owner, GsdRemoteDisplayManager *manager) { g_debug ("Vino appeared"); g_dbus_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE, NULL, name, "/org/gnome/vino/screens/0", "org.gnome.VinoScreen", manager->priv->cancellable, (GAsyncReadyCallback) got_vino_proxy, manager); } static void vino_vanished_cb (GDBusConnection *connection, const char *name, GsdRemoteDisplayManager *manager) { g_debug ("Vino vanished"); if (manager->priv->cancellable != NULL) { g_cancellable_cancel (manager->priv->cancellable); g_clear_object (&manager->priv->cancellable); } g_clear_object (&manager->priv->vino_proxy); /* And reset for us to have animations */ g_settings_set_boolean (manager->priv->desktop_settings, "enable-animations", TRUE); } static gboolean gsd_display_has_extension (const gchar *ext) { int op, event, error; return XQueryExtension (gdk_x11_get_default_xdisplay (), ext, &op, &event, &error); } gboolean gsd_remote_display_manager_start (GsdRemoteDisplayManager *manager, GError **error) { g_debug ("Starting remote-display manager"); gnome_settings_profile_start (NULL); manager->priv->desktop_settings = g_settings_new ("org.gnome.desktop.interface"); /* Check if spice is used: * https://bugzilla.gnome.org/show_bug.cgi?id=680195#c7 * This doesn't change at run-time, so it's to the point */ if (g_file_test ("/dev/virtio-ports/com.redhat.spice.0", G_FILE_TEST_EXISTS)) { g_debug ("Disabling animations because SPICE is in use"); g_settings_set_boolean (manager->priv->desktop_settings, "enable-animations", FALSE); goto out; } /* Xvnc exposes an extension named VNC-EXTENSION */ if (gsd_display_has_extension ("VNC-EXTENSION")) { g_debug ("Disabling animations because VNC-EXTENSION was detected"); g_settings_set_boolean (manager->priv->desktop_settings, "enable-animations", FALSE); goto out; } /* Monitor Vino's usage */ manager->priv->vino_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, "org.gnome.Vino", G_BUS_NAME_WATCHER_FLAGS_NONE, (GBusNameAppearedCallback) vino_appeared_cb, (GBusNameVanishedCallback) vino_vanished_cb, manager, NULL); out: gnome_settings_profile_end (NULL); return TRUE; } void gsd_remote_display_manager_stop (GsdRemoteDisplayManager *manager) { g_debug ("Stopping remote_display manager"); if (manager->priv->cancellable != NULL) { g_cancellable_cancel (manager->priv->cancellable); g_clear_object (&manager->priv->cancellable); } g_clear_object (&manager->priv->vino_proxy); if (manager->priv->desktop_settings) { g_settings_reset (manager->priv->desktop_settings, "enable-animations"); g_clear_object (&manager->priv->desktop_settings); } } static void gsd_remote_display_manager_class_init (GsdRemoteDisplayManagerClass *klass) { g_type_class_add_private (klass, sizeof (GsdRemoteDisplayManagerPrivate)); } static void gsd_remote_display_manager_init (GsdRemoteDisplayManager *manager) { manager->priv = GSD_REMOTE_DISPLAY_MANAGER_GET_PRIVATE (manager); } GsdRemoteDisplayManager * gsd_remote_display_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_REMOTE_DISPLAY_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_REMOTE_DISPLAY_MANAGER (manager_object); } ./plugins/clipboard/0000755000004100000410000000000012735467744014706 5ustar www-datawww-data./plugins/clipboard/gsd-clipboard-manager.h0000644000004100000410000000456012735467744021206 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_CLIPBOARD_MANAGER_H #define __GSD_CLIPBOARD_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_CLIPBOARD_MANAGER (gsd_clipboard_manager_get_type ()) #define GSD_CLIPBOARD_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_CLIPBOARD_MANAGER, GsdClipboardManager)) #define GSD_CLIPBOARD_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_CLIPBOARD_MANAGER, GsdClipboardManagerClass)) #define GSD_IS_CLIPBOARD_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_CLIPBOARD_MANAGER)) #define GSD_IS_CLIPBOARD_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_CLIPBOARD_MANAGER)) #define GSD_CLIPBOARD_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_CLIPBOARD_MANAGER, GsdClipboardManagerClass)) typedef struct GsdClipboardManagerPrivate GsdClipboardManagerPrivate; typedef struct { GObject parent; GsdClipboardManagerPrivate *priv; } GsdClipboardManager; typedef struct { GObjectClass parent_class; } GsdClipboardManagerClass; GType gsd_clipboard_manager_get_type (void); GsdClipboardManager * gsd_clipboard_manager_new (void); gboolean gsd_clipboard_manager_start (GsdClipboardManager *manager, GError **error); void gsd_clipboard_manager_stop (GsdClipboardManager *manager); G_END_DECLS #endif /* __GSD_CLIPBOARD_MANAGER_H */ ./plugins/clipboard/Makefile.am0000644000004100000410000000166112735467744016746 0ustar www-datawww-dataNULL = plugin_name = clipboard plugin_LTLIBRARIES = \ libclipboard.la \ $(NULL) libclipboard_la_SOURCES = \ gsd-clipboard-plugin.c \ gsd-clipboard-manager.h \ gsd-clipboard-manager.c \ xutils.h \ xutils.c \ list.h \ list.c \ $(NULL) libclipboard_la_CPPFLAGS = \ $(PLUGIN_CFLAGS) \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) libclipboard_la_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) libclipboard_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) \ $(NULL) libclipboard_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(NULL) plugin_in_files = \ clipboard.gnome-settings-plugin.in \ $(NULL) plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) \ $(NULL) CLEANFILES = \ $(plugin_DATA) \ $(NULL) DISTCLEANFILES = \ $(plugin_DATA) \ $(NULL) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/clipboard/list.c0000644000004100000410000000550412735467744016031 0ustar www-datawww-data/* * Copyright © 2004 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Matthias Clasen, Red Hat, Inc. */ #include #include void list_foreach (List *list, Callback func, void *user_data) { while (list) { func (list->data, user_data); list = list->next; } } List * list_prepend (List *list, void *data) { List *link; link = (List *) malloc (sizeof (List)); link->next = list; link->data = data; return link; } void list_free (List *list) { while (list) { List *next = list->next; free (list); list = next; } } List * list_find (List *list, ListFindFunc func, void *user_data) { List *tmp; for (tmp = list; tmp; tmp = tmp->next) { if ((*func) (tmp->data, user_data)) break; } return tmp; } List * list_remove (List *list, void *data) { List *tmp, *prev; prev = NULL; for (tmp = list; tmp; tmp = tmp->next) { if (tmp->data == data) { if (prev) prev->next = tmp->next; else list = tmp->next; free (tmp); break; } prev = tmp; } return list; } int list_length (List *list) { List *tmp; int length; length = 0; for (tmp = list; tmp; tmp = tmp->next) length++; return length; } List * list_copy (List *list) { List *new_list = NULL; if (list) { List *last; new_list = (List *) malloc (sizeof (List)); new_list->data = list->data; new_list->next = NULL; last = new_list; list = list->next; while (list) { last->next = (List *) malloc (sizeof (List)); last = last->next; last->data = list->data; list = list->next; } last->next = NULL; } return new_list; } ./plugins/clipboard/gsd-clipboard-plugin.c0000644000004100000410000000203112735467744021054 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-clipboard-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdClipboard, gsd_clipboard) ./plugins/clipboard/list.h0000644000004100000410000000356612735467744016044 0ustar www-datawww-data/* * Copyright © 2004 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Matthias Clasen, Red Hat, Inc. */ #ifndef LIST_H #define LIST_H typedef struct _List List; typedef void (*Callback) (void *data, void *user_data); struct _List { void *data; List *next; }; typedef int (*ListFindFunc) (void *data, void *user_data); void list_foreach (List *list, Callback func, void *user_data); List *list_prepend (List *list, void *data); void list_free (List *list); List *list_find (List *list, ListFindFunc func, void *user_data); List *list_remove (List *list, void *data); int list_length (List *list); List *list_copy (List *list); #endif /* LIST_H */ ./plugins/clipboard/clipboard.gnome-settings-plugin.in0000644000004100000410000000031512735467744023432 0ustar www-datawww-data[GNOME Settings Plugin] Module=clipboard IAge=0 # Default Priority # Priority=100 _Name=Clipboard _Description=Clipboard plugin Authors=Matthias Clasen Copyright=Copyright © 2007 Matthias Clasen Website= ./plugins/clipboard/gsd-clipboard-manager.c0000644000004100000410000011642612735467744021206 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 Matthias Clasen * Copyright (C) 2007 Anders Carlsson * Copyright (C) 2007 Rodrigo Moya * Copyright (C) 2007 William Jon McCann * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xutils.h" #include "list.h" #include "gnome-settings-profile.h" #include "gsd-clipboard-manager.h" #define GSD_CLIPBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_CLIPBOARD_MANAGER, GsdClipboardManagerPrivate)) struct GsdClipboardManagerPrivate { guint start_idle_id; Display *display; Window window; Time timestamp; List *contents; List *conversions; Window requestor; Atom property; Time time; }; typedef struct { unsigned char *data; int length; Atom target; Atom type; int format; int refcount; } TargetData; typedef struct { Atom target; TargetData *data; Atom property; Window requestor; int offset; } IncrConversion; static void gsd_clipboard_manager_class_init (GsdClipboardManagerClass *klass); static void gsd_clipboard_manager_init (GsdClipboardManager *clipboard_manager); static void gsd_clipboard_manager_finalize (GObject *object); static void clipboard_manager_watch_cb (GsdClipboardManager *manager, Window window, Bool is_start, long mask, void *cb_data); G_DEFINE_TYPE (GsdClipboardManager, gsd_clipboard_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; /* We need to use reference counting for the target data, since we may * need to keep the data around after loosing the CLIPBOARD ownership * to complete incremental transfers. */ static TargetData * target_data_ref (TargetData *data) { data->refcount++; return data; } static void target_data_unref (TargetData *data) { data->refcount--; if (data->refcount == 0) { free (data->data); free (data); } } static void conversion_free (IncrConversion *rdata) { if (rdata->data) { target_data_unref (rdata->data); } free (rdata); } static void send_selection_notify (GsdClipboardManager *manager, Bool success) { XSelectionEvent notify; notify.type = SelectionNotify; notify.serial = 0; notify.send_event = True; notify.display = manager->priv->display; notify.requestor = manager->priv->requestor; notify.selection = XA_CLIPBOARD_MANAGER; notify.target = XA_SAVE_TARGETS; notify.property = success ? manager->priv->property : None; notify.time = manager->priv->time; gdk_error_trap_push (); XSendEvent (manager->priv->display, manager->priv->requestor, False, NoEventMask, (XEvent *)¬ify); XSync (manager->priv->display, False); gdk_error_trap_pop_ignored (); } static void finish_selection_request (GsdClipboardManager *manager, XEvent *xev, Bool success) { XSelectionEvent notify; notify.type = SelectionNotify; notify.serial = 0; notify.send_event = True; notify.display = xev->xselectionrequest.display; notify.requestor = xev->xselectionrequest.requestor; notify.selection = xev->xselectionrequest.selection; notify.target = xev->xselectionrequest.target; notify.property = success ? xev->xselectionrequest.property : None; notify.time = xev->xselectionrequest.time; gdk_error_trap_push (); XSendEvent (xev->xselectionrequest.display, xev->xselectionrequest.requestor, False, NoEventMask, (XEvent *) ¬ify); XSync (manager->priv->display, False); gdk_error_trap_pop_ignored (); } static int clipboard_bytes_per_item (int format) { switch (format) { case 8: return sizeof (char); case 16: return sizeof (short); case 32: return sizeof (long); default: ; } return 0; } static void save_targets (GsdClipboardManager *manager, Atom *save_targets, int nitems) { int nout, i; Atom *multiple; TargetData *tdata; multiple = (Atom *) malloc (2 * nitems * sizeof (Atom)); nout = 0; for (i = 0; i < nitems; i++) { if (save_targets[i] != XA_TARGETS && save_targets[i] != XA_MULTIPLE && save_targets[i] != XA_DELETE && save_targets[i] != XA_INSERT_PROPERTY && save_targets[i] != XA_INSERT_SELECTION && save_targets[i] != XA_PIXMAP) { tdata = (TargetData *) malloc (sizeof (TargetData)); tdata->data = NULL; tdata->length = 0; tdata->target = save_targets[i]; tdata->type = None; tdata->format = 0; tdata->refcount = 1; manager->priv->contents = list_prepend (manager->priv->contents, tdata); multiple[nout++] = save_targets[i]; multiple[nout++] = save_targets[i]; } } XFree (save_targets); XChangeProperty (manager->priv->display, manager->priv->window, XA_MULTIPLE, XA_ATOM_PAIR, 32, PropModeReplace, (const unsigned char *) multiple, nout); free (multiple); XConvertSelection (manager->priv->display, XA_CLIPBOARD, XA_MULTIPLE, XA_MULTIPLE, manager->priv->window, manager->priv->time); } static int find_content_target (TargetData *tdata, Atom target) { return tdata->target == target; } static int find_content_type (TargetData *tdata, Atom type) { return tdata->type == type; } static int find_conversion_requestor (IncrConversion *rdata, XEvent *xev) { return (rdata->requestor == xev->xproperty.window && rdata->property == xev->xproperty.atom); } static void get_property (TargetData *tdata, GsdClipboardManager *manager) { Atom type; int format; unsigned long length; unsigned long remaining; unsigned char *data; XGetWindowProperty (manager->priv->display, manager->priv->window, tdata->target, 0, 0x1FFFFFFF, True, AnyPropertyType, &type, &format, &length, &remaining, &data); if (type == None) { manager->priv->contents = list_remove (manager->priv->contents, tdata); free (tdata); } else if (type == XA_INCR) { tdata->type = type; tdata->length = 0; XFree (data); } else { tdata->type = type; tdata->data = data; tdata->length = length * clipboard_bytes_per_item (format); tdata->format = format; } } static Bool receive_incrementally (GsdClipboardManager *manager, XEvent *xev) { List *list; TargetData *tdata; Atom type; int format; unsigned long length, nitems, remaining; unsigned char *data; if (xev->xproperty.window != manager->priv->window) return False; list = list_find (manager->priv->contents, (ListFindFunc) find_content_target, (void *) xev->xproperty.atom); if (!list) return False; tdata = (TargetData *) list->data; if (tdata->type != XA_INCR) return False; XGetWindowProperty (xev->xproperty.display, xev->xproperty.window, xev->xproperty.atom, 0, 0x1FFFFFFF, True, AnyPropertyType, &type, &format, &nitems, &remaining, &data); length = nitems * clipboard_bytes_per_item (format); if (length == 0) { tdata->type = type; tdata->format = format; if (!list_find (manager->priv->contents, (ListFindFunc) find_content_type, (void *)XA_INCR)) { /* all incremental transfers done */ send_selection_notify (manager, True); manager->priv->requestor = None; } XFree (data); } else { if (!tdata->data) { tdata->data = data; tdata->length = length; } else { tdata->data = realloc (tdata->data, tdata->length + length + 1); memcpy (tdata->data + tdata->length, data, length + 1); tdata->length += length; XFree (data); } } return True; } static Bool send_incrementally (GsdClipboardManager *manager, XEvent *xev) { List *list; IncrConversion *rdata; unsigned long length; unsigned long items; unsigned char *data; list = list_find (manager->priv->conversions, (ListFindFunc) find_conversion_requestor, xev); if (list == NULL) return False; rdata = (IncrConversion *) list->data; data = rdata->data->data + rdata->offset; length = rdata->data->length - rdata->offset; if (length > SELECTION_MAX_SIZE) length = SELECTION_MAX_SIZE; rdata->offset += length; items = length / clipboard_bytes_per_item (rdata->data->format); XChangeProperty (manager->priv->display, rdata->requestor, rdata->property, rdata->data->type, rdata->data->format, PropModeAppend, data, items); if (length == 0) { clipboard_manager_watch_cb (manager, rdata->requestor, False, PropertyChangeMask, NULL); manager->priv->conversions = list_remove (manager->priv->conversions, rdata); conversion_free (rdata); } return True; } static void convert_clipboard_manager (GsdClipboardManager *manager, XEvent *xev) { Atom type = None; int format; unsigned long nitems; unsigned long remaining; Atom *targets = NULL; if (xev->xselectionrequest.target == XA_SAVE_TARGETS) { if (manager->priv->requestor != None || manager->priv->contents != NULL) { /* We're in the middle of a conversion request, or own * the CLIPBOARD already */ finish_selection_request (manager, xev, False); } else { gdk_error_trap_push (); clipboard_manager_watch_cb (manager, xev->xselectionrequest.requestor, True, StructureNotifyMask, NULL); XSelectInput (manager->priv->display, xev->xselectionrequest.requestor, StructureNotifyMask); XSync (manager->priv->display, False); if (gdk_error_trap_pop () != Success) return; gdk_error_trap_push (); if (xev->xselectionrequest.property != None) { XGetWindowProperty (manager->priv->display, xev->xselectionrequest.requestor, xev->xselectionrequest.property, 0, 0x1FFFFFFF, False, XA_ATOM, &type, &format, &nitems, &remaining, (unsigned char **) &targets); if (gdk_error_trap_pop () != Success) { if (targets) XFree (targets); return; } } manager->priv->requestor = xev->xselectionrequest.requestor; manager->priv->property = xev->xselectionrequest.property; manager->priv->time = xev->xselectionrequest.time; if (type == None) XConvertSelection (manager->priv->display, XA_CLIPBOARD, XA_TARGETS, XA_TARGETS, manager->priv->window, manager->priv->time); else save_targets (manager, targets, nitems); } } else if (xev->xselectionrequest.target == XA_TIMESTAMP) { XChangeProperty (manager->priv->display, xev->xselectionrequest.requestor, xev->xselectionrequest.property, XA_INTEGER, 32, PropModeReplace, (unsigned char *) &manager->priv->timestamp, 1); finish_selection_request (manager, xev, True); } else if (xev->xselectionrequest.target == XA_TARGETS) { int n_targets = 0; Atom targets[3]; targets[n_targets++] = XA_TARGETS; targets[n_targets++] = XA_TIMESTAMP; targets[n_targets++] = XA_SAVE_TARGETS; XChangeProperty (manager->priv->display, xev->xselectionrequest.requestor, xev->xselectionrequest.property, XA_ATOM, 32, PropModeReplace, (unsigned char *) targets, n_targets); finish_selection_request (manager, xev, True); } else finish_selection_request (manager, xev, False); } static void convert_clipboard_target (IncrConversion *rdata, GsdClipboardManager *manager) { TargetData *tdata; Atom *targets; int n_targets; List *list; unsigned long items; XWindowAttributes atts; if (rdata->target == XA_TARGETS) { n_targets = list_length (manager->priv->contents) + 2; targets = (Atom *) malloc (n_targets * sizeof (Atom)); n_targets = 0; targets[n_targets++] = XA_TARGETS; targets[n_targets++] = XA_MULTIPLE; for (list = manager->priv->contents; list; list = list->next) { tdata = (TargetData *) list->data; targets[n_targets++] = tdata->target; } XChangeProperty (manager->priv->display, rdata->requestor, rdata->property, XA_ATOM, 32, PropModeReplace, (unsigned char *) targets, n_targets); free (targets); } else { /* Convert from stored CLIPBOARD data */ list = list_find (manager->priv->contents, (ListFindFunc) find_content_target, (void *) rdata->target); /* We got a target that we don't support */ if (!list) return; tdata = (TargetData *)list->data; if (tdata->type == XA_INCR) { /* we haven't completely received this target yet */ rdata->property = None; return; } rdata->data = target_data_ref (tdata); items = tdata->length / clipboard_bytes_per_item (tdata->format); if (tdata->length <= SELECTION_MAX_SIZE) XChangeProperty (manager->priv->display, rdata->requestor, rdata->property, tdata->type, tdata->format, PropModeReplace, tdata->data, items); else { /* start incremental transfer */ rdata->offset = 0; gdk_error_trap_push (); XGetWindowAttributes (manager->priv->display, rdata->requestor, &atts); clipboard_manager_watch_cb (manager, rdata->requestor, True, PropertyChangeMask, NULL); XSelectInput (manager->priv->display, rdata->requestor, atts.your_event_mask | PropertyChangeMask); XChangeProperty (manager->priv->display, rdata->requestor, rdata->property, XA_INCR, 32, PropModeReplace, (unsigned char *) &items, 1); XSync (manager->priv->display, False); gdk_error_trap_pop_ignored (); } } } static void collect_incremental (IncrConversion *rdata, GsdClipboardManager *manager) { if (rdata->offset >= 0) manager->priv->conversions = list_prepend (manager->priv->conversions, rdata); else { if (rdata->data) { target_data_unref (rdata->data); rdata->data = NULL; } free (rdata); } } static void convert_clipboard (GsdClipboardManager *manager, XEvent *xev) { List *list; List *conversions; IncrConversion *rdata; Atom type; int i; int format; unsigned long nitems; unsigned long remaining; Atom *multiple; conversions = NULL; type = None; if (xev->xselectionrequest.target == XA_MULTIPLE) { XGetWindowProperty (xev->xselectionrequest.display, xev->xselectionrequest.requestor, xev->xselectionrequest.property, 0, 0x1FFFFFFF, False, XA_ATOM_PAIR, &type, &format, &nitems, &remaining, (unsigned char **) &multiple); if (type != XA_ATOM_PAIR || nitems == 0) { if (multiple) free (multiple); return; } for (i = 0; i < nitems; i += 2) { rdata = (IncrConversion *) malloc (sizeof (IncrConversion)); rdata->requestor = xev->xselectionrequest.requestor; rdata->target = multiple[i]; rdata->property = multiple[i+1]; rdata->data = NULL; rdata->offset = -1; conversions = list_prepend (conversions, rdata); } } else { multiple = NULL; rdata = (IncrConversion *) malloc (sizeof (IncrConversion)); rdata->requestor = xev->xselectionrequest.requestor; rdata->target = xev->xselectionrequest.target; rdata->property = xev->xselectionrequest.property; rdata->data = NULL; rdata->offset = -1; conversions = list_prepend (conversions, rdata); } list_foreach (conversions, (Callback) convert_clipboard_target, manager); if (conversions->next == NULL && ((IncrConversion *) conversions->data)->property == None) { finish_selection_request (manager, xev, False); } else { if (multiple) { i = 0; for (list = conversions; list; list = list->next) { rdata = (IncrConversion *)list->data; multiple[i++] = rdata->target; multiple[i++] = rdata->property; } XChangeProperty (xev->xselectionrequest.display, xev->xselectionrequest.requestor, xev->xselectionrequest.property, XA_ATOM_PAIR, 32, PropModeReplace, (unsigned char *) multiple, nitems); } finish_selection_request (manager, xev, True); } list_foreach (conversions, (Callback) collect_incremental, manager); list_free (conversions); if (multiple) free (multiple); } static Bool clipboard_manager_process_event (GsdClipboardManager *manager, XEvent *xev) { Atom type; int format; unsigned long nitems; unsigned long remaining; Atom *targets; targets = NULL; switch (xev->xany.type) { case DestroyNotify: if (xev->xdestroywindow.window == manager->priv->requestor) { list_foreach (manager->priv->contents, (Callback)target_data_unref, NULL); list_free (manager->priv->contents); manager->priv->contents = NULL; clipboard_manager_watch_cb (manager, manager->priv->requestor, False, 0, NULL); manager->priv->requestor = None; } break; case PropertyNotify: if (xev->xproperty.state == PropertyNewValue) { return receive_incrementally (manager, xev); } else { return send_incrementally (manager, xev); } case SelectionClear: if (xev->xany.window != manager->priv->window) return False; if (xev->xselectionclear.selection == XA_CLIPBOARD_MANAGER) { /* We lost the manager selection */ if (manager->priv->contents) { list_foreach (manager->priv->contents, (Callback)target_data_unref, NULL); list_free (manager->priv->contents); manager->priv->contents = NULL; XSetSelectionOwner (manager->priv->display, XA_CLIPBOARD, None, manager->priv->time); } return True; } if (xev->xselectionclear.selection == XA_CLIPBOARD) { /* We lost the clipboard selection */ list_foreach (manager->priv->contents, (Callback)target_data_unref, NULL); list_free (manager->priv->contents); manager->priv->contents = NULL; clipboard_manager_watch_cb (manager, manager->priv->requestor, False, 0, NULL); manager->priv->requestor = None; return True; } break; case SelectionNotify: if (xev->xany.window != manager->priv->window) return False; if (xev->xselection.selection == XA_CLIPBOARD) { /* a CLIPBOARD conversion is done */ if (xev->xselection.property == XA_TARGETS) { XGetWindowProperty (xev->xselection.display, xev->xselection.requestor, xev->xselection.property, 0, 0x1FFFFFFF, True, XA_ATOM, &type, &format, &nitems, &remaining, (unsigned char **) &targets); save_targets (manager, targets, nitems); } else if (xev->xselection.property == XA_MULTIPLE) { List *tmp; tmp = list_copy (manager->priv->contents); list_foreach (tmp, (Callback) get_property, manager); list_free (tmp); manager->priv->time = xev->xselection.time; XSetSelectionOwner (manager->priv->display, XA_CLIPBOARD, manager->priv->window, manager->priv->time); if (manager->priv->property != None) XChangeProperty (manager->priv->display, manager->priv->requestor, manager->priv->property, XA_ATOM, 32, PropModeReplace, (unsigned char *)&XA_NULL, 1); if (!list_find (manager->priv->contents, (ListFindFunc)find_content_type, (void *)XA_INCR)) { /* all transfers done */ send_selection_notify (manager, True); clipboard_manager_watch_cb (manager, manager->priv->requestor, False, 0, NULL); manager->priv->requestor = None; } } else if (xev->xselection.property == None) { send_selection_notify (manager, False); clipboard_manager_watch_cb (manager, manager->priv->requestor, False, 0, NULL); manager->priv->requestor = None; } return True; } break; case SelectionRequest: if (xev->xany.window != manager->priv->window) { return False; } if (xev->xselectionrequest.selection == XA_CLIPBOARD_MANAGER) { convert_clipboard_manager (manager, xev); return True; } else if (xev->xselectionrequest.selection == XA_CLIPBOARD) { convert_clipboard (manager, xev); return True; } break; default: ; } return False; } static GdkFilterReturn clipboard_manager_event_filter (GdkXEvent *xevent, GdkEvent *event, GsdClipboardManager *manager) { if (clipboard_manager_process_event (manager, (XEvent *)xevent)) { return GDK_FILTER_REMOVE; } else { return GDK_FILTER_CONTINUE; } } static void clipboard_manager_watch_cb (GsdClipboardManager *manager, Window window, Bool is_start, long mask, void *cb_data) { GdkWindow *gdkwin; GdkDisplay *display; display = gdk_display_get_default (); gdkwin = gdk_x11_window_lookup_for_display (display, window); if (is_start) { if (gdkwin == NULL) { gdkwin = gdk_x11_window_foreign_new_for_display (display, window); } else { g_object_ref (gdkwin); } gdk_window_add_filter (gdkwin, (GdkFilterFunc)clipboard_manager_event_filter, manager); } else { if (gdkwin == NULL) { return; } gdk_window_remove_filter (gdkwin, (GdkFilterFunc)clipboard_manager_event_filter, manager); g_object_unref (gdkwin); } } static gboolean start_clipboard_idle_cb (GsdClipboardManager *manager) { XClientMessageEvent xev; gnome_settings_profile_start (NULL); init_atoms (manager->priv->display); /* check if there is a clipboard manager running */ if (XGetSelectionOwner (manager->priv->display, XA_CLIPBOARD_MANAGER)) { g_warning ("Clipboard manager is already running."); return FALSE; } manager->priv->contents = NULL; manager->priv->conversions = NULL; manager->priv->requestor = None; manager->priv->window = XCreateSimpleWindow (manager->priv->display, DefaultRootWindow (manager->priv->display), 0, 0, 10, 10, 0, WhitePixel (manager->priv->display, DefaultScreen (manager->priv->display)), WhitePixel (manager->priv->display, DefaultScreen (manager->priv->display))); clipboard_manager_watch_cb (manager, manager->priv->window, True, PropertyChangeMask, NULL); XSelectInput (manager->priv->display, manager->priv->window, PropertyChangeMask); manager->priv->timestamp = get_server_time (manager->priv->display, manager->priv->window); XSetSelectionOwner (manager->priv->display, XA_CLIPBOARD_MANAGER, manager->priv->window, manager->priv->timestamp); /* Check to see if we managed to claim the selection. If not, * we treat it as if we got it then immediately lost it */ if (XGetSelectionOwner (manager->priv->display, XA_CLIPBOARD_MANAGER) == manager->priv->window) { xev.type = ClientMessage; xev.window = DefaultRootWindow (manager->priv->display); xev.message_type = XA_MANAGER; xev.format = 32; xev.data.l[0] = manager->priv->timestamp; xev.data.l[1] = XA_CLIPBOARD_MANAGER; xev.data.l[2] = manager->priv->window; xev.data.l[3] = 0; /* manager specific data */ xev.data.l[4] = 0; /* manager specific data */ XSendEvent (manager->priv->display, DefaultRootWindow (manager->priv->display), False, StructureNotifyMask, (XEvent *)&xev); } else { clipboard_manager_watch_cb (manager, manager->priv->window, False, 0, NULL); /* FIXME: manager->priv->terminate (manager->priv->cb_data); */ } gnome_settings_profile_end (NULL); manager->priv->start_idle_id = 0; return FALSE; } gboolean gsd_clipboard_manager_start (GsdClipboardManager *manager, GError **error) { gnome_settings_profile_start (NULL); manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_clipboard_idle_cb, manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_clipboard_manager_stop (GsdClipboardManager *manager) { g_debug ("Stopping clipboard manager"); if (manager->priv->window != None) { clipboard_manager_watch_cb (manager, manager->priv->window, FALSE, 0, NULL); XDestroyWindow (manager->priv->display, manager->priv->window); manager->priv->window = None; } if (manager->priv->conversions != NULL) { list_foreach (manager->priv->conversions, (Callback) conversion_free, NULL); list_free (manager->priv->conversions); manager->priv->conversions = NULL; } if (manager->priv->contents != NULL) { list_foreach (manager->priv->contents, (Callback) target_data_unref, NULL); list_free (manager->priv->contents); manager->priv->contents = NULL; } } static GObject * gsd_clipboard_manager_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdClipboardManager *clipboard_manager; clipboard_manager = GSD_CLIPBOARD_MANAGER (G_OBJECT_CLASS (gsd_clipboard_manager_parent_class)->constructor (type, n_construct_properties, construct_properties)); return G_OBJECT (clipboard_manager); } static void gsd_clipboard_manager_class_init (GsdClipboardManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_clipboard_manager_constructor; object_class->finalize = gsd_clipboard_manager_finalize; g_type_class_add_private (klass, sizeof (GsdClipboardManagerPrivate)); } static void gsd_clipboard_manager_init (GsdClipboardManager *manager) { manager->priv = GSD_CLIPBOARD_MANAGER_GET_PRIVATE (manager); manager->priv->display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); } static void gsd_clipboard_manager_finalize (GObject *object) { GsdClipboardManager *clipboard_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_CLIPBOARD_MANAGER (object)); clipboard_manager = GSD_CLIPBOARD_MANAGER (object); g_return_if_fail (clipboard_manager->priv != NULL); if (clipboard_manager->priv->start_idle_id !=0) g_source_remove (clipboard_manager->priv->start_idle_id); G_OBJECT_CLASS (gsd_clipboard_manager_parent_class)->finalize (object); } GsdClipboardManager * gsd_clipboard_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_CLIPBOARD_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_CLIPBOARD_MANAGER (manager_object); } ./plugins/clipboard/xutils.h0000644000004100000410000000334012735467744016407 0ustar www-datawww-data/* * Copyright © 2004 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Matthias Clasen, Red Hat, Inc. */ #ifndef X_UTILS_H #define X_UTILS_H #include extern Atom XA_ATOM_PAIR; extern Atom XA_CLIPBOARD_MANAGER; extern Atom XA_CLIPBOARD; extern Atom XA_DELETE; extern Atom XA_INCR; extern Atom XA_INSERT_PROPERTY; extern Atom XA_INSERT_SELECTION; extern Atom XA_MANAGER; extern Atom XA_MULTIPLE; extern Atom XA_NULL; extern Atom XA_SAVE_TARGETS; extern Atom XA_TARGETS; extern Atom XA_TIMESTAMP; extern unsigned long SELECTION_MAX_SIZE; void init_atoms (Display *display); Time get_server_time (Display *display, Window window); #endif /* X_UTILS_H */ ./plugins/clipboard/xutils.c0000644000004100000410000000675612735467744016420 0ustar www-datawww-data/* * Copyright © 2004 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Matthias Clasen, Red Hat, Inc. */ #include #include "xutils.h" Atom XA_ATOM_PAIR; Atom XA_CLIPBOARD_MANAGER; Atom XA_CLIPBOARD; Atom XA_DELETE; Atom XA_INCR; Atom XA_INSERT_PROPERTY; Atom XA_INSERT_SELECTION; Atom XA_MANAGER; Atom XA_MULTIPLE; Atom XA_NULL; Atom XA_SAVE_TARGETS; Atom XA_TARGETS; Atom XA_TIMESTAMP; unsigned long SELECTION_MAX_SIZE = 0; void init_atoms (Display *display) { unsigned long max_request_size; if (SELECTION_MAX_SIZE > 0) return; XA_ATOM_PAIR = XInternAtom (display, "ATOM_PAIR", False); XA_CLIPBOARD_MANAGER = XInternAtom (display, "CLIPBOARD_MANAGER", False); XA_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False); XA_DELETE = XInternAtom (display, "DELETE", False); XA_INCR = XInternAtom (display, "INCR", False); XA_INSERT_PROPERTY = XInternAtom (display, "INSERT_PROPERTY", False); XA_INSERT_SELECTION = XInternAtom (display, "INSERT_SELECTION", False); XA_MANAGER = XInternAtom (display, "MANAGER", False); XA_MULTIPLE = XInternAtom (display, "MULTIPLE", False); XA_NULL = XInternAtom (display, "NULL", False); XA_SAVE_TARGETS = XInternAtom (display, "SAVE_TARGETS", False); XA_TARGETS = XInternAtom (display, "TARGETS", False); XA_TIMESTAMP = XInternAtom (display, "TIMESTAMP", False); max_request_size = XExtendedMaxRequestSize (display); if (max_request_size == 0) max_request_size = XMaxRequestSize (display); SELECTION_MAX_SIZE = max_request_size - 100; if (SELECTION_MAX_SIZE > 262144) SELECTION_MAX_SIZE = 262144; } typedef struct { Window window; Atom timestamp_prop_atom; } TimeStampInfo; static Bool timestamp_predicate (Display *display, XEvent *xevent, XPointer arg) { TimeStampInfo *info = (TimeStampInfo *)arg; if (xevent->type == PropertyNotify && xevent->xproperty.window == info->window && xevent->xproperty.atom == info->timestamp_prop_atom) return True; return False; } Time get_server_time (Display *display, Window window) { unsigned char c = 'a'; XEvent xevent; TimeStampInfo info; info.timestamp_prop_atom = XInternAtom (display, "_TIMESTAMP_PROP", False); info.window = window; XChangeProperty (display, window, info.timestamp_prop_atom, info.timestamp_prop_atom, 8, PropModeReplace, &c, 1); XIfEvent (display, &xevent, timestamp_predicate, (XPointer)&info); return xevent.xproperty.time; } ./plugins/smartcard/0000755000004100000410000000000012735467763014730 5ustar www-datawww-data./plugins/smartcard/gsd-smartcard-manager.h0000644000004100000410000000716012735467744021247 0ustar www-datawww-data/* gsd-smartcard-manager.h - object for monitoring smartcard insertion and * removal events * * Copyright (C) 2006, 2009 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. * * Written by: Ray Strode */ #ifndef GSD_SMARTCARD_MANAGER_H #define GSD_SMARTCARD_MANAGER_H #define GSD_SMARTCARD_ENABLE_INTERNAL_API #include "gsd-smartcard.h" #include #include G_BEGIN_DECLS #define GSD_TYPE_SMARTCARD_MANAGER (gsd_smartcard_manager_get_type ()) #define GSD_SMARTCARD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_SMARTCARD_MANAGER, GsdSmartcardManager)) #define GSD_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_SMARTCARD_MANAGER, GsdSmartcardManagerClass)) #define GSD_IS_SMARTCARD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SMARTCARD_MANAGER)) #define GSD_IS_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SMARTCARD_MANAGER)) #define GSD_SMARTCARD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSD_TYPE_SMARTCARD_MANAGER, GsdSmartcardManagerClass)) #define GSD_SMARTCARD_MANAGER_ERROR (gsd_smartcard_manager_error_quark ()) typedef struct _GsdSmartcardManager GsdSmartcardManager; typedef struct _GsdSmartcardManagerClass GsdSmartcardManagerClass; typedef struct _GsdSmartcardManagerPrivate GsdSmartcardManagerPrivate; typedef enum _GsdSmartcardManagerError GsdSmartcardManagerError; struct _GsdSmartcardManager { GObject parent; /*< private > */ GsdSmartcardManagerPrivate *priv; }; struct _GsdSmartcardManagerClass { GObjectClass parent_class; /* Signals */ void (*smartcard_inserted) (GsdSmartcardManager *manager, GsdSmartcard *token); void (*smartcard_removed) (GsdSmartcardManager *manager, GsdSmartcard *token); void (*error) (GsdSmartcardManager *manager, GError *error); }; enum _GsdSmartcardManagerError { GSD_SMARTCARD_MANAGER_ERROR_GENERIC = 0, GSD_SMARTCARD_MANAGER_ERROR_WITH_NSS, GSD_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, GSD_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, GSD_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS }; GType gsd_smartcard_manager_get_type (void) G_GNUC_CONST; GQuark gsd_smartcard_manager_error_quark (void) G_GNUC_CONST; GsdSmartcardManager *gsd_smartcard_manager_new_default (void); GsdSmartcardManager *gsd_smartcard_manager_new (const char *module); gboolean gsd_smartcard_manager_start (GsdSmartcardManager *manager, GError **error); void gsd_smartcard_manager_stop (GsdSmartcardManager *manager); char *gsd_smartcard_manager_get_module_path (GsdSmartcardManager *manager); gboolean gsd_smartcard_manager_login_card_is_inserted (GsdSmartcardManager *manager); G_END_DECLS #endif /* GSD_SMARTCARD_MANAGER_H */ ./plugins/smartcard/gsd-smartcard-plugin.h0000644000004100000410000000426712735467744021140 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 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. * */ #ifndef __GSD_SMARTCARD_PLUGIN_H__ #define __GSD_SMARTCARD_PLUGIN_H__ #include #include #include #include "gnome-settings-plugin.h" G_BEGIN_DECLS #define GSD_TYPE_SMARTCARD_PLUGIN (gsd_smartcard_plugin_get_type ()) #define GSD_SMARTCARD_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_SMARTCARD_PLUGIN, GsdSmartcardPlugin)) #define GSD_SMARTCARD_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSD_TYPE_SMARTCARD_PLUGIN, GsdSmartcardPluginClass)) #define GSD_IS_SMARTCARD_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_SMARTCARD_PLUGIN)) #define GSD_IS_SMARTCARD_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_SMARTCARD_PLUGIN)) #define GSD_SMARTCARD_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_SMARTCARD_PLUGIN, GsdSmartcardPluginClass)) typedef struct GsdSmartcardPluginPrivate GsdSmartcardPluginPrivate; typedef struct { GnomeSettingsPlugin parent; GsdSmartcardPluginPrivate *priv; } GsdSmartcardPlugin; typedef struct { GnomeSettingsPluginClass parent_class; } GsdSmartcardPluginClass; GType gsd_smartcard_plugin_get_type (void) G_GNUC_CONST; /* All the plugins must implement this function */ G_MODULE_EXPORT GType register_gnome_settings_plugin (GTypeModule *module); G_END_DECLS #endif /* __GSD_SMARTCARD_PLUGIN_H__ */ ./plugins/smartcard/Makefile.am0000644000004100000410000000326412735467744016770 0ustar www-datawww-dataplugin_name = smartcard libexec_PROGRAMS = usd-test-smartcard usd_test_smartcard_SOURCES = \ gsd-smartcard-manager.h \ gsd-smartcard-manager.c \ gsd-smartcard.h \ gsd-smartcard.c \ test-smartcard.c usd_test_smartcard_CFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DLIBDIR=\""$(libdir)"\" \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(NSS_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) usd_test_smartcard_LDADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/plugins/common/libcommon.la \ $(NSS_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_LTLIBRARIES = \ libsmartcard.la libsmartcard_la_SOURCES = \ gsd-smartcard-plugin.h \ gsd-smartcard-plugin.c \ gsd-smartcard.h \ gsd-smartcard.c \ gsd-smartcard-manager.h \ gsd-smartcard-manager.c libsmartcard_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DLIBDIR=\""$(libdir)"\" \ -DGSD_SMARTCARD_MANAGER_NSS_DB=\""$(NSS_DATABASE)"\" \ $(AM_CPPFLAGS) libsmartcard_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(NSS_CFLAGS) \ $(AM_CFLAGS) libsmartcard_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libsmartcard_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(NSS_LIBS) @GSD_INTLTOOL_PLUGIN_RULE@ plugin_in_files = \ smartcard.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) ./plugins/smartcard/gsd-smartcard.h0000644000004100000410000000656212735467744017644 0ustar www-datawww-data/* securitycard.h - api for reading and writing data to a security card * * Copyright (C) 2006 Ray Strode * * 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. */ #ifndef GSD_SMARTCARD_H #define GSD_SMARTCARD_H #include #include #include G_BEGIN_DECLS #define GSD_TYPE_SMARTCARD (gsd_smartcard_get_type ()) #define GSD_SMARTCARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSD_TYPE_SMARTCARD, GsdSmartcard)) #define GSD_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSD_TYPE_SMARTCARD, GsdSmartcardClass)) #define GSD_IS_SMARTCARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSD_TYPE_SMARTCARD)) #define GSD_IS_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSD_TYPE_SMARTCARD)) #define GSD_SMARTCARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSD_TYPE_SMARTCARD, GsdSmartcardClass)) #define GSD_SMARTCARD_ERROR (gsd_smartcard_error_quark ()) typedef struct _GsdSmartcardClass GsdSmartcardClass; typedef struct _GsdSmartcard GsdSmartcard; typedef struct _GsdSmartcardPrivate GsdSmartcardPrivate; typedef enum _GsdSmartcardError GsdSmartcardError; typedef enum _GsdSmartcardState GsdSmartcardState; typedef struct _GsdSmartcardRequest GsdSmartcardRequest; struct _GsdSmartcard { GObject parent; /*< private > */ GsdSmartcardPrivate *priv; }; struct _GsdSmartcardClass { GObjectClass parent_class; void (* inserted) (GsdSmartcard *card); void (* removed) (GsdSmartcard *card); }; enum _GsdSmartcardError { GSD_SMARTCARD_ERROR_GENERIC = 0, }; enum _GsdSmartcardState { GSD_SMARTCARD_STATE_INSERTED = 0, GSD_SMARTCARD_STATE_REMOVED, }; GType gsd_smartcard_get_type (void) G_GNUC_CONST; GQuark gsd_smartcard_error_quark (void) G_GNUC_CONST; CK_SLOT_ID gsd_smartcard_get_slot_id (GsdSmartcard *card); gint gsd_smartcard_get_slot_series (GsdSmartcard *card); GsdSmartcardState gsd_smartcard_get_state (GsdSmartcard *card); char *gsd_smartcard_get_name (GsdSmartcard *card); gboolean gsd_smartcard_is_login_card (GsdSmartcard *card); gboolean gsd_smartcard_unlock (GsdSmartcard *card, const char *password); /* don't under any circumstances call these functions */ #ifdef GSD_SMARTCARD_ENABLE_INTERNAL_API GsdSmartcard *_gsd_smartcard_new (SECMODModule *module, CK_SLOT_ID slot_id, gint slot_series); GsdSmartcard *_gsd_smartcard_new_from_name (SECMODModule *module, const char *name); void _gsd_smartcard_set_state (GsdSmartcard *card, GsdSmartcardState state); #endif G_END_DECLS #endif /* GSD_SMARTCARD_H */ ./plugins/smartcard/smartcard.gnome-settings-plugin.in0000644000004100000410000000025712735467744023501 0ustar www-datawww-data[GNOME Settings Plugin] Module=smartcard IAge=0 Priority=8 _Name=Smartcard _Description=Smartcard plugin Authors=Ray Strode Copyright=Copyright © 2010 Red Hat, Inc. Website= ./plugins/smartcard/gsd-smartcard-manager.c0000644000004100000410000014362512735467744021251 0ustar www-datawww-data/* gsd-smartcard-manager.c - object for monitoring smartcard insertion and * removal events * * Copyright (C) 2006, 2009 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. * * Written By: Ray Strode */ #include "config.h" #include "gsd-smartcard-manager.h" #define SMARTCARD_ENABLE_INTERNAL_API #include "gsd-smartcard.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef GSD_SMARTCARD_MANAGER_DRIVER #define GSD_SMARTCARD_MANAGER_DRIVER LIBDIR"/pkcs11/libcoolkeypk11.so" #endif #ifndef GSD_SMARTCARD_MANAGER_NSS_DB #define GSD_SMARTCARD_MANAGER_NSS_DB SYSCONFDIR"/pki/nssdb" #endif #ifndef GSD_MAX_OPEN_FILE_DESCRIPTORS #define GSD_MAX_OPEN_FILE_DESCRIPTORS 1024 #endif #ifndef GSD_OPEN_FILE_DESCRIPTORS_DIR #define GSD_OPEN_FILE_DESCRIPTORS_DIR "/proc/self/fd" #endif typedef enum _GsdSmartcardManagerState GsdSmartcardManagerState; typedef struct _GsdSmartcardManagerWorker GsdSmartcardManagerWorker; enum _GsdSmartcardManagerState { GSD_SMARTCARD_MANAGER_STATE_STOPPED = 0, GSD_SMARTCARD_MANAGER_STATE_STARTING, GSD_SMARTCARD_MANAGER_STATE_STARTED, GSD_SMARTCARD_MANAGER_STATE_STOPPING, }; struct _GsdSmartcardManagerPrivate { GsdSmartcardManagerState state; GList *modules; char *module_path; GList *workers; GPid smartcard_event_watcher_pid; GHashTable *smartcards; guint poll_timeout_id; guint32 is_unstoppable : 1; guint32 nss_is_loaded : 1; }; struct _GsdSmartcardManagerWorker { GsdSmartcardManager *manager; int manager_fd; GThread *thread; SECMODModule *module; GHashTable *smartcards; int fd; GSource *event_source; guint32 nss_is_loaded : 1; }; static void gsd_smartcard_manager_finalize (GObject *object); static void gsd_smartcard_manager_class_install_signals (GsdSmartcardManagerClass *service_class); static void gsd_smartcard_manager_class_install_properties (GsdSmartcardManagerClass *service_class); static void gsd_smartcard_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gsd_smartcard_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void gsd_smartcard_manager_set_module_path (GsdSmartcardManager *manager, const char *module_path); static void gsd_smartcard_manager_card_removed_handler (GsdSmartcardManager *manager, GsdSmartcard *card); static void gsd_smartcard_manager_card_inserted_handler (GsdSmartcardManager *manager_class, GsdSmartcard *card); static gboolean gsd_smartcard_manager_stop_now (GsdSmartcardManager *manager); static void gsd_smartcard_manager_queue_stop (GsdSmartcardManager *manager); static GsdSmartcardManagerWorker *gsd_smartcard_manager_create_worker (GsdSmartcardManager *manager, SECMODModule *module); static GsdSmartcardManagerWorker * gsd_smartcard_manager_worker_new (GsdSmartcardManager *manager, int worker_fd, int manager_fd, SECMODModule *module); static void gsd_smartcard_manager_worker_free (GsdSmartcardManagerWorker *worker); static gboolean open_pipe (int *write_fd, int *read_fd); static gboolean read_bytes (int fd, gpointer bytes, gsize num_bytes); static gboolean write_bytes (int fd, gconstpointer bytes, gsize num_bytes); static GsdSmartcard *read_smartcard (int fd, SECMODModule *module); static gboolean write_smartcard (int fd, GsdSmartcard *card); enum { PROP_0 = 0, PROP_MODULE_PATH, NUMBER_OF_PROPERTIES }; enum { SMARTCARD_INSERTED = 0, SMARTCARD_REMOVED, ERROR, NUMBER_OF_SIGNALS }; static guint gsd_smartcard_manager_signals[NUMBER_OF_SIGNALS]; G_DEFINE_TYPE (GsdSmartcardManager, gsd_smartcard_manager, G_TYPE_OBJECT); static void gsd_smartcard_manager_class_init (GsdSmartcardManagerClass *manager_class) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (manager_class); gobject_class->finalize = gsd_smartcard_manager_finalize; gsd_smartcard_manager_class_install_signals (manager_class); gsd_smartcard_manager_class_install_properties (manager_class); g_type_class_add_private (manager_class, sizeof (GsdSmartcardManagerPrivate)); } static void gsd_smartcard_manager_class_install_properties (GsdSmartcardManagerClass *card_class) { GObjectClass *object_class; GParamSpec *param_spec; object_class = G_OBJECT_CLASS (card_class); object_class->set_property = gsd_smartcard_manager_set_property; object_class->get_property = gsd_smartcard_manager_get_property; param_spec = g_param_spec_string ("module-path", "Module Path", "path to smartcard PKCS #11 driver", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_MODULE_PATH, param_spec); } static void gsd_smartcard_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GsdSmartcardManager *manager = GSD_SMARTCARD_MANAGER (object); switch (prop_id) { case PROP_MODULE_PATH: gsd_smartcard_manager_set_module_path (manager, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_smartcard_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsdSmartcardManager *manager = GSD_SMARTCARD_MANAGER (object); char *module_path; switch (prop_id) { case PROP_MODULE_PATH: module_path = gsd_smartcard_manager_get_module_path (manager); g_value_set_string (value, module_path); g_free (module_path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } char * gsd_smartcard_manager_get_module_path (GsdSmartcardManager *manager) { return manager->priv->module_path; } static void gsd_smartcard_manager_set_module_path (GsdSmartcardManager *manager, const char *module_path) { if ((manager->priv->module_path == NULL) && (module_path == NULL)) { return; } if (((manager->priv->module_path == NULL) || (module_path == NULL) || (strcmp (manager->priv->module_path, module_path) != 0))) { g_free (manager->priv->module_path); manager->priv->module_path = g_strdup (module_path); g_object_notify (G_OBJECT (manager), "module-path"); } } static void gsd_smartcard_manager_card_removed_handler (GsdSmartcardManager *manager, GsdSmartcard *card) { g_debug ("informing smartcard of its removal"); _gsd_smartcard_set_state (card, GSD_SMARTCARD_STATE_REMOVED); g_debug ("done"); } static void gsd_smartcard_manager_card_inserted_handler (GsdSmartcardManager *manager, GsdSmartcard *card) { g_debug ("informing smartcard of its insertion"); _gsd_smartcard_set_state (card, GSD_SMARTCARD_STATE_INSERTED); g_debug ("done"); } static void gsd_smartcard_manager_class_install_signals (GsdSmartcardManagerClass *manager_class) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (manager_class); gsd_smartcard_manager_signals[SMARTCARD_INSERTED] = g_signal_new ("smartcard-inserted", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GsdSmartcardManagerClass, smartcard_inserted), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); manager_class->smartcard_inserted = gsd_smartcard_manager_card_inserted_handler; gsd_smartcard_manager_signals[SMARTCARD_REMOVED] = g_signal_new ("smartcard-removed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GsdSmartcardManagerClass, smartcard_removed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); manager_class->smartcard_removed = gsd_smartcard_manager_card_removed_handler; gsd_smartcard_manager_signals[ERROR] = g_signal_new ("error", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsdSmartcardManagerClass, error), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); manager_class->error = NULL; } static gboolean slot_id_equal (CK_SLOT_ID *slot_id_1, CK_SLOT_ID *slot_id_2) { g_assert (slot_id_1 != NULL); g_assert (slot_id_2 != NULL); return *slot_id_1 == *slot_id_2; } static gboolean slot_id_hash (CK_SLOT_ID *slot_id) { guint32 upper_bits, lower_bits; int temp; if (sizeof (CK_SLOT_ID) == sizeof (int)) { return g_int_hash (slot_id); } upper_bits = ((*slot_id) >> 31) - 1; lower_bits = (*slot_id) & 0xffffffff; /* The upper bits are almost certainly always zero, * so let's degenerate to g_int_hash for the * (very) common case */ temp = lower_bits + upper_bits; return upper_bits + g_int_hash (&temp); } static void gsd_smartcard_manager_init (GsdSmartcardManager *manager) { g_debug ("initializing smartcard manager"); manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, GSD_TYPE_SMARTCARD_MANAGER, GsdSmartcardManagerPrivate); manager->priv->poll_timeout_id = 0; manager->priv->is_unstoppable = FALSE; manager->priv->smartcards = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); } static void gsd_smartcard_manager_finalize (GObject *object) { GsdSmartcardManager *manager; GObjectClass *gobject_class; manager = GSD_SMARTCARD_MANAGER (object); gobject_class = G_OBJECT_CLASS (gsd_smartcard_manager_parent_class); gsd_smartcard_manager_stop_now (manager); g_hash_table_destroy (manager->priv->smartcards); manager->priv->smartcards = NULL; gobject_class->finalize (object); } GQuark gsd_smartcard_manager_error_quark (void) { static GQuark error_quark = 0; if (error_quark == 0) { error_quark = g_quark_from_static_string ("gsd-smartcard-manager-error-quark"); } return error_quark; } GsdSmartcardManager * gsd_smartcard_manager_new_default (void) { return gsd_smartcard_manager_new (NULL); } GsdSmartcardManager * gsd_smartcard_manager_new (const char *module_path) { GsdSmartcardManager *instance; instance = GSD_SMARTCARD_MANAGER (g_object_new (GSD_TYPE_SMARTCARD_MANAGER, "module-path", module_path, NULL)); return instance; } static void gsd_smartcard_manager_emit_error (GsdSmartcardManager *manager, GError *error) { manager->priv->is_unstoppable = TRUE; g_signal_emit (manager, gsd_smartcard_manager_signals[ERROR], 0, error); manager->priv->is_unstoppable = FALSE; } static void gsd_smartcard_manager_emit_smartcard_inserted (GsdSmartcardManager *manager, GsdSmartcard *card) { manager->priv->is_unstoppable = TRUE; g_signal_emit (manager, gsd_smartcard_manager_signals[SMARTCARD_INSERTED], 0, card); manager->priv->is_unstoppable = FALSE; } static void gsd_smartcard_manager_emit_smartcard_removed (GsdSmartcardManager *manager, GsdSmartcard *card) { manager->priv->is_unstoppable = TRUE; g_signal_emit (manager, gsd_smartcard_manager_signals[SMARTCARD_REMOVED], 0, card); manager->priv->is_unstoppable = FALSE; } static gboolean gsd_smartcard_manager_check_for_and_process_events (GIOChannel *io_channel, GIOCondition condition, GsdSmartcardManagerWorker *worker) { GsdSmartcard *card; GsdSmartcardManager *manager; gboolean should_stop; guchar event_type; char *card_name; int fd; manager = worker->manager; g_debug ("event!"); card = NULL; should_stop = (condition & G_IO_HUP) || (condition & G_IO_ERR); if (should_stop) { g_debug ("received %s on event socket, stopping " "manager...", (condition & G_IO_HUP) && (condition & G_IO_ERR)? "error and hangup" : (condition & G_IO_HUP)? "hangup" : "error"); } if (!(condition & G_IO_IN)) { g_debug ("nevermind outta here!"); goto out; } fd = g_io_channel_unix_get_fd (io_channel); event_type = '\0'; if (!read_bytes (fd, &event_type, 1)) { g_debug ("could not read event type, stopping"); should_stop = TRUE; goto out; } card = read_smartcard (fd, worker->module); if (card == NULL) { g_debug ("could not read card, stopping"); should_stop = TRUE; goto out; } card_name = gsd_smartcard_get_name (card); g_debug ("card '%s' had event %c", card_name, event_type); switch (event_type) { case 'I': g_hash_table_replace (manager->priv->smartcards, card_name, card); card_name = NULL; gsd_smartcard_manager_emit_smartcard_inserted (manager, card); card = NULL; break; case 'R': gsd_smartcard_manager_emit_smartcard_removed (manager, card); if (!g_hash_table_remove (manager->priv->smartcards, card_name)) { g_debug ("got removal event of unknown card!"); } g_free (card_name); card_name = NULL; card = NULL; break; default: g_free (card_name); card_name = NULL; g_object_unref (card); should_stop = TRUE; break; } out: if (should_stop) { GError *error; error = g_error_new (GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, "%s", (condition & G_IO_IN) ? g_strerror (errno) : _("received error or hang up from event source")); gsd_smartcard_manager_emit_error (manager, error); g_error_free (error); gsd_smartcard_manager_stop_now (manager); return FALSE; } return TRUE; } static void stop_manager (GsdSmartcardManager *manager) { manager->priv->state = GSD_SMARTCARD_MANAGER_STATE_STOPPED; if (manager->priv->nss_is_loaded) { NSS_Shutdown (); manager->priv->nss_is_loaded = FALSE; } g_debug ("smartcard manager stopped"); } static void stop_worker (GsdSmartcardManagerWorker *worker) { GsdSmartcardManager *manager; manager = worker->manager; if (worker->event_source != NULL) { g_source_destroy (worker->event_source); worker->event_source = NULL; } if (worker->thread != NULL) { SECMOD_CancelWait (worker->module); worker->thread = NULL; } SECMOD_DestroyModule (worker->module); manager->priv->workers = g_list_remove (manager->priv->workers, worker); if (manager->priv->workers == NULL && manager->priv->state != GSD_SMARTCARD_MANAGER_STATE_STOPPED) { stop_manager (manager); } } static void gsd_smartcard_manager_event_processing_stopped_handler (GsdSmartcardManagerWorker *worker) { worker->event_source = NULL; stop_worker (worker); } static gboolean open_pipe (int *write_fd, int *read_fd) { int pipe_fds[2] = { -1, -1 }; g_assert (write_fd != NULL); g_assert (read_fd != NULL); if (pipe (pipe_fds) < 0) { return FALSE; } if (fcntl (pipe_fds[0], F_SETFD, FD_CLOEXEC) < 0) { close (pipe_fds[0]); close (pipe_fds[1]); return FALSE; } if (fcntl (pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) { close (pipe_fds[0]); close (pipe_fds[1]); return FALSE; } *read_fd = pipe_fds[0]; *write_fd = pipe_fds[1]; return TRUE; } static void gsd_smartcard_manager_stop_watching_for_events (GsdSmartcardManager *manager) { GList *node; node = manager->priv->workers; while (node != NULL) { GsdSmartcardManagerWorker *worker; GList *next_node; worker = (GsdSmartcardManagerWorker *) node->data; next_node = node->next; stop_worker (worker); node = next_node; } } static gboolean load_nss (GError **error) { SECStatus status = SECSuccess; static const guint32 flags = NSS_INIT_READONLY | NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD; g_debug ("attempting to load NSS database '%s'", GSD_SMARTCARD_MANAGER_NSS_DB); PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); status = NSS_Initialize (GSD_SMARTCARD_MANAGER_NSS_DB, "", "", SECMOD_DB, flags); if (status != SECSuccess) { gsize error_message_size; char *error_message; error_message_size = PR_GetErrorTextLength (); if (error_message_size == 0) { g_debug ("NSS security system could not be initialized"); g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_WITH_NSS, _("NSS security system could not be initialized")); goto out; } error_message = g_slice_alloc0 (error_message_size); PR_GetErrorText (error_message); g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_WITH_NSS, "%s", error_message); g_debug ("NSS security system could not be initialized - %s", error_message); g_slice_free1 (error_message_size, error_message); goto out; } g_debug ("NSS database sucessfully loaded"); return TRUE; out: g_debug ("NSS database couldn't be sucessfully loaded"); return FALSE; } static GList * get_available_modules (GsdSmartcardManager *manager) { SECMODModuleList *module_list, *tmp; GList *modules; g_debug ("Getting list of suitable modules"); module_list = SECMOD_GetDefaultModuleList (); modules = NULL; for (tmp = module_list; tmp != NULL; tmp = tmp->next) { if (!SECMOD_HasRemovableSlots (tmp->module) || !tmp->module->loaded) continue; g_debug ("Using module '%s'", tmp->module->commonName); modules = g_list_prepend (modules, SECMOD_ReferenceModule (tmp->module)); } return modules; } static gboolean load_driver (GsdSmartcardManager *manager, char *module_path, GError **error) { GList *modules; char *module_spec; gboolean module_explicitly_specified; g_debug ("attempting to load driver..."); modules = NULL; module_explicitly_specified = module_path != NULL; if (module_explicitly_specified) { SECMODModule *module; module_spec = g_strdup_printf ("library=\"%s\"", module_path); g_debug ("loading smartcard driver using spec '%s'", module_spec); module = SECMOD_LoadUserModule (module_spec, NULL /* parent */, FALSE /* recurse */); g_free (module_spec); module_spec = NULL; if (SECMOD_HasRemovableSlots (module) && module->loaded) { modules = g_list_prepend (modules, module); } else { g_debug ("fallback module found but not %s", SECMOD_HasRemovableSlots (module)? "removable" : "loaded"); SECMOD_DestroyModule (module); } } else { SECMODListLock *lock; lock = SECMOD_GetDefaultModuleListLock (); if (lock != NULL) { SECMOD_GetReadLock (lock); modules = get_available_modules (manager); SECMOD_ReleaseReadLock (lock); } /* fallback to compiled in driver path */ if (modules == NULL) { SECMODModule *module; module_path = GSD_SMARTCARD_MANAGER_DRIVER; module_spec = g_strdup_printf ("library=\"%s\"", module_path); g_debug ("loading smartcard driver using spec '%s'", module_spec); module = SECMOD_LoadUserModule (module_spec, NULL /* parent */, FALSE /* recurse */); g_free (module_spec); module_spec = NULL; if (SECMOD_HasRemovableSlots (module) && module->loaded) { modules = g_list_prepend (modules, module); } else { g_debug ("fallback module found but not loaded"); SECMOD_DestroyModule (module); } } } if (!module_explicitly_specified && modules == NULL) { g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, _("no suitable smartcard driver could be found")); } else if (modules == NULL) { gsize error_message_size; char *error_message; error_message_size = PR_GetErrorTextLength (); if (error_message_size == 0) { g_debug ("smartcard driver '%s' could not be loaded", module_path); g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, _("smartcard driver '%s' could not be " "loaded"), module_path); goto out; } error_message = g_slice_alloc0 (error_message_size); PR_GetErrorText (error_message); g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, "%s", error_message); g_debug ("smartcard driver '%s' could not be loaded - %s", module_path, error_message); g_slice_free1 (error_message_size, error_message); } manager->priv->modules = modules; out: return manager->priv->modules != NULL; } static void gsd_smartcard_manager_get_all_cards (GsdSmartcardManager *manager) { GList *node; int i; node = manager->priv->workers; while (node != NULL) { GsdSmartcardManagerWorker *worker; worker = (GsdSmartcardManagerWorker *) node->data; for (i = 0; i < worker->module->slotCount; i++) { GsdSmartcard *card; CK_SLOT_ID slot_id; int slot_series; char *card_name; slot_id = PK11_GetSlotID (worker->module->slots[i]); slot_series = PK11_GetSlotSeries (worker->module->slots[i]); card = _gsd_smartcard_new (worker->module, slot_id, slot_series); card_name = gsd_smartcard_get_name (card); g_hash_table_replace (manager->priv->smartcards, card_name, card); } node = node->next; } } static GsdSmartcardManagerWorker * start_worker (GsdSmartcardManager *manager, SECMODModule *module, GError **error) { GIOChannel *io_channel; GSource *source; GsdSmartcardManagerWorker *worker; worker = gsd_smartcard_manager_create_worker (manager, module); if (worker == NULL) { g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, _("could not watch for incoming card events - %s"), g_strerror (errno)); goto out; } io_channel = g_io_channel_unix_new (worker->manager_fd); source = g_io_create_watch (io_channel, G_IO_IN | G_IO_HUP); g_io_channel_unref (io_channel); io_channel = NULL; worker->event_source = source; g_source_set_callback (worker->event_source, (GSourceFunc) (GIOFunc) gsd_smartcard_manager_check_for_and_process_events, worker, (GDestroyNotify) gsd_smartcard_manager_event_processing_stopped_handler); g_source_attach (worker->event_source, NULL); g_source_unref (worker->event_source); out: return worker; } static void start_workers (GsdSmartcardManager *manager) { GList *node; node = manager->priv->modules; while (node != NULL) { SECMODModule *module; GsdSmartcardManagerWorker *worker; GError *error; module = (SECMODModule *) node->data; error = NULL; worker = start_worker (manager, module, &error); if (worker == NULL) { g_warning ("%s", error->message); g_error_free (error); } else { manager->priv->workers = g_list_prepend (manager->priv->workers, worker); } node = node->next; } } gboolean gsd_smartcard_manager_start (GsdSmartcardManager *manager, GError **error) { GError *nss_error; if (manager->priv->state == GSD_SMARTCARD_MANAGER_STATE_STARTED) { g_debug ("smartcard manager already started"); return TRUE; } manager->priv->state = GSD_SMARTCARD_MANAGER_STATE_STARTING; nss_error = NULL; if (!manager->priv->nss_is_loaded && !load_nss (&nss_error)) { g_propagate_error (error, nss_error); goto out; } manager->priv->nss_is_loaded = TRUE; if (manager->priv->modules == NULL) { if (!load_driver (manager, manager->priv->module_path, &nss_error)) { g_propagate_error (error, nss_error); goto out; } } start_workers (manager); /* populate the hash with cards that are already inserted */ gsd_smartcard_manager_get_all_cards (manager); manager->priv->state = GSD_SMARTCARD_MANAGER_STATE_STARTED; out: /* don't leave it in a half started state */ if (manager->priv->state != GSD_SMARTCARD_MANAGER_STATE_STARTED) { g_debug ("smartcard manager could not be completely started"); gsd_smartcard_manager_stop (manager); } else { g_debug ("smartcard manager started"); } return manager->priv->state == GSD_SMARTCARD_MANAGER_STATE_STARTED; } static gboolean gsd_smartcard_manager_stop_now (GsdSmartcardManager *manager) { if (manager->priv->state == GSD_SMARTCARD_MANAGER_STATE_STOPPED) { return FALSE; } gsd_smartcard_manager_stop_watching_for_events (manager); return FALSE; } static void gsd_smartcard_manager_queue_stop (GsdSmartcardManager *manager) { manager->priv->state = GSD_SMARTCARD_MANAGER_STATE_STOPPING; g_idle_add ((GSourceFunc) gsd_smartcard_manager_stop_now, manager); } void gsd_smartcard_manager_stop (GsdSmartcardManager *manager) { if (manager->priv->state == GSD_SMARTCARD_MANAGER_STATE_STOPPED) { return; } if (manager->priv->is_unstoppable) { gsd_smartcard_manager_queue_stop (manager); return; } gsd_smartcard_manager_stop_now (manager); } static void gsd_smartcard_manager_check_for_login_card (CK_SLOT_ID slot_id, GsdSmartcard *card, gboolean *is_inserted) { g_assert (is_inserted != NULL); if (gsd_smartcard_is_login_card (card)) { *is_inserted = TRUE; } } gboolean gsd_smartcard_manager_login_card_is_inserted (GsdSmartcardManager *manager) { gboolean is_inserted; is_inserted = FALSE; g_hash_table_foreach (manager->priv->smartcards, (GHFunc) gsd_smartcard_manager_check_for_login_card, &is_inserted); return is_inserted; } static GsdSmartcardManagerWorker * gsd_smartcard_manager_worker_new (GsdSmartcardManager *manager, int worker_fd, int manager_fd, SECMODModule *module) { GsdSmartcardManagerWorker *worker; worker = g_slice_new0 (GsdSmartcardManagerWorker); worker->manager = manager; worker->fd = worker_fd; worker->manager_fd = manager_fd; worker->module = module; worker->smartcards = g_hash_table_new_full ((GHashFunc) slot_id_hash, (GEqualFunc) slot_id_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); return worker; } static void gsd_smartcard_manager_worker_free (GsdSmartcardManagerWorker *worker) { if (worker->smartcards != NULL) { g_hash_table_destroy (worker->smartcards); worker->smartcards = NULL; } g_slice_free (GsdSmartcardManagerWorker, worker); } static gboolean read_bytes (int fd, gpointer bytes, gsize num_bytes) { size_t bytes_left; size_t total_bytes_read; ssize_t bytes_read; bytes_left = (size_t) num_bytes; total_bytes_read = 0; do { bytes_read = read (fd, (char *) bytes + total_bytes_read, bytes_left); g_assert (bytes_read <= (ssize_t) bytes_left); if (bytes_read <= 0) { if ((bytes_read < 0) && (errno == EINTR || errno == EAGAIN)) { continue; } bytes_left = 0; } else { bytes_left -= bytes_read; total_bytes_read += bytes_read; } } while (bytes_left > 0); if (total_bytes_read < (size_t) num_bytes) { return FALSE; } return TRUE; } static gboolean write_bytes (int fd, gconstpointer bytes, gsize num_bytes) { size_t bytes_left; size_t total_bytes_written; ssize_t bytes_written; bytes_left = (size_t) num_bytes; total_bytes_written = 0; do { bytes_written = write (fd, (char *) bytes + total_bytes_written, bytes_left); g_assert (bytes_written <= (ssize_t) bytes_left); if (bytes_written <= 0) { if ((bytes_written < 0) && (errno == EINTR || errno == EAGAIN)) { continue; } bytes_left = 0; } else { bytes_left -= bytes_written; total_bytes_written += bytes_written; } } while (bytes_left > 0); if (total_bytes_written < (size_t) num_bytes) { return FALSE; } return TRUE; } static GsdSmartcard * read_smartcard (int fd, SECMODModule *module) { GsdSmartcard *card; char *card_name; gsize card_name_size; card_name_size = 0; if (!read_bytes (fd, &card_name_size, sizeof (card_name_size))) { return NULL; } card_name = g_slice_alloc0 (card_name_size); if (!read_bytes (fd, card_name, card_name_size)) { g_slice_free1 (card_name_size, card_name); return NULL; } card = _gsd_smartcard_new_from_name (module, card_name); g_slice_free1 (card_name_size, card_name); return card; } static gboolean write_smartcard (int fd, GsdSmartcard *card) { gsize card_name_size; char *card_name; card_name = gsd_smartcard_get_name (card); card_name_size = strlen (card_name) + 1; if (!write_bytes (fd, &card_name_size, sizeof (card_name_size))) { g_free (card_name); return FALSE; } if (!write_bytes (fd, card_name, card_name_size)) { g_free (card_name); return FALSE; } g_free (card_name); return TRUE; } static gboolean gsd_smartcard_manager_worker_emit_smartcard_removed (GsdSmartcardManagerWorker *worker, GsdSmartcard *card, GError **error) { g_debug ("card '%s' removed!", gsd_smartcard_get_name (card)); if (!write_bytes (worker->fd, "R", 1)) { goto error_out; } if (!write_smartcard (worker->fd, card)) { goto error_out; } return TRUE; error_out: g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS, "%s", g_strerror (errno)); return FALSE; } static gboolean gsd_smartcard_manager_worker_emit_smartcard_inserted (GsdSmartcardManagerWorker *worker, GsdSmartcard *card, GError **error) { g_debug ("card '%s' inserted!", gsd_smartcard_get_name (card)); if (!write_bytes (worker->fd, "I", 1)) { goto error_out; } if (!write_smartcard (worker->fd, card)) { goto error_out; } return TRUE; error_out: g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS, "%s", g_strerror (errno)); return FALSE; } static gboolean gsd_smartcard_manager_worker_watch_for_and_process_event (GsdSmartcardManagerWorker *worker, GError **error) { PK11SlotInfo *slot; CK_SLOT_ID slot_id, *key = NULL; int slot_series, card_slot_series; GsdSmartcard *card; GError *processing_error; gboolean ret; g_debug ("waiting for card event"); ret = FALSE; slot = SECMOD_WaitForAnyTokenEvent (worker->module, 0, PR_SecondsToInterval (1)); processing_error = NULL; if (slot == NULL) { int error_code; error_code = PORT_GetError (); if ((error_code == 0) || (error_code == SEC_ERROR_NO_EVENT)) { g_debug ("spurrious event occurred"); return TRUE; } /* FIXME: is there a function to convert from a PORT error * code to a translated string? */ g_set_error (error, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_WITH_NSS, _("encountered unexpected error while " "waiting for smartcard events")); goto out; } /* the slot id and series together uniquely identify a card. * You can never have two cards with the same slot id at the * same time, however (I think), so we can key off of it. */ slot_id = PK11_GetSlotID (slot); slot_series = PK11_GetSlotSeries (slot); /* First check to see if there is a card that we're currently * tracking in the slot. */ key = g_new (CK_SLOT_ID, 1); *key = slot_id; card = g_hash_table_lookup (worker->smartcards, key); if (card != NULL) { card_slot_series = gsd_smartcard_get_slot_series (card); } else { card_slot_series = -1; } if (PK11_IsPresent (slot)) { /* Now, check to see if their is a new card in the slot. * If there was a different card in the slot now than * there was before, then we need to emit a removed signal * for the old card (we don't want unpaired insertion events). */ if ((card != NULL) && card_slot_series != slot_series) { if (!gsd_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { g_propagate_error (error, processing_error); goto out; } } card = _gsd_smartcard_new (worker->module, slot_id, slot_series); g_hash_table_replace (worker->smartcards, key, card); key = NULL; if (!gsd_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) { g_propagate_error (error, processing_error); goto out; } } else { /* if we aren't tracking the card, just discard the event. * We don't want unpaired remove events. Note on startup * NSS will generate an "insertion" event if a card is * already inserted in the slot. */ if ((card != NULL)) { /* FIXME: i'm not sure about this code. Maybe we * shouldn't do this at all, or maybe we should do it * n times (where n = slot_series - card_slot_series + 1) * * Right now, i'm just doing it once. */ if ((slot_series - card_slot_series) > 1) { if (!gsd_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { g_propagate_error (error, processing_error); goto out; } g_hash_table_remove (worker->smartcards, key); card = _gsd_smartcard_new (worker->module, slot_id, slot_series); g_hash_table_replace (worker->smartcards, key, card); key = NULL; if (!gsd_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) { g_propagate_error (error, processing_error); goto out; } } if (!gsd_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { g_propagate_error (error, processing_error); goto out; } g_hash_table_remove (worker->smartcards, key); card = NULL; } else { g_debug ("got spurious remove event"); } } ret = TRUE; out: g_free (key); PK11_FreeSlot (slot); return ret; } static void gsd_smartcard_manager_worker_run (GsdSmartcardManagerWorker *worker) { GError *error; gboolean should_continue; do { error = NULL; should_continue = gsd_smartcard_manager_worker_watch_for_and_process_event (worker, &error); } while (should_continue); if (error != NULL) { g_debug ("could not process card event - %s", error->message); g_error_free (error); } gsd_smartcard_manager_worker_free (worker); } static GsdSmartcardManagerWorker * gsd_smartcard_manager_create_worker (GsdSmartcardManager *manager, SECMODModule *module) { GsdSmartcardManagerWorker *worker; int write_fd, read_fd; write_fd = -1; read_fd = -1; if (!open_pipe (&write_fd, &read_fd)) { return NULL; } worker = gsd_smartcard_manager_worker_new (manager, write_fd, read_fd, module); worker->thread = g_thread_create ((GThreadFunc) gsd_smartcard_manager_worker_run, worker, FALSE, NULL); if (worker->thread == NULL) { gsd_smartcard_manager_worker_free (worker); return NULL; } return worker; } #ifdef GSD_SMARTCARD_MANAGER_ENABLE_TEST #include static GMainLoop *event_loop; static gboolean should_exit_on_next_remove = FALSE; static gboolean on_timeout (GsdSmartcardManager *manager) { GError *error; g_print ("Re-enabling manager.\n"); if (!gsd_smartcard_manager_start (manager, &error)) { g_warning ("could not start smartcard manager - %s", error->message); g_error_free (error); return TRUE; } g_print ("Please re-insert smartcard\n"); should_exit_on_next_remove = TRUE; return FALSE; } static void on_device_inserted (GsdSmartcardManager *manager, GsdSmartcard *card) { g_print ("smartcard inserted!\n"); g_print ("Please remove it.\n"); } static void on_device_removed (GsdSmartcardManager *manager, GsdSmartcard *card) { g_print ("smartcard removed!\n"); if (should_exit_on_next_remove) { g_main_loop_quit (event_loop); } else { g_print ("disabling manager for 2 seconds\n"); gsd_smartcard_manager_stop (manager); g_timeout_add_seconds (2, (GSourceFunc) on_timeout, manager); } } int main (int argc, char *argv[]) { GsdSmartcardManager *manager; GError *error; g_log_set_always_fatal (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); g_message ("creating instance of 'smartcard manager' object..."); manager = gsd_smartcard_manager_new (NULL); g_message ("'smartcard manager' object created successfully"); g_signal_connect (manager, "smartcard-inserted", G_CALLBACK (on_device_inserted), NULL); g_signal_connect (manager, "smartcard-removed", G_CALLBACK (on_device_removed), NULL); g_message ("starting listener..."); error = NULL; if (!gsd_smartcard_manager_start (manager, &error)) { g_warning ("could not start smartcard manager - %s", error->message); g_error_free (error); return 1; } event_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (event_loop); g_main_loop_unref (event_loop); event_loop = NULL; g_message ("destroying previously created 'smartcard manager' object..."); g_object_unref (manager); manager = NULL; g_message ("'smartcard manager' object destroyed successfully"); return 0; } #endif ./plugins/smartcard/test-smartcard.c0000644000004100000410000000034112735467744020026 0ustar www-datawww-data#define NEW gsd_smartcard_manager_new_default #define START gsd_smartcard_manager_start #define STOP gsd_smartcard_manager_stop #define MANAGER GsdSmartcardManager #include "gsd-smartcard-manager.h" #include "test-plugin.h" ./plugins/smartcard/gsd-smartcard-plugin.c0000644000004100000410000002404012735467763021123 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 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. * */ #include "config.h" #include #include #include #include #include #include "gnome-settings-plugin.h" #include "gnome-settings-bus.h" #include "gsd-smartcard-plugin.h" #include "gsd-smartcard-manager.h" struct GsdSmartcardPluginPrivate { GsdSmartcardManager *manager; guint32 is_active : 1; }; typedef enum { GSD_SMARTCARD_REMOVE_ACTION_NONE, GSD_SMARTCARD_REMOVE_ACTION_LOCK_SCREEN, GSD_SMARTCARD_REMOVE_ACTION_FORCE_LOGOUT, } GsdSmartcardRemoveAction; #define SM_LOGOUT_MODE_FORCE 2 #define KEY_REMOVE_ACTION "removal-action" #define GSD_SMARTCARD_PLUGIN_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), GSD_TYPE_SMARTCARD_PLUGIN, GsdSmartcardPluginPrivate)) GNOME_SETTINGS_PLUGIN_REGISTER (GsdSmartcardPlugin, gsd_smartcard_plugin); static void simulate_user_activity (GsdSmartcardPlugin *plugin) { GsdScreenSaver *screensaver_proxy; GDBusProxy *screensaver_proxy; g_debug ("GsdSmartcardPlugin telling screensaver about smart card insertion"); screensaver_proxy = gnome_settings_bus_get_screen_saver_proxy (); gsd_screen_saver_call_simulate_user_activity_sync (screensaver_proxy, NULL, NULL); g_object_unref (screensaver_proxy); } static void lock_screen (GsdSmartcardPlugin *plugin) { GDBusProxy *screensaver_proxy; g_debug ("GsdSmartcardPlugin telling screensaver to lock screen"); screensaver_proxy = gnome_settings_bus_get_screen_saver_proxy (); gsd_screen_saver_call_lock_sync (screensaver_proxy, NULL, NULL); g_object_unref (screensaver_proxy); } static void force_logout (GsdSmartcardPlugin *plugin) { GDBusProxy *sm_proxy; GError *error; GVariant *res; g_debug ("GsdSmartcardPlugin telling session manager to force logout"); sm_proxy = gnome_settings_bus_get_session_proxy (); error = NULL; res = g_dbus_proxy_call_sync (sm_proxy, "Logout", g_variant_new ("(i)", SM_LOGOUT_MODE_FORCE), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (! res) { g_warning ("GsdSmartcardPlugin Unable to force logout: %s", error->message); g_error_free (error); } else g_variant_unref (res); g_object_unref (sm_proxy); } static void gsd_smartcard_plugin_init (GsdSmartcardPlugin *plugin) { plugin->priv = GSD_SMARTCARD_PLUGIN_GET_PRIVATE (plugin); g_debug ("GsdSmartcardPlugin initializing"); plugin->priv->manager = gsd_smartcard_manager_new (NULL); } static void gsd_smartcard_plugin_finalize (GObject *object) { GsdSmartcardPlugin *plugin; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_SMARTCARD_PLUGIN (object)); g_debug ("GsdSmartcardPlugin finalizing"); plugin = GSD_SMARTCARD_PLUGIN (object); g_return_if_fail (plugin->priv != NULL); if (plugin->priv->manager != NULL) { g_object_unref (plugin->priv->manager); } G_OBJECT_CLASS (gsd_smartcard_plugin_parent_class)->finalize (object); } static void smartcard_inserted_cb (GsdSmartcardManager *card_monitor, GsdSmartcard *card, GsdSmartcardPlugin *plugin) { char *name; name = gsd_smartcard_get_name (card); g_debug ("GsdSmartcardPlugin smart card '%s' inserted", name); g_free (name); simulate_user_activity (plugin); } static gboolean user_logged_in_with_smartcard (void) { return g_getenv ("PKCS11_LOGIN_TOKEN_NAME") != NULL; } static GsdSmartcardRemoveAction get_configured_remove_action (GsdSmartcardPlugin *plugin) { GSettings *settings; char *remove_action_string; GsdSmartcardRemoveAction remove_action; settings = g_settings_new ("org.gnome.settings-daemon.peripherals.smartcard"); remove_action_string = g_settings_get_string (settings, KEY_REMOVE_ACTION); if (remove_action_string == NULL) { g_warning ("GsdSmartcardPlugin unable to get smartcard remove action"); remove_action = GSD_SMARTCARD_REMOVE_ACTION_NONE; } else if (strcmp (remove_action_string, "none") == 0) { remove_action = GSD_SMARTCARD_REMOVE_ACTION_NONE; } else if (strcmp (remove_action_string, "lock_screen") == 0) { remove_action = GSD_SMARTCARD_REMOVE_ACTION_LOCK_SCREEN; } else if (strcmp (remove_action_string, "force_logout") == 0) { remove_action = GSD_SMARTCARD_REMOVE_ACTION_FORCE_LOGOUT; } else { g_warning ("GsdSmartcardPlugin unknown smartcard remove action"); remove_action = GSD_SMARTCARD_REMOVE_ACTION_NONE; } g_object_unref (settings); return remove_action; } static void process_smartcard_removal (GsdSmartcardPlugin *plugin) { GsdSmartcardRemoveAction remove_action; g_debug ("GsdSmartcardPlugin processing smartcard removal"); remove_action = get_configured_remove_action (plugin); switch (remove_action) { case GSD_SMARTCARD_REMOVE_ACTION_NONE: return; case GSD_SMARTCARD_REMOVE_ACTION_LOCK_SCREEN: lock_screen (plugin); break; case GSD_SMARTCARD_REMOVE_ACTION_FORCE_LOGOUT: force_logout (plugin); break; } } static void smartcard_removed_cb (GsdSmartcardManager *card_monitor, GsdSmartcard *card, GsdSmartcardPlugin *plugin) { char *name; name = gsd_smartcard_get_name (card); g_debug ("GsdSmartcardPlugin smart card '%s' removed", name); g_free (name); if (!gsd_smartcard_is_login_card (card)) { g_debug ("GsdSmartcardPlugin removed smart card was not used to login"); return; } process_smartcard_removal (plugin); } static void impl_activate (GnomeSettingsPlugin *plugin) { GError *error; GsdSmartcardPlugin *smartcard_plugin = GSD_SMARTCARD_PLUGIN (plugin); if (smartcard_plugin->priv->is_active) { g_debug ("GsdSmartcardPlugin Not activating smartcard plugin, because it's " "already active"); return; } if (!user_logged_in_with_smartcard ()) { g_debug ("GsdSmartcardPlugin Not activating smartcard plugin, because user didn't use " " smartcard to log in"); smartcard_plugin->priv->is_active = FALSE; return; } g_debug ("GsdSmartcardPlugin Activating smartcard plugin"); error = NULL; if (!gsd_smartcard_manager_start (smartcard_plugin->priv->manager, &error)) { g_warning ("GsdSmartcardPlugin Unable to start smartcard manager: %s", error->message); g_error_free (error); } g_signal_connect (smartcard_plugin->priv->manager, "smartcard-removed", G_CALLBACK (smartcard_removed_cb), smartcard_plugin); g_signal_connect (smartcard_plugin->priv->manager, "smartcard-inserted", G_CALLBACK (smartcard_inserted_cb), smartcard_plugin); if (!gsd_smartcard_manager_login_card_is_inserted (smartcard_plugin->priv->manager)) { g_debug ("GsdSmartcardPlugin processing smartcard removal immediately user logged in with smartcard " "and it's not inserted"); process_smartcard_removal (smartcard_plugin); } smartcard_plugin->priv->is_active = TRUE; } static void impl_deactivate (GnomeSettingsPlugin *plugin) { GsdSmartcardPlugin *smartcard_plugin = GSD_SMARTCARD_PLUGIN (plugin); if (!smartcard_plugin->priv->is_active) { g_debug ("GsdSmartcardPlugin Not deactivating smartcard plugin, " "because it's already inactive"); return; } g_debug ("GsdSmartcardPlugin Deactivating smartcard plugin"); gsd_smartcard_manager_stop (smartcard_plugin->priv->manager); g_signal_handlers_disconnect_by_func (smartcard_plugin->priv->manager, smartcard_removed_cb, smartcard_plugin); g_signal_handlers_disconnect_by_func (smartcard_plugin->priv->manager, smartcard_inserted_cb, smartcard_plugin); smartcard_plugin->priv->is_active = FALSE; } static void gsd_smartcard_plugin_class_init (GsdSmartcardPluginClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GnomeSettingsPluginClass *plugin_class = GNOME_SETTINGS_PLUGIN_CLASS (klass); object_class->finalize = gsd_smartcard_plugin_finalize; plugin_class->activate = impl_activate; plugin_class->deactivate = impl_deactivate; g_type_class_add_private (klass, sizeof (GsdSmartcardPluginPrivate)); } ./plugins/smartcard/gsd-smartcard.c0000644000004100000410000004424112735467744017633 0ustar www-datawww-data/* gsd-smartcard.c - smartcard object * * Copyright (C) 2006 Ray Strode * * 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. */ #include "config.h" #define GSD_SMARTCARD_ENABLE_INTERNAL_API #include "gsd-smartcard.h" #include #include #include #include #include #include #include #include #include #include #include struct _GsdSmartcardPrivate { SECMODModule *module; GsdSmartcardState state; CK_SLOT_ID slot_id; int slot_series; PK11SlotInfo *slot; char *name; CERTCertificate *signing_certificate; CERTCertificate *encryption_certificate; }; static void gsd_smartcard_finalize (GObject *object); static void gsd_smartcard_class_install_signals (GsdSmartcardClass *card_class); static void gsd_smartcard_class_install_properties (GsdSmartcardClass *card_class); static void gsd_smartcard_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void gsd_smartcard_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void gsd_smartcard_set_name (GsdSmartcard *card, const char *name); static void gsd_smartcard_set_slot_id (GsdSmartcard *card, int slot_id); static void gsd_smartcard_set_slot_series (GsdSmartcard *card, int slot_series); static void gsd_smartcard_set_module (GsdSmartcard *card, SECMODModule *module); static PK11SlotInfo *gsd_smartcard_find_slot_from_id (GsdSmartcard *card, int slot_id); static PK11SlotInfo *gsd_smartcard_find_slot_from_card_name (GsdSmartcard *card, const char *card_name); #ifndef GSD_SMARTCARD_DEFAULT_SLOT_ID #define GSD_SMARTCARD_DEFAULT_SLOT_ID ((gulong) -1) #endif #ifndef GSD_SMARTCARD_DEFAULT_SLOT_SERIES #define GSD_SMARTCARD_DEFAULT_SLOT_SERIES -1 #endif enum { PROP_0 = 0, PROP_NAME, PROP_SLOT_ID, PROP_SLOT_SERIES, PROP_MODULE, NUMBER_OF_PROPERTIES }; enum { INSERTED, REMOVED, NUMBER_OF_SIGNALS }; static guint gsd_smartcard_signals[NUMBER_OF_SIGNALS]; G_DEFINE_TYPE (GsdSmartcard, gsd_smartcard, G_TYPE_OBJECT); static void gsd_smartcard_class_init (GsdSmartcardClass *card_class) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (card_class); gobject_class->finalize = gsd_smartcard_finalize; gsd_smartcard_class_install_signals (card_class); gsd_smartcard_class_install_properties (card_class); g_type_class_add_private (card_class, sizeof (GsdSmartcardPrivate)); } static void gsd_smartcard_class_install_signals (GsdSmartcardClass *card_class) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (card_class); gsd_smartcard_signals[INSERTED] = g_signal_new ("inserted", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsdSmartcardClass, inserted), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); gsd_smartcard_signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsdSmartcardClass, removed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void gsd_smartcard_class_install_properties (GsdSmartcardClass *card_class) { GObjectClass *object_class; GParamSpec *param_spec; object_class = G_OBJECT_CLASS (card_class); object_class->set_property = gsd_smartcard_set_property; object_class->get_property = gsd_smartcard_get_property; param_spec = g_param_spec_ulong ("slot-id", "Slot ID", "The slot the card is in", 1, G_MAXULONG, GSD_SMARTCARD_DEFAULT_SLOT_ID, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_SLOT_ID, param_spec); param_spec = g_param_spec_int ("slot-series", "Slot Series", "per-slot card identifier", -1, G_MAXINT, GSD_SMARTCARD_DEFAULT_SLOT_SERIES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_SLOT_SERIES, param_spec); param_spec = g_param_spec_string ("name", "name", "name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_NAME, param_spec); param_spec = g_param_spec_pointer ("module", "Module", "smartcard driver", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_MODULE, param_spec); } static void gsd_smartcard_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GsdSmartcard *card = GSD_SMARTCARD (object); switch (prop_id) { case PROP_NAME: gsd_smartcard_set_name (card, g_value_get_string (value)); break; case PROP_SLOT_ID: gsd_smartcard_set_slot_id (card, g_value_get_ulong (value)); break; case PROP_SLOT_SERIES: gsd_smartcard_set_slot_series (card, g_value_get_int (value)); break; case PROP_MODULE: gsd_smartcard_set_module (card, (SECMODModule *) g_value_get_pointer (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } CK_SLOT_ID gsd_smartcard_get_slot_id (GsdSmartcard *card) { return card->priv->slot_id; } GsdSmartcardState gsd_smartcard_get_state (GsdSmartcard *card) { return card->priv->state; } char * gsd_smartcard_get_name (GsdSmartcard *card) { return g_strdup (card->priv->name); } gboolean gsd_smartcard_is_login_card (GsdSmartcard *card) { const char *login_card_name; login_card_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME"); if ((login_card_name == NULL) || (card->priv->name == NULL)) { return FALSE; } if (strcmp (card->priv->name, login_card_name) == 0) { return TRUE; } return FALSE; } static void gsd_smartcard_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsdSmartcard *card = GSD_SMARTCARD (object); switch (prop_id) { case PROP_NAME: g_value_take_string (value, gsd_smartcard_get_name (card)); break; case PROP_SLOT_ID: g_value_set_ulong (value, (gulong) gsd_smartcard_get_slot_id (card)); break; case PROP_SLOT_SERIES: g_value_set_int (value, gsd_smartcard_get_slot_series (card)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gsd_smartcard_set_name (GsdSmartcard *card, const char *name) { if (name == NULL) { return; } if ((card->priv->name == NULL) || (strcmp (card->priv->name, name) != 0)) { g_free (card->priv->name); card->priv->name = g_strdup (name); if (card->priv->slot == NULL) { card->priv->slot = gsd_smartcard_find_slot_from_card_name (card, card->priv->name); if (card->priv->slot != NULL) { int slot_id, slot_series; slot_id = PK11_GetSlotID (card->priv->slot); if (slot_id != card->priv->slot_id) { gsd_smartcard_set_slot_id (card, slot_id); } slot_series = PK11_GetSlotSeries (card->priv->slot); if (slot_series != card->priv->slot_series) { gsd_smartcard_set_slot_series (card, slot_series); } _gsd_smartcard_set_state (card, GSD_SMARTCARD_STATE_INSERTED); } else { _gsd_smartcard_set_state (card, GSD_SMARTCARD_STATE_REMOVED); } } g_object_notify (G_OBJECT (card), "name"); } } static void gsd_smartcard_set_slot_id (GsdSmartcard *card, int slot_id) { if (card->priv->slot_id != slot_id) { card->priv->slot_id = slot_id; if (card->priv->slot == NULL) { card->priv->slot = gsd_smartcard_find_slot_from_id (card, card->priv->slot_id); if (card->priv->slot != NULL) { const char *card_name; card_name = PK11_GetTokenName (card->priv->slot); if ((card->priv->name == NULL) || ((card_name != NULL) && (strcmp (card_name, card->priv->name) != 0))) { gsd_smartcard_set_name (card, card_name); } _gsd_smartcard_set_state (card, GSD_SMARTCARD_STATE_INSERTED); } else { _gsd_smartcard_set_state (card, GSD_SMARTCARD_STATE_REMOVED); } } g_object_notify (G_OBJECT (card), "slot-id"); } } static void gsd_smartcard_set_slot_series (GsdSmartcard *card, int slot_series) { if (card->priv->slot_series != slot_series) { card->priv->slot_series = slot_series; g_object_notify (G_OBJECT (card), "slot-series"); } } static void gsd_smartcard_set_module (GsdSmartcard *card, SECMODModule *module) { gboolean should_notify; if (card->priv->module != module) { should_notify = TRUE; } else { should_notify = FALSE; } if (card->priv->module != NULL) { SECMOD_DestroyModule (card->priv->module); card->priv->module = NULL; } if (module != NULL) { card->priv->module = SECMOD_ReferenceModule (module); } if (should_notify) { g_object_notify (G_OBJECT (card), "module"); } } int gsd_smartcard_get_slot_series (GsdSmartcard *card) { return card->priv->slot_series; } static void gsd_smartcard_init (GsdSmartcard *card) { g_debug ("initializing smartcard "); card->priv = G_TYPE_INSTANCE_GET_PRIVATE (card, GSD_TYPE_SMARTCARD, GsdSmartcardPrivate); } static void gsd_smartcard_finalize (GObject *object) { GsdSmartcard *card; GObjectClass *gobject_class; card = GSD_SMARTCARD (object); g_free (card->priv->name); gsd_smartcard_set_module (card, NULL); gobject_class = G_OBJECT_CLASS (gsd_smartcard_parent_class); gobject_class->finalize (object); } GQuark gsd_smartcard_error_quark (void) { static GQuark error_quark = 0; if (error_quark == 0) { error_quark = g_quark_from_static_string ("gsd-smartcard-error-quark"); } return error_quark; } GsdSmartcard * _gsd_smartcard_new (SECMODModule *module, CK_SLOT_ID slot_id, int slot_series) { GsdSmartcard *card; g_return_val_if_fail (module != NULL, NULL); g_return_val_if_fail (slot_id >= 1, NULL); g_return_val_if_fail (slot_series > 0, NULL); g_return_val_if_fail (sizeof (gulong) == sizeof (slot_id), NULL); card = GSD_SMARTCARD (g_object_new (GSD_TYPE_SMARTCARD, "module", module, "slot-id", (gulong) slot_id, "slot-series", slot_series, NULL)); return card; } GsdSmartcard * _gsd_smartcard_new_from_name (SECMODModule *module, const char *name) { GsdSmartcard *card; g_return_val_if_fail (module != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); card = GSD_SMARTCARD (g_object_new (GSD_TYPE_SMARTCARD, "module", module, "name", name, NULL)); return card; } void _gsd_smartcard_set_state (GsdSmartcard *card, GsdSmartcardState state) { /* gsd_smartcard_fetch_certificates (card); */ if (card->priv->state != state) { card->priv->state = state; if (state == GSD_SMARTCARD_STATE_INSERTED) { g_signal_emit (card, gsd_smartcard_signals[INSERTED], 0); } else if (state == GSD_SMARTCARD_STATE_REMOVED) { g_signal_emit (card, gsd_smartcard_signals[REMOVED], 0); } else { g_assert_not_reached (); } } } /* So we could conceivably make the closure data a pointer to the card * or something similiar and then emit signals when we want passwords, * but it's probably easier to just get the password up front and use * it. So we just take the passed in g_malloc'd (well probably, who knows) * and strdup it using NSPR's memory allocation routines. */ static char * gsd_smartcard_password_handler (PK11SlotInfo *slot, PRBool is_retrying, const char *password) { if (is_retrying) { return NULL; } return password != NULL? PL_strdup (password): NULL; } gboolean gsd_smartcard_unlock (GsdSmartcard *card, const char *password) { SECStatus status; PK11_SetPasswordFunc ((PK11PasswordFunc) gsd_smartcard_password_handler); /* we pass PR_TRUE to load certificates */ status = PK11_Authenticate (card->priv->slot, PR_TRUE, (gpointer) password); if (status != SECSuccess) { g_debug ("could not unlock card - %d", status); return FALSE; } return TRUE; } static PK11SlotInfo * gsd_smartcard_find_slot_from_card_name (GsdSmartcard *card, const char *card_name) { int i; for (i = 0; i < card->priv->module->slotCount; i++) { const char *slot_card_name; slot_card_name = PK11_GetTokenName (card->priv->module->slots[i]); if ((slot_card_name != NULL) && (strcmp (slot_card_name, card_name) == 0)) { return card->priv->module->slots[i]; } } return NULL; } static PK11SlotInfo * gsd_smartcard_find_slot_from_id (GsdSmartcard *card, int slot_id) { int i; for (i = 0; i < card->priv->module->slotCount; i++) { if (PK11_GetSlotID (card->priv->module->slots[i]) == slot_id) { return card->priv->module->slots[i]; } } return NULL; } ./plugins/power/0000755000004100000410000000000012735467763014104 5ustar www-datawww-data./plugins/power/gsd-power-constants.h0000644000004100000410000000330512735467744020176 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2013 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 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. * */ /* The blank delay when the screensaver is active */ #define SCREENSAVER_TIMEOUT_BLANK 15 /* seconds */ /* The dim delay when dimming on idle is requested but idle-delay * is set to "Never" */ #define IDLE_DIM_BLANK_DISABLED_MIN 60 /* seconds */ /* Which fraction of the idle-delay is the idle-dim delay */ #define IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER 4.0/5.0 /* The dim delay under which we do not bother dimming */ #define MINIMUM_IDLE_DIM_DELAY 10 /* seconds */ /* The amount of time we'll undim if the machine is idle when plugged in */ #define POWER_UP_TIME_ON_AC 15 /* seconds */ /* Default brightness values for the mock backlight used in the test suite */ #define GSD_MOCK_DEFAULT_BRIGHTNESS 50 #define GSD_MOCK_MAX_BRIGHTNESS 100 ./plugins/power/gsd-power-constants-update.pl0000755000004100000410000000254212735467744021647 0ustar www-datawww-data#!/usr/bin/env perl # Author : Simos Xenitellis . # Author : Bastien Nocera # Version : 1.2 # # Input : gsd-power-constants.h # Output : gsdpowerconstants.py # use strict; # Used for reading the keysymdef symbols. my @constantselements; die "Could not open file gsd-power-constants.h: $!\n" unless open(IN_CONSTANTS, "<:utf8", "gsd-power-constants.h"); # Output: gtk+/gdk/gdkkeysyms.h die "Could not open file gsdpowerconstants.py: $!\n" unless open(OUT_CONSTANTS, ">:utf8", "gsdpowerconstants.py"); print OUT_CONSTANTS<) { next if ( ! /^#define / ); @constantselements = split(/\s+/); die "Internal error, no \@constantselements: $_\n" unless @constantselements; my $constant = $constantselements[1]; my $value = $constantselements[2]; printf OUT_CONSTANTS "%s = %s;\n", $constant, $value; } close IN_CONSTANTS; printf "We just finished converting gsd-power-constants.h to gsdpowerconstants.py\nThank you\n"; ./plugins/power/gsd-power-manager.h0000644000004100000410000000467212735467744017604 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_POWER_MANAGER_H #define __GSD_POWER_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_POWER_MANAGER (gsd_power_manager_get_type ()) #define GSD_POWER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_POWER_MANAGER, GsdPowerManager)) #define GSD_POWER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_POWER_MANAGER, GsdPowerManagerClass)) #define GSD_IS_POWER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_POWER_MANAGER)) #define GSD_IS_POWER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_POWER_MANAGER)) #define GSD_POWER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_POWER_MANAGER, GsdPowerManagerClass)) #define GSD_POWER_MANAGER_ERROR (gsd_power_manager_error_quark ()) typedef struct GsdPowerManagerPrivate GsdPowerManagerPrivate; typedef struct { GObject parent; GsdPowerManagerPrivate *priv; } GsdPowerManager; typedef struct { GObjectClass parent_class; } GsdPowerManagerClass; enum { GSD_POWER_MANAGER_ERROR_FAILED }; GType gsd_power_manager_get_type (void); GQuark gsd_power_manager_error_quark (void); GsdPowerManager * gsd_power_manager_new (void); gboolean gsd_power_manager_start (GsdPowerManager *manager, GError **error); void gsd_power_manager_stop (GsdPowerManager *manager); G_END_DECLS #endif /* __GSD_POWER_MANAGER_H */ ./plugins/power/gpm-common.h0000644000004100000410000000771112735467744016333 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2005-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GPMCOMMON_H #define __GPMCOMMON_H #include #include #include "gsd-rr.h" G_BEGIN_DECLS /* UPower helpers */ gchar *gpm_get_timestring (guint time); const gchar *gpm_device_to_localised_string (UpDevice *device); const gchar *gpm_device_kind_to_localised_string (UpDeviceKind kind, guint number); const gchar *gpm_device_kind_to_icon (UpDeviceKind kind); const gchar *gpm_device_technology_to_localised_string (UpDeviceTechnology technology_enum); const gchar *gpm_device_state_to_localised_string (UpDeviceState state); GIcon *gpm_upower_get_device_icon (UpDevice *device, gboolean use_symbolic); gchar *gpm_upower_get_device_summary (UpDevice *device); gchar *gpm_upower_get_device_description (UpDevice *device); /* Power helpers */ gboolean gsd_power_is_hardware_a_vm (void); guint gsd_power_enable_screensaver_watchdog (void); void reset_idletime (void); /* Backlight helpers */ /* on ACPI machines we have 4-16 levels, on others it's ~150 */ #define BRIGHTNESS_STEP_AMOUNT(max) ((max) < 20 ? 1 : (max) / 20) #define ABS_TO_PERCENTAGE(min, max, value) gsd_power_backlight_abs_to_percentage(min, max, value) #define PERCENTAGE_TO_ABS(min, max, value) (min + (((max - min) * value) / 100)) int gsd_power_backlight_abs_to_percentage (int min, int max, int value); gboolean backlight_available (GsdRRScreen *rr_screen); int backlight_get_abs (GsdRRScreen *rr_screen, GError **error); int backlight_get_percentage (GsdRRScreen *rr_screen, GError **error); int backlight_get_min (GsdRRScreen *rr_screen); int backlight_get_max (GsdRRScreen *rr_screen, GError **error); gboolean backlight_set_percentage (GsdRRScreen *rr_screen, guint value, GError **error); int backlight_step_up (GsdRRScreen *rr_screen, GError **error); int backlight_step_down (GsdRRScreen *rr_screen, GError **error); int backlight_set_abs (GsdRRScreen *rr_screen, guint value, GError **error); /* RandR helpers */ gboolean external_monitor_is_connected (GsdRRScreen *screen); /* Sound helpers */ void play_loop_start (guint *id); void play_loop_stop (guint *id); G_END_DECLS #endif /* __GPMCOMMON_H */ ./plugins/power/gpm-common.c0000644000004100000410000020245212735467744016325 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2005-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "gpm-common.h" #include "gsd-power-constants.h" #include "gsd-power-manager.h" #include "gsd-backlight-linux.h" #include "gsd-rr.h" #define XSCREENSAVER_WATCHDOG_TIMEOUT 120 /* seconds */ #define UPS_SOUND_LOOP_ID 99 #define GSD_POWER_MANAGER_CRITICAL_ALERT_TIMEOUT 5 /* seconds */ /* take a discrete value with offset and convert to percentage */ int gsd_power_backlight_abs_to_percentage (int min, int max, int value) { g_return_val_if_fail (max > min, -1); g_return_val_if_fail (value >= min, -1); g_return_val_if_fail (value <= max, -1); return (((value - min) * 100) / (max - min)); } #define GPM_UP_TIME_PRECISION 5*60 #define GPM_UP_TEXT_MIN_TIME 120 /** * Return value: The time string, e.g. "2 hours 3 minutes" **/ gchar * gpm_get_timestring (guint time_secs) { char* timestring = NULL; gint hours; gint minutes; /* Add 0.5 to do rounding */ minutes = (int) ( ( time_secs / 60.0 ) + 0.5 ); if (minutes == 0) { timestring = g_strdup (_("Unknown time")); return timestring; } if (minutes < 60) { timestring = g_strdup_printf (ngettext ("%i minute", "%i minutes", minutes), minutes); return timestring; } hours = minutes / 60; minutes = minutes % 60; if (minutes == 0) timestring = g_strdup_printf (ngettext ( "%i hour", "%i hours", hours), hours); else /* TRANSLATOR: "%i %s %i %s" are "%i hours %i minutes" * Swap order with "%2$s %2$i %1$s %1$i if needed */ timestring = g_strdup_printf (_("%i %s %i %s"), hours, ngettext ("hour", "hours", hours), minutes, ngettext ("minute", "minutes", minutes)); return timestring; } static const gchar * gpm_upower_get_device_icon_index (UpDevice *device) { gdouble percentage; /* get device properties */ g_object_get (device, "percentage", &percentage, NULL); if (percentage < 10) return "000"; else if (percentage < 30) return "020"; else if (percentage < 50) return "040"; else if (percentage < 70) return "060"; else if (percentage < 90) return "080"; return "100"; } static const gchar * gpm_upower_get_device_icon_suffix (UpDevice *device) { gdouble percentage; /* get device properties */ g_object_get (device, "percentage", &percentage, NULL); if (percentage < 10) return "caution"; else if (percentage < 30) return "low"; else if (percentage < 60) return "good"; return "full"; } GIcon * gpm_upower_get_device_icon (UpDevice *device, gboolean use_symbolic) { GString *filename; gchar **iconnames; const gchar *kind_str; const gchar *suffix_str; const gchar *index_str; UpDeviceKind kind; UpDeviceState state; gboolean is_present; gdouble percentage; GIcon *icon = NULL; g_return_val_if_fail (device != NULL, NULL); /* get device properties */ g_object_get (device, "kind", &kind, "state", &state, "percentage", &percentage, "is-present", &is_present, NULL); /* get correct icon prefix */ filename = g_string_new (NULL); /* get the icon from some simple rules */ if (kind == UP_DEVICE_KIND_LINE_POWER) { if (use_symbolic) g_string_append (filename, "ac-adapter-symbolic;"); g_string_append (filename, "ac-adapter;"); } else if (kind == UP_DEVICE_KIND_MONITOR) { if (use_symbolic) g_string_append (filename, "gpm-monitor-symbolic;"); g_string_append (filename, "gpm-monitor;"); } else { kind_str = up_device_kind_to_string (kind); if (!is_present) { if (use_symbolic) g_string_append (filename, "battery-missing-symbolic;"); g_string_append_printf (filename, "gpm-%s-missing;", kind_str); g_string_append_printf (filename, "gpm-%s-000;", kind_str); g_string_append (filename, "battery-missing;"); } else { switch (state) { case UP_DEVICE_STATE_EMPTY: if (use_symbolic) g_string_append (filename, "battery-empty-symbolic;"); g_string_append_printf (filename, "gpm-%s-empty;", kind_str); g_string_append_printf (filename, "gpm-%s-000;", kind_str); g_string_append (filename, "battery-empty;"); break; case UP_DEVICE_STATE_FULLY_CHARGED: if (use_symbolic) { g_string_append (filename, "battery-full-charged-symbolic;"); g_string_append (filename, "battery-full-charging-symbolic;"); } g_string_append_printf (filename, "gpm-%s-full;", kind_str); g_string_append_printf (filename, "gpm-%s-100;", kind_str); g_string_append (filename, "battery-full-charged;"); g_string_append (filename, "battery-full-charging;"); break; case UP_DEVICE_STATE_CHARGING: case UP_DEVICE_STATE_PENDING_CHARGE: suffix_str = gpm_upower_get_device_icon_suffix (device); index_str = gpm_upower_get_device_icon_index (device); if (use_symbolic) g_string_append_printf (filename, "battery-%s-charging-symbolic;", suffix_str); g_string_append_printf (filename, "gpm-%s-%s-charging;", kind_str, index_str); g_string_append_printf (filename, "battery-%s-charging;", suffix_str); break; case UP_DEVICE_STATE_DISCHARGING: case UP_DEVICE_STATE_PENDING_DISCHARGE: suffix_str = gpm_upower_get_device_icon_suffix (device); index_str = gpm_upower_get_device_icon_index (device); if (use_symbolic) g_string_append_printf (filename, "battery-%s-symbolic;", suffix_str); g_string_append_printf (filename, "gpm-%s-%s;", kind_str, index_str); g_string_append_printf (filename, "battery-%s;", suffix_str); break; default: if (use_symbolic) g_string_append (filename, "battery-missing-symbolic;"); g_string_append (filename, "gpm-battery-missing;"); g_string_append (filename, "battery-missing;"); } } } /* nothing matched */ if (filename->len == 0) { g_warning ("nothing matched, falling back to default icon"); g_string_append (filename, "dialog-warning;"); } g_debug ("got filename: %s", filename->str); iconnames = g_strsplit (filename->str, ";", -1); icon = g_themed_icon_new_from_names (iconnames, -1); g_strfreev (iconnames); g_string_free (filename, TRUE); return icon; } /** * gpm_precision_round_down: * @value: The input value * @smallest: The smallest increment allowed * * 101, 10 100 * 95, 10 90 * 0, 10 0 * 112, 10 110 * 100, 10 100 **/ static gint gpm_precision_round_down (gfloat value, gint smallest) { gfloat division; if (fabs (value) < 0.01) return 0; if (smallest == 0) { g_warning ("divisor zero"); return 0; } division = (gfloat) value / (gfloat) smallest; division = floorf (division); division *= smallest; return (gint) division; } gchar * gpm_upower_get_device_summary (UpDevice *device) { const gchar *kind_desc = NULL; const gchar *device_desc = NULL; GString *description; guint time_to_full_round; guint time_to_empty_round; gchar *time_to_full_str = NULL; gchar *time_to_empty_str = NULL; UpDeviceKind kind; UpDeviceState state; gdouble percentage; gboolean is_present; gint64 time_to_full; gint64 time_to_empty; /* get device properties */ g_object_get (device, "kind", &kind, "state", &state, "percentage", &percentage, "is-present", &is_present, "time-to-full", &time_to_full, "time-to-empty", &time_to_empty, NULL); description = g_string_new (NULL); kind_desc = gpm_device_kind_to_localised_string (kind, 1); device_desc = gpm_device_to_localised_string (device); /* not installed */ if (!is_present) { g_string_append (description, device_desc); goto out; } /* don't display all the extra stuff for keyboards and mice */ if (kind == UP_DEVICE_KIND_MOUSE || kind == UP_DEVICE_KIND_KEYBOARD || kind == UP_DEVICE_KIND_PDA) { g_string_append (description, kind_desc); g_string_append_printf (description, " (%.0f%%)", percentage); goto out; } /* we care if we are on AC */ if (kind == UP_DEVICE_KIND_PHONE) { if (state == UP_DEVICE_STATE_CHARGING || !(state == UP_DEVICE_STATE_DISCHARGING)) { g_string_append (description, device_desc); g_string_append_printf (description, " (%.0f%%)", percentage); goto out; } g_string_append (description, kind_desc); g_string_append_printf (description, " (%.0f%%)", percentage); goto out; } /* precalculate so we don't get Unknown time remaining */ time_to_full_round = gpm_precision_round_down (time_to_full, GPM_UP_TIME_PRECISION); time_to_empty_round = gpm_precision_round_down (time_to_empty, GPM_UP_TIME_PRECISION); /* we always display "Laptop battery 16 minutes remaining" as we need to clarify what device we are refering to */ if (state == UP_DEVICE_STATE_FULLY_CHARGED) { g_string_append (description, device_desc); if (kind == UP_DEVICE_KIND_BATTERY && time_to_empty_round > GPM_UP_TEXT_MIN_TIME) { time_to_empty_str = gpm_get_timestring (time_to_empty_round); g_string_append (description, " - "); /* TRANSLATORS: The laptop battery is charged, and we know a time. * The parameter is the time, e.g. 7 hours 6 minutes */ g_string_append_printf (description, _("provides %s laptop runtime"), time_to_empty_str); } goto out; } if (state == UP_DEVICE_STATE_DISCHARGING) { if (time_to_empty_round > GPM_UP_TEXT_MIN_TIME) { time_to_empty_str = gpm_get_timestring (time_to_empty_round); /* TRANSLATORS: the device is discharging, and we have a time remaining * The first parameter is the device type, e.g. "Laptop battery" and * the second is the time, e.g. 7 hours 6 minutes */ g_string_append_printf (description, _("%s %s remaining"), kind_desc, time_to_empty_str); g_string_append_printf (description, " (%.0f%%)", percentage); } else { g_string_append (description, device_desc); g_string_append_printf (description, " (%.0f%%)", percentage); } goto out; } if (state == UP_DEVICE_STATE_CHARGING) { if (time_to_full_round > GPM_UP_TEXT_MIN_TIME && time_to_empty_round > GPM_UP_TEXT_MIN_TIME) { /* display both discharge and charge time */ time_to_full_str = gpm_get_timestring (time_to_full_round); time_to_empty_str = gpm_get_timestring (time_to_empty_round); /* TRANSLATORS: device is charging, and we have a time to full and a percentage * The first parameter is the device type, e.g. "Laptop battery" and * the second is the time, e.g. "7 hours 6 minutes" */ g_string_append_printf (description, _("%s %s until charged"), kind_desc, time_to_full_str); g_string_append_printf (description, " (%.0f%%)", percentage); g_string_append (description, " - "); /* TRANSLATORS: the device is charging, and we have a time to full and empty. * The parameter is a time string, e.g. "7 hours 6 minutes" */ g_string_append_printf (description, _("provides %s battery runtime"), time_to_empty_str); } else if (time_to_full_round > GPM_UP_TEXT_MIN_TIME) { /* display only charge time */ time_to_full_str = gpm_get_timestring (time_to_full_round); /* TRANSLATORS: device is charging, and we have a time to full and a percentage. * The first parameter is the device type, e.g. "Laptop battery" and * the second is the time, e.g. "7 hours 6 minutes" */ g_string_append_printf (description, _("%s %s until charged"), kind_desc, time_to_full_str); g_string_append_printf (description, " (%.0f%%)", percentage); } else { g_string_append (description, device_desc); g_string_append_printf (description, " (%.0f%%)", percentage); } goto out; } if (state == UP_DEVICE_STATE_PENDING_DISCHARGE) { g_string_append (description, device_desc); g_string_append_printf (description, " (%.0f%%)", percentage); goto out; } if (state == UP_DEVICE_STATE_PENDING_CHARGE) { g_string_append (description, device_desc); g_string_append_printf (description, " (%.0f%%)", percentage); goto out; } if (state == UP_DEVICE_STATE_EMPTY) { g_string_append (description, device_desc); goto out; } /* fallback */ g_warning ("in an undefined state we are not charging or " "discharging and the batteries are also not charged"); g_string_append (description, device_desc); g_string_append_printf (description, " (%.0f%%)", percentage); out: g_free (time_to_full_str); g_free (time_to_empty_str); return g_string_free (description, FALSE); } gchar * gpm_upower_get_device_description (UpDevice *device) { GString *details; const gchar *text; gchar *time_str; UpDeviceKind kind; UpDeviceState state; UpDeviceTechnology technology; gdouble percentage; gdouble capacity; gdouble energy; gdouble energy_full; gdouble energy_full_design; gdouble energy_rate; gboolean is_present; gint64 time_to_full; gint64 time_to_empty; gchar *vendor = NULL; gchar *serial = NULL; gchar *model = NULL; g_return_val_if_fail (device != NULL, NULL); /* get device properties */ g_object_get (device, "kind", &kind, "state", &state, "percentage", &percentage, "is-present", &is_present, "time-to-full", &time_to_full, "time-to-empty", &time_to_empty, "technology", &technology, "capacity", &capacity, "energy", &energy, "energy-full", &energy_full, "energy-full-design", &energy_full_design, "energy-rate", &energy_rate, "vendor", &vendor, "serial", &serial, "model", &model, NULL); details = g_string_new (""); text = gpm_device_kind_to_localised_string (kind, 1); /* TRANSLATORS: the type of data, e.g. Laptop battery */ g_string_append_printf (details, "%s %s\n", _("Product:"), text); if (!is_present) { /* TRANSLATORS: device is missing */ g_string_append_printf (details, "%s %s\n", _("Status:"), _("Missing")); } else if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: device is charged */ g_string_append_printf (details, "%s %s\n", _("Status:"), _("Charged")); } else if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: device is charging */ g_string_append_printf (details, "%s %s\n", _("Status:"), _("Charging")); } else if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: device is discharging */ g_string_append_printf (details, "%s %s\n", _("Status:"), _("Discharging")); } if (percentage >= 0) { /* TRANSLATORS: percentage */ g_string_append_printf (details, "%s %.1f%%\n", _("Percentage charge:"), percentage); } if (vendor) { /* TRANSLATORS: manufacturer */ g_string_append_printf (details, "%s %s\n", _("Vendor:"), vendor); } if (technology != UP_DEVICE_TECHNOLOGY_UNKNOWN) { text = gpm_device_technology_to_localised_string (technology); /* TRANSLATORS: how the battery is made, e.g. Lithium Ion */ g_string_append_printf (details, "%s %s\n", _("Technology:"), text); } if (serial) { /* TRANSLATORS: serial number of the battery */ g_string_append_printf (details, "%s %s\n", _("Serial number:"), serial); } if (model) { /* TRANSLATORS: model number of the battery */ g_string_append_printf (details, "%s %s\n", _("Model:"), model); } if (time_to_full > 0) { time_str = gpm_get_timestring (time_to_full); /* TRANSLATORS: time to fully charged */ g_string_append_printf (details, "%s %s\n", _("Charge time:"), time_str); g_free (time_str); } if (time_to_empty > 0) { time_str = gpm_get_timestring (time_to_empty); /* TRANSLATORS: time to empty */ g_string_append_printf (details, "%s %s\n", _("Discharge time:"), time_str); g_free (time_str); } if (capacity > 0) { const gchar *condition; if (capacity > 99) { /* TRANSLATORS: Excellent, Good, Fair and Poor are all related to battery Capacity */ condition = _("Excellent"); } else if (capacity > 90) { condition = _("Good"); } else if (capacity > 70) { condition = _("Fair"); } else { condition = _("Poor"); } /* TRANSLATORS: %.1f is a percentage and %s the condition (Excellent, Good, ...) */ g_string_append_printf (details, "%s %.1f%% (%s)\n", _("Capacity:"), capacity, condition); } if (kind == UP_DEVICE_KIND_BATTERY) { if (energy > 0) { /* TRANSLATORS: current charge */ g_string_append_printf (details, "%s %.1f Wh\n", _("Current charge:"), energy); } if (energy_full > 0 && energy_full_design != energy_full) { /* TRANSLATORS: last full is the charge the battery was seen to charge to */ g_string_append_printf (details, "%s %.1f Wh\n", _("Last full charge:"), energy_full); } if (energy_full_design > 0) { /* Translators: */ /* TRANSLATORS: Design charge is the amount of charge the battery is designed to have when brand new */ g_string_append_printf (details, "%s %.1f Wh\n", _("Design charge:"), energy_full_design); } if (energy_rate > 0) { /* TRANSLATORS: the charge or discharge rate */ g_string_append_printf (details, "%s %.1f W\n", _("Charge rate:"), energy_rate); } } if (kind == UP_DEVICE_KIND_MOUSE || kind == UP_DEVICE_KIND_KEYBOARD) { if (energy > 0) { /* TRANSLATORS: the current charge for CSR devices */ g_string_append_printf (details, "%s %.0f/7\n", _("Current charge:"), energy); } if (energy_full_design > 0) { /* TRANSLATORS: the design charge for CSR devices */ g_string_append_printf (details, "%s %.0f/7\n", _("Design charge:"), energy_full_design); } } /* remove the last \n */ g_string_truncate (details, details->len-1); g_free (vendor); g_free (serial); g_free (model); return g_string_free (details, FALSE); } const gchar * gpm_device_kind_to_localised_string (UpDeviceKind kind, guint number) { const gchar *text = NULL; switch (kind) { case UP_DEVICE_KIND_LINE_POWER: /* TRANSLATORS: system power cord */ text = ngettext ("AC adapter", "AC adapters", number); break; case UP_DEVICE_KIND_BATTERY: /* TRANSLATORS: laptop primary battery */ text = ngettext ("Laptop battery", "Laptop batteries", number); break; case UP_DEVICE_KIND_UPS: /* TRANSLATORS: battery-backed AC power source */ text = ngettext ("UPS", "UPSs", number); break; case UP_DEVICE_KIND_MONITOR: /* TRANSLATORS: a monitor is a device to measure voltage and current */ text = ngettext ("Monitor", "Monitors", number); break; case UP_DEVICE_KIND_MOUSE: /* TRANSLATORS: wireless mice with internal batteries */ text = ngettext ("Mouse", "Mice", number); break; case UP_DEVICE_KIND_KEYBOARD: /* TRANSLATORS: wireless keyboard with internal battery */ text = ngettext ("Keyboard", "Keyboards", number); break; case UP_DEVICE_KIND_PDA: /* TRANSLATORS: portable device */ text = ngettext ("PDA", "PDAs", number); break; case UP_DEVICE_KIND_PHONE: /* TRANSLATORS: cell phone (mobile...) */ text = ngettext ("Cell phone", "Cell phones", number); break; #if UP_CHECK_VERSION(0,9,5) case UP_DEVICE_KIND_MEDIA_PLAYER: /* TRANSLATORS: media player, mp3 etc */ text = ngettext ("Media player", "Media players", number); break; case UP_DEVICE_KIND_TABLET: /* TRANSLATORS: tablet device */ text = ngettext ("Tablet", "Tablets", number); break; case UP_DEVICE_KIND_COMPUTER: /* TRANSLATORS: tablet device */ text = ngettext ("Computer", "Computers", number); break; #endif default: g_warning ("enum unrecognised: %i", kind); text = up_device_kind_to_string (kind); } return text; } const gchar * gpm_device_kind_to_icon (UpDeviceKind kind) { const gchar *icon = NULL; switch (kind) { case UP_DEVICE_KIND_LINE_POWER: icon = "ac-adapter"; break; case UP_DEVICE_KIND_BATTERY: icon = "battery"; break; case UP_DEVICE_KIND_UPS: icon = "network-wired"; break; case UP_DEVICE_KIND_MONITOR: icon = "application-certificate"; break; case UP_DEVICE_KIND_MOUSE: icon = "input-mouse"; break; case UP_DEVICE_KIND_KEYBOARD: icon = "input-keyboard"; break; case UP_DEVICE_KIND_PDA: icon = "pda"; break; case UP_DEVICE_KIND_PHONE: icon = "phone"; break; #if UP_CHECK_VERSION(0,9,5) case UP_DEVICE_KIND_MEDIA_PLAYER: icon = "multimedia-player"; break; case UP_DEVICE_KIND_TABLET: icon = "input-tablet"; break; case UP_DEVICE_KIND_COMPUTER: icon = "computer-apple-ipad"; break; #endif default: g_warning ("enum unrecognised: %i", kind); icon = "gtk-help"; } return icon; } const gchar * gpm_device_technology_to_localised_string (UpDeviceTechnology technology_enum) { const gchar *technology = NULL; switch (technology_enum) { case UP_DEVICE_TECHNOLOGY_LITHIUM_ION: /* TRANSLATORS: battery technology */ technology = _("Lithium Ion"); break; case UP_DEVICE_TECHNOLOGY_LITHIUM_POLYMER: /* TRANSLATORS: battery technology */ technology = _("Lithium Polymer"); break; case UP_DEVICE_TECHNOLOGY_LITHIUM_IRON_PHOSPHATE: /* TRANSLATORS: battery technology */ technology = _("Lithium Iron Phosphate"); break; case UP_DEVICE_TECHNOLOGY_LEAD_ACID: /* TRANSLATORS: battery technology */ technology = _("Lead acid"); break; case UP_DEVICE_TECHNOLOGY_NICKEL_CADMIUM: /* TRANSLATORS: battery technology */ technology = _("Nickel Cadmium"); break; case UP_DEVICE_TECHNOLOGY_NICKEL_METAL_HYDRIDE: /* TRANSLATORS: battery technology */ technology = _("Nickel metal hydride"); break; case UP_DEVICE_TECHNOLOGY_UNKNOWN: /* TRANSLATORS: battery technology */ technology = _("Unknown technology"); break; default: g_assert_not_reached (); break; } return technology; } const gchar * gpm_device_state_to_localised_string (UpDeviceState state) { const gchar *state_string = NULL; switch (state) { case UP_DEVICE_STATE_CHARGING: /* TRANSLATORS: battery state */ state_string = _("Charging"); break; case UP_DEVICE_STATE_DISCHARGING: /* TRANSLATORS: battery state */ state_string = _("Discharging"); break; case UP_DEVICE_STATE_EMPTY: /* TRANSLATORS: battery state */ state_string = _("Empty"); break; case UP_DEVICE_STATE_FULLY_CHARGED: /* TRANSLATORS: battery state */ state_string = _("Charged"); break; case UP_DEVICE_STATE_PENDING_CHARGE: /* TRANSLATORS: battery state */ state_string = _("Waiting to charge"); break; case UP_DEVICE_STATE_PENDING_DISCHARGE: /* TRANSLATORS: battery state */ state_string = _("Waiting to discharge"); break; default: g_assert_not_reached (); break; } return state_string; } const gchar * gpm_device_to_localised_string (UpDevice *device) { UpDeviceState state; UpDeviceKind kind; gboolean present; /* get device parameters */ g_object_get (device, "is-present", &present, "kind", &kind, "state", &state, NULL); /* laptop battery */ if (kind == UP_DEVICE_KIND_BATTERY) { if (!present) { /* TRANSLATORS: device not present */ return _("Laptop battery not present"); } if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("Laptop battery is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("Laptop battery is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("Laptop battery is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("Laptop battery is charged"); } if (state == UP_DEVICE_STATE_PENDING_CHARGE) { /* TRANSLATORS: battery state */ return _("Laptop battery is waiting to charge"); } if (state == UP_DEVICE_STATE_PENDING_DISCHARGE) { /* TRANSLATORS: battery state */ return _("Laptop battery is waiting to discharge"); } } /* UPS */ if (kind == UP_DEVICE_KIND_UPS) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("UPS is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("UPS is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("UPS is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("UPS is charged"); } } /* mouse */ if (kind == UP_DEVICE_KIND_MOUSE) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("Mouse is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("Mouse is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("Mouse is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("Mouse is charged"); } } /* keyboard */ if (kind == UP_DEVICE_KIND_KEYBOARD) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("Keyboard is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("Keyboard is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("Keyboard is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("Keyboard is charged"); } } /* PDA */ if (kind == UP_DEVICE_KIND_PDA) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("PDA is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("PDA is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("PDA is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("PDA is charged"); } } /* phone */ if (kind == UP_DEVICE_KIND_PHONE) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("Cell phone is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("Cell phone is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("Cell phone is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("Cell phone is charged"); } } #if UP_CHECK_VERSION(0,9,5) /* media player */ if (kind == UP_DEVICE_KIND_MEDIA_PLAYER) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("Media player is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("Media player is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("Media player is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("Media player is charged"); } } /* tablet */ if (kind == UP_DEVICE_KIND_TABLET) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("Tablet is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("Tablet is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("Tablet is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("Tablet is charged"); } } /* computer */ if (kind == UP_DEVICE_KIND_COMPUTER) { if (state == UP_DEVICE_STATE_CHARGING) { /* TRANSLATORS: battery state */ return _("Computer is charging"); } if (state == UP_DEVICE_STATE_DISCHARGING) { /* TRANSLATORS: battery state */ return _("Computer is discharging"); } if (state == UP_DEVICE_STATE_EMPTY) { /* TRANSLATORS: battery state */ return _("Computer is empty"); } if (state == UP_DEVICE_STATE_FULLY_CHARGED) { /* TRANSLATORS: battery state */ return _("Computer is charged"); } } #endif return gpm_device_kind_to_localised_string (kind, 1); } static gboolean parse_vm_kernel_cmdline (gboolean *is_virtual_machine) { gboolean ret = FALSE; GRegex *regex; GMatchInfo *match; char *contents; char *word; const char *arg; if (!g_file_get_contents ("/proc/cmdline", &contents, NULL, NULL)) return ret; regex = g_regex_new ("gnome.is_vm=(\\S+)", 0, G_REGEX_MATCH_NOTEMPTY, NULL); if (!g_regex_match (regex, contents, G_REGEX_MATCH_NOTEMPTY, &match)) goto out; word = g_match_info_fetch (match, 0); g_debug ("Found command-line match '%s'", word); arg = word + strlen ("gnome.is_vm="); if (*arg != '0' && *arg != '1') { g_warning ("Invalid value '%s' for gnome.is_vm passed in kernel command line.\n", arg); } else { *is_virtual_machine = atoi (arg); ret = TRUE; } g_free (word); out: g_match_info_free (match); g_regex_unref (regex); g_free (contents); if (ret) g_debug ("Kernel command-line parsed to %d", *is_virtual_machine); return ret; } gboolean gsd_power_is_hardware_a_vm (void) { const gchar *str; gboolean ret = FALSE; GError *error = NULL; GVariant *inner; GVariant *variant = NULL; GDBusConnection *connection; if (parse_vm_kernel_cmdline (&ret)) return ret; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); if (connection == NULL) { g_warning ("system bus not available: %s", error->message); g_error_free (error); goto out; } variant = g_dbus_connection_call_sync (connection, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.DBus.Properties", "Get", g_variant_new ("(ss)", "org.freedesktop.systemd1.Manager", "Virtualization"), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (variant == NULL) { g_debug ("Failed to get property '%s': %s", "Virtualization", error->message); g_error_free (error); goto out; } /* on bare-metal hardware this is the empty string, * otherwise an identifier such as "kvm", "vmware", etc. */ g_variant_get (variant, "(v)", &inner); str = g_variant_get_string (inner, NULL); if (str != NULL && str[0] != '\0') ret = TRUE; out: if (connection != NULL) g_object_unref (connection); if (variant != NULL) g_variant_unref (variant); return ret; } /* This timer goes off every few minutes, whether the user is idle or not, to try and clean up anything that has gone wrong. It calls disable_builtin_screensaver() so that if xset has been used, or some other program (like xlock) has messed with the XSetScreenSaver() settings, they will be set back to sensible values (if a server extension is in use, messing with xlock can cause the screensaver to never get a wakeup event, and could cause monitor power-saving to occur, and all manner of heinousness.) This code was originally part of gnome-screensaver, see http://git.gnome.org/browse/gnome-screensaver/tree/src/gs-watcher-x11.c?id=fec00b12ec46c86334cfd36b37771cc4632f0d4d#n530 */ static gboolean disable_builtin_screensaver (gpointer unused) { int current_server_timeout, current_server_interval; int current_prefer_blank, current_allow_exp; int desired_server_timeout, desired_server_interval; int desired_prefer_blank, desired_allow_exp; XGetScreenSaver (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), ¤t_server_timeout, ¤t_server_interval, ¤t_prefer_blank, ¤t_allow_exp); desired_server_timeout = current_server_timeout; desired_server_interval = current_server_interval; desired_prefer_blank = current_prefer_blank; desired_allow_exp = current_allow_exp; desired_server_interval = 0; /* I suspect (but am not sure) that DontAllowExposures might have something to do with powering off the monitor as well, at least on some systems that don't support XDPMS? Who know... */ desired_allow_exp = AllowExposures; /* When we're not using an extension, set the server-side timeout to 0, so that the server never gets involved with screen blanking, and we do it all ourselves. (However, when we *are* using an extension, we tell the server when to notify us, and rather than blanking the screen, the server will send us an X event telling us to blank.) */ desired_server_timeout = 0; if (desired_server_timeout != current_server_timeout || desired_server_interval != current_server_interval || desired_prefer_blank != current_prefer_blank || desired_allow_exp != current_allow_exp) { g_debug ("disabling server builtin screensaver:" " (xset s %d %d; xset s %s; xset s %s)", desired_server_timeout, desired_server_interval, (desired_prefer_blank ? "blank" : "noblank"), (desired_allow_exp ? "expose" : "noexpose")); XSetScreenSaver (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), desired_server_timeout, desired_server_interval, desired_prefer_blank, desired_allow_exp); XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); } return TRUE; } guint gsd_power_enable_screensaver_watchdog (void) { int dummy; /* Make sure that Xorg's DPMS extension never gets in our * way. The defaults are now applied in Fedora 20 from * being "0" by default to being "600" by default */ gdk_error_trap_push (); if (DPMSQueryExtension(GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &dummy, &dummy)) DPMSSetTimeouts (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), 0, 0, 0); gdk_error_trap_pop_ignored (); return g_timeout_add_seconds (XSCREENSAVER_WATCHDOG_TIMEOUT, disable_builtin_screensaver, NULL); } static GsdRROutput * get_primary_output (GsdRRScreen *rr_screen) { GsdRROutput *output = NULL; GsdRROutput **outputs; guint i; /* search all X11 outputs for the device id */ outputs = gsd_rr_screen_list_outputs (rr_screen); if (outputs == NULL) goto out; for (i = 0; outputs[i] != NULL; i++) { if (gsd_rr_output_is_connected (outputs[i]) && gsd_rr_output_is_laptop (outputs[i]) && gsd_rr_output_get_backlight_min (outputs[i]) >= 0 && gsd_rr_output_get_backlight_max (outputs[i]) > 0) { output = outputs[i]; break; } } out: return output; } #ifdef GSD_MOCK static void backlight_set_mock_value (gint value) { const char *filename; char *contents; g_debug ("Settings mock brightness: %d", value); filename = "GSD_MOCK_brightness"; contents = g_strdup_printf ("%d", value); g_file_set_contents (filename, contents, -1, NULL); g_free (contents); } static gint64 backlight_get_mock_value (const char *argument) { const char *filename; char *contents; gint64 ret; if (g_str_equal (argument, "get-max-brightness")) { g_debug ("Returning max mock brightness: %d", GSD_MOCK_MAX_BRIGHTNESS); return GSD_MOCK_MAX_BRIGHTNESS; } if (g_str_equal (argument, "get-brightness")) { filename = "GSD_MOCK_brightness"; ret = GSD_MOCK_DEFAULT_BRIGHTNESS; } else { g_assert_not_reached (); } if (g_file_get_contents (filename, &contents, NULL, NULL)) { ret = g_ascii_strtoll (contents, NULL, 0); g_free (contents); g_debug ("Returning mock brightness: %"G_GINT64_FORMAT, ret); } else { ret = GSD_MOCK_DEFAULT_BRIGHTNESS; backlight_set_mock_value (GSD_MOCK_DEFAULT_BRIGHTNESS); g_debug ("Returning default mock brightness: %"G_GINT64_FORMAT, ret); } return ret; } #endif /* GSD_MOCK */ gboolean backlight_available (GsdRRScreen *rr_screen) { char *path; #ifdef GSD_MOCK return TRUE; #endif if (get_primary_output (rr_screen) != NULL) return TRUE; path = gsd_backlight_helper_get_best_backlight (NULL); if (path == NULL) return FALSE; g_free (path); return TRUE; } /** * backlight_helper_get_value: * * Gets a brightness value from the PolicyKit helper. * * Return value: the signed integer value from the helper, or -1 * for failure. If -1 then @error is set. **/ static gint64 backlight_helper_get_value (const gchar *argument, GError **error) { gboolean ret; gchar *stdout_data = NULL; gint exit_status = 0; gint64 value = -1; gchar *command = NULL; gchar *endptr = NULL; #ifdef GSD_MOCK return backlight_get_mock_value (argument); #endif #ifndef __linux__ /* non-Linux platforms won't have /sys/class/backlight */ g_set_error_literal (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "The sysfs backlight helper is only for Linux"); goto out; #endif /* get the data */ command = g_strdup_printf (LIBEXECDIR "/usd-backlight-helper --%s", argument); ret = g_spawn_command_line_sync (command, &stdout_data, NULL, &exit_status, error); g_debug ("executed %s retval: %i", command, exit_status); if (!ret) goto out; if (WEXITSTATUS (exit_status) != 0) { g_set_error (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "usd-backlight-helper failed: %s", stdout_data ? stdout_data : "No reason"); goto out; } /* parse */ value = g_ascii_strtoll (stdout_data, &endptr, 10); /* parsing error */ if (endptr == stdout_data) { value = -1; g_set_error (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "failed to parse value: %s", stdout_data); goto out; } /* out of range */ if (value > G_MAXINT) { value = -1; g_set_error (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "value out of range: %s", stdout_data); goto out; } /* Fetching the value failed, for some other reason */ if (value < 0) { g_set_error (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "value negative, but helper did not fail: %s", stdout_data); goto out; } out: g_free (command); g_free (stdout_data); return value; } /** * backlight_helper_set_value: * * Sets a brightness value using the PolicyKit helper. * * Return value: Success. If FALSE then @error is set. **/ static gboolean backlight_helper_set_value (const gchar *argument, gint value, GError **error) { gboolean ret = FALSE; gint exit_status = 0; gchar *command = NULL; #ifdef GSD_MOCK backlight_set_mock_value (value); return TRUE; #endif #ifndef __linux__ /* non-Linux platforms won't have /sys/class/backlight */ g_set_error_literal (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "The sysfs backlight helper is only for Linux"); goto out; #endif /* get the data */ command = g_strdup_printf ("pkexec " LIBEXECDIR "/usd-backlight-helper --%s %i", argument, value); ret = g_spawn_command_line_sync (command, NULL, NULL, &exit_status, error); g_debug ("executed %s retval: %i", command, exit_status); if (!ret || WEXITSTATUS (exit_status) != 0) goto out; out: g_free (command); return ret; } int backlight_get_abs (GsdRRScreen *rr_screen, GError **error) { GsdRROutput *output; /* prefer xbacklight */ output = get_primary_output (rr_screen); if (output != NULL) { return gsd_rr_output_get_backlight (output, error); } /* fall back to the polkit helper */ return backlight_helper_get_value ("get-brightness", error); } int backlight_get_percentage (GsdRRScreen *rr_screen, GError **error) { GsdRROutput *output; gint now; gint value = -1; gint min = 0; gint max; /* prefer xbacklight */ output = get_primary_output (rr_screen); if (output != NULL) { min = gsd_rr_output_get_backlight_min (output); max = gsd_rr_output_get_backlight_max (output); now = gsd_rr_output_get_backlight (output, error); if (now < 0) goto out; value = ABS_TO_PERCENTAGE (min, max, now); goto out; } /* fall back to the polkit helper */ max = backlight_helper_get_value ("get-max-brightness", error); if (max < 0) goto out; now = backlight_helper_get_value ("get-brightness", error); if (now < 0) goto out; value = ABS_TO_PERCENTAGE (min, max, now); out: return value; } int backlight_get_min (GsdRRScreen *rr_screen) { GsdRROutput *output; /* if we have no xbacklight device, then hardcode zero as sysfs * offsets everything to 0 as min */ output = get_primary_output (rr_screen); if (output == NULL) return 0; /* get xbacklight value, which maybe non-zero */ return gsd_rr_output_get_backlight_min (output); } int backlight_get_max (GsdRRScreen *rr_screen, GError **error) { gint value; GsdRROutput *output; /* prefer xbacklight */ output = get_primary_output (rr_screen); if (output != NULL) { value = gsd_rr_output_get_backlight_max (output); if (value < 0) { g_set_error (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "failed to get backlight max"); } return value; } /* fall back to the polkit helper */ return backlight_helper_get_value ("get-max-brightness", error); } gboolean backlight_set_percentage (GsdRRScreen *rr_screen, guint value, GError **error) { GsdRROutput *output; gboolean ret = FALSE; gint min = 0; gint max; guint discrete; /* prefer xbacklight */ output = get_primary_output (rr_screen); if (output != NULL) { min = gsd_rr_output_get_backlight_min (output); max = gsd_rr_output_get_backlight_max (output); if (min < 0 || max < 0) { g_warning ("no xrandr backlight capability"); return ret; } discrete = PERCENTAGE_TO_ABS (min, max, value); ret = gsd_rr_output_set_backlight (output, discrete, error); return ret; } /* fall back to the polkit helper */ max = backlight_helper_get_value ("get-max-brightness", error); if (max < 0) return ret; discrete = PERCENTAGE_TO_ABS (min, max, value); ret = backlight_helper_set_value ("set-brightness", discrete, error); return ret; } int backlight_step_up (GsdRRScreen *rr_screen, GError **error) { GsdRROutput *output; gboolean ret = FALSE; gint percentage_value = -1; gint min = 0; gint max; gint now; gint step; guint discrete; GsdRRCrtc *crtc; /* prefer xbacklight */ output = get_primary_output (rr_screen); if (output != NULL) { crtc = gsd_rr_output_get_crtc (output); if (crtc == NULL) { g_set_error (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "no crtc for %s", gsd_rr_output_get_name (output)); return percentage_value; } min = gsd_rr_output_get_backlight_min (output); max = gsd_rr_output_get_backlight_max (output); now = gsd_rr_output_get_backlight (output, error); if (now < 0) return percentage_value; step = BRIGHTNESS_STEP_AMOUNT (max - min + 1); discrete = MIN (now + step, max); ret = gsd_rr_output_set_backlight (output, discrete, error); if (ret) percentage_value = ABS_TO_PERCENTAGE (min, max, discrete); return percentage_value; } /* fall back to the polkit helper */ now = backlight_helper_get_value ("get-brightness", error); if (now < 0) return percentage_value; max = backlight_helper_get_value ("get-max-brightness", error); if (max < 0) return percentage_value; step = BRIGHTNESS_STEP_AMOUNT (max - min + 1); discrete = MIN (now + step, max); ret = backlight_helper_set_value ("set-brightness", discrete, error); if (ret) percentage_value = ABS_TO_PERCENTAGE (min, max, discrete); return percentage_value; } int backlight_step_down (GsdRRScreen *rr_screen, GError **error) { GsdRROutput *output; gboolean ret = FALSE; gint percentage_value = -1; gint min = 0; gint max; gint now; gint step; guint discrete; GsdRRCrtc *crtc; /* prefer xbacklight */ output = get_primary_output (rr_screen); if (output != NULL) { crtc = gsd_rr_output_get_crtc (output); if (crtc == NULL) { g_set_error (error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "no crtc for %s", gsd_rr_output_get_name (output)); return percentage_value; } min = gsd_rr_output_get_backlight_min (output); max = gsd_rr_output_get_backlight_max (output); now = gsd_rr_output_get_backlight (output, error); if (now < 0) return percentage_value; step = BRIGHTNESS_STEP_AMOUNT (max - min + 1); discrete = MAX (now - step, 0); ret = gsd_rr_output_set_backlight (output, discrete, error); if (ret) percentage_value = ABS_TO_PERCENTAGE (min, max, discrete); return percentage_value; } /* fall back to the polkit helper */ now = backlight_helper_get_value ("get-brightness", error); if (now < 0) return percentage_value; max = backlight_helper_get_value ("get-max-brightness", error); if (max < 0) return percentage_value; step = BRIGHTNESS_STEP_AMOUNT (max - min + 1); discrete = MAX (now - step, 0); ret = backlight_helper_set_value ("set-brightness", discrete, error); if (ret) percentage_value = ABS_TO_PERCENTAGE (min, max, discrete); return percentage_value; } int backlight_set_abs (GsdRRScreen *rr_screen, guint value, GError **error) { GsdRROutput *output; gboolean ret = FALSE; /* prefer xbacklight */ output = get_primary_output (rr_screen); if (output != NULL) { ret = gsd_rr_output_set_backlight (output, value, error); return ret; } /* fall back to the polkit helper */ ret = backlight_helper_set_value ("set-brightness", value, error); return ret; } void reset_idletime (void) { static gboolean inited = FALSE; static KeyCode keycode1, keycode2; static gboolean first_keycode = FALSE; if (inited == FALSE) { keycode1 = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_KEY_Shift_L); keycode2 = XKeysymToKeycode (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_KEY_Shift_R); } gdk_error_trap_push (); /* send a left or right alt key; first press, then release */ XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), first_keycode ? keycode1 : keycode2, True, CurrentTime); XTestFakeKeyEvent (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), first_keycode ? keycode1 : keycode2, False, CurrentTime); first_keycode = !first_keycode; gdk_error_trap_pop_ignored (); } static gboolean randr_output_is_on (GsdRROutput *output) { GsdRRCrtc *crtc; crtc = gsd_rr_output_get_crtc (output); if (!crtc) return FALSE; return gsd_rr_crtc_get_current_mode (crtc) != NULL; } gboolean external_monitor_is_connected (GsdRRScreen *screen) { GsdRROutput **outputs; guint i; #ifdef GSD_MOCK char *mock_external_monitor_contents; if (g_file_get_contents ("GSD_MOCK_EXTERNAL_MONITOR", &mock_external_monitor_contents, NULL, NULL)) { if (mock_external_monitor_contents[0] == '1') { g_free (mock_external_monitor_contents); return TRUE; } else if (mock_external_monitor_contents[0] == '0') { g_free (mock_external_monitor_contents); return FALSE; } g_warning ("Unhandled value for GSD_MOCK_EXTERNAL_MONITOR contents: %s", mock_external_monitor_contents); g_free (mock_external_monitor_contents); } #endif /* GSD_MOCK */ /* see if we have more than one screen plugged in */ outputs = gsd_rr_screen_list_outputs (screen); for (i = 0; outputs[i] != NULL; i++) { if (randr_output_is_on (outputs[i]) && !gsd_rr_output_is_laptop (outputs[i])) return TRUE; } return FALSE; } static void play_sound (void) { ca_context_play (ca_gtk_context_get (), UPS_SOUND_LOOP_ID, CA_PROP_EVENT_ID, "battery-caution", CA_PROP_EVENT_DESCRIPTION, _("Battery is critically low"), NULL); } static gboolean play_loop_timeout_cb (gpointer user_data) { play_sound (); return TRUE; } void play_loop_start (guint *id) { if (*id != 0) return; *id = g_timeout_add_seconds (GSD_POWER_MANAGER_CRITICAL_ALERT_TIMEOUT, (GSourceFunc) play_loop_timeout_cb, NULL); play_sound (); } void play_loop_stop (guint *id) { if (*id == 0) return; ca_context_cancel (ca_gtk_context_get (), UPS_SOUND_LOOP_ID); g_source_remove (*id); *id = 0; } ./plugins/power/Makefile.am0000644000004100000410000001266412735467744016150 0ustar www-datawww-dataBUILT_SOURCES = plugin_name = power plugin_LTLIBRARIES = \ libpower.la libpower_la_SOURCES = \ gpm-common.c \ gpm-common.h \ gsd-backlight-linux.c \ gsd-backlight-linux.h \ gsd-power-manager.c \ gsd-power-manager.h \ gsm-inhibitor-flag.h \ gsm-presence-flag.h \ gsm-manager-logout-mode.h \ gsd-power-constants.h \ gsd-power-plugin.c libpower_la_CPPFLAGS = \ -I$(top_srcdir)/data/ \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DGTKBUILDERDIR=\""$(pkgdatadir)"\" \ -DSBINDIR=\"$(sbindir)\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ $(AM_CPPFLAGS) libpower_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(POWER_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AM_CFLAGS) libpower_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libpower_la_LIBADD = \ $(top_builddir)/plugins/common/libcommon.la \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/gnome-settings-daemon/libunity-settings-daemon.la \ $(POWER_LIBS) \ $(SETTINGS_PLUGIN_LIBS) plugin_in_files = \ power.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) com.ubuntu.unity-settings-daemon.plugins.power.policy.in: com.ubuntu.unity-settings-daemon.plugins.power.policy.in.in Makefile $(AM_V_GEN) sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ @INTLTOOL_POLICY_RULE@ polkit_policydir = $(datadir)/polkit-1/actions polkit_policy_in_files = com.ubuntu.unity-settings-daemon.plugins.power.policy.in polkit_policy_DATA = $(polkit_policy_in_files:.policy.in=.policy) # so it always gets included in the tarball usd_backlight_helper_SOURCES = \ gsd-backlight-linux.c \ gsd-backlight-linux.h \ gsd-backlight-helper.c noinst_PROGRAMS = usd-test-power usd_test_power_SOURCES = \ gpm-common.c \ gpm-common.h \ gsd-backlight-linux.c \ gsd-backlight-linux.h \ gsd-power-manager.c \ gsd-power-manager.h \ gsm-inhibitor-flag.h \ gsm-presence-flag.h \ gsm-manager-logout-mode.h \ test-power.c usd_test_power_CFLAGS = $(libpower_la_CFLAGS) usd_test_power_CPPFLAGS = $(libpower_la_CPPFLAGS) -DGSD_MOCK=1 -DGSD_ACTION_DELAY=1 usd_test_power_LDADD = \ -lm \ $(top_builddir)/plugins/common/libcommon.la \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(top_builddir)/gnome-settings-daemon/libunity-settings-daemon.la \ $(POWER_LIBS) \ $(SETTINGS_PLUGIN_LIBS) EXTRA_DIST = \ $(plugin_in_files) \ $(gsd_backlight_helper_SOURCES) \ $(NULL) if HAVE_GUDEV libexec_PROGRAMS = usd-backlight-helper usd_backlight_helper_LDFLAGS = \ $(BACKLIGHT_HELPER_LIBS) \ -lm usd_backlight_helper_CFLAGS = \ $(BACKLIGHT_HELPER_CFLAGS) EXTRA_DIST += \ com.ubuntu.unity-settings-daemon.plugins.power.policy.in.in endif # Enums GSD_POWER_ENUM_FILES = gsd-power-enums.c gsd-power-enums.h gsd-power-enums.h: gsm-inhibitor-flag.h gsm-presence-flag.h Makefile $(AM_V_GEN)($(GLIB_MKENUMS) \ --fhead "#ifndef GSD_POWER_ENUMS_H\n#define GSD_POWER_ENUMS_H\n\n#include \n\nG_BEGIN_DECLS\n" \ --fprod "/* enumerations from \"@filename@\" */\n" \ --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define GSD_POWER_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ --ftail "G_END_DECLS\n\n#endif /* !GSD_POWER_ENUMS_H */" $(srcdir)/gsm-inhibitor-flag.h $(srcdir)/gsm-presence-flag.h > $@) gsd-power-enums.c: gsd-power-constants.h gsm-inhibitor-flag.h gsm-presence-flag.h Makefile gsd-power-enums.h $(AM_V_GEN)($(GLIB_MKENUMS) \ --fhead "#include \"gsm-inhibitor-flag.h\"\n#include \"gsm-presence-flag.h\"\n#include \"gsd-power-enums.h\"" \ --fprod "\n/* enumerations from \"@filename@\" */" \ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \ $(srcdir)/gsm-inhibitor-flag.h $(srcdir)/gsm-presence-flag.h > $@) BUILT_SOURCES += $(GSD_POWER_ENUM_FILES) gsdpowerconstants.py: gsd-power-constants-update.pl gsd-power-constants.h $(AM_V_GEN) $(srcdir)/gsd-power-constants-update.pl gsdpowerenums.py: gsd-power-enums-update gsd-power-enums.h gsd-power-enums.c $(AM_V_GEN) $(builddir)/gsd-power-enums-update > $@ noinst_PROGRAMS += gsd-power-enums-update gsd_power_enums_update_SOURCES = \ gsd-power-enums-update.c \ gsd-power-enums.h \ gsd-power-enums.c gsd_power_enums_update_CFLAGS = $(libpower_la_CFLAGS) gsd_power_enums_update_CPPFLAGS = $(libpower_la_CPPFLAGS) -I$(top_builddir)/plugins/power gsd_power_enums_update_LDFLAGS = $(POWER_LIBS) EXTRA_DIST += gsd-power-constants-update.pl gsdpowerconstants.py gsdpowerenums.py test.py check-local: $(top_builddir)/tests/shiftkey usd-test-power test.py gsdpowerconstants.py gsdpowerenums.py # This is how you run a single test # BUILDDIR=$(builddir) TOP_BUILDDIR=$(top_builddir) ${PYTHON} $(srcdir)/test.py PowerPluginTest.test_sleep_inactive_blank BUILDDIR=$(builddir) TOP_BUILDDIR=$(top_builddir) ${PYTHON} $(srcdir)/test.py clean-local: rm -f *~ CLEANFILES = \ $(plugin_DATA) \ $(GSD_POWER_ENUM_FILES) \ gsdpowerenums.py \ com.ubuntu.unity-settings-daemon.plugins.power.policy \ com.ubuntu.unity-settings-daemon.plugins.power.policy.in @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/power/gsd-backlight-helper.c0000644000004100000410000001623412735467744020235 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2010-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "gsd-backlight-linux.h" #define GSD_BACKLIGHT_HELPER_EXIT_CODE_SUCCESS 0 #define GSD_BACKLIGHT_HELPER_EXIT_CODE_FAILED 1 #define GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID 3 #define GSD_BACKLIGHT_HELPER_EXIT_CODE_INVALID_USER 4 #define GSD_BACKLIGHT_HELPER_EXIT_CODE_NO_DEVICES 5 static gboolean gsd_backlight_helper_write (const gchar *filename, gint value, GError **error) { gchar *filename_path = NULL; gchar *text = NULL; gint retval; gint length; gint fd = -1; gboolean ret = TRUE; filename_path = g_build_filename (filename, "brightness", NULL); fd = open (filename_path, O_WRONLY); if (fd < 0) { ret = FALSE; g_set_error (error, 1, 0, "failed to open filename: %s", filename); goto out; } /* convert to text */ text = g_strdup_printf ("%i", value); length = strlen (text); /* write to device file */ retval = write (fd, text, length); if (retval != length) { ret = FALSE; g_set_error (error, 1, 0, "writing '%s' to %s failed", text, filename); goto out; } out: if (fd >= 0) close (fd); g_free (text); g_free (filename_path); return ret; } static gint gsd_backlight_helper_read_value (const gchar *filename, GError **error) { gchar *contents = NULL; gint value; if (g_file_get_contents (filename, &contents, NULL, error)) value = atoi (contents); else value = -1; g_free (contents); if (value < 0 && *error == NULL) g_set_error (error, 1, 0, "got invalid backlight value from %s", filename); return value; } static gint gsd_backlight_helper_get (const gchar *filename, GError **error) { gchar *filename_path = NULL; gint value; filename_path = g_build_filename (filename, "brightness", NULL); value = gsd_backlight_helper_read_value (filename_path, error); g_free (filename_path); return value; } static gint gsd_backlight_helper_get_max (const gchar *filename, GError **error) { gchar *filename_path = NULL; gint value; filename_path = g_build_filename (filename, "max_brightness", NULL); value = gsd_backlight_helper_read_value (filename_path, error); g_free (filename_path); return value; } static gint clamp_minimum (gint max, gint value) { gint minimum; /* If the interface has less than 100 possible values, it's * likely that 0 doesn't turn the backlight off so we let 0 be * set in that case. */ if (max > 99) minimum = 1; else minimum = 0; return MAX (value, minimum); } int main (int argc, char *argv[]) { GOptionContext *context; gint uid; gint euid; guint retval = 0; GError *error = NULL; gint set_brightness = -1; gboolean get_brightness = FALSE; gboolean get_max_brightness = FALSE; gchar *filename = NULL; GsdBacklightType type; const GOptionEntry options[] = { { "set-brightness", '\0', 0, G_OPTION_ARG_INT, &set_brightness, /* command line argument */ "Set the current brightness", NULL }, { "get-brightness", '\0', 0, G_OPTION_ARG_NONE, &get_brightness, /* command line argument */ "Get the current brightness", NULL }, { "get-max-brightness", '\0', 0, G_OPTION_ARG_NONE, &get_max_brightness, /* command line argument */ "Get the number of brightness levels supported", NULL }, { NULL} }; context = g_option_context_new (NULL); g_option_context_set_summary (context, "GNOME Settings Daemon Backlight Helper"); g_option_context_add_main_entries (context, options, NULL); g_option_context_parse (context, &argc, &argv, NULL); g_option_context_free (context); #ifndef __linux__ /* the g-s-d plugin should only call this helper on linux */ g_critical ("Attempting to call gsb-backlight-helper on non-Linux"); g_assert_not_reached (); #endif /* no input */ if (set_brightness == -1 && !get_brightness && !get_max_brightness) { g_print ("%s\n", "No valid option was specified"); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID; goto out; } /* find device */ filename = gsd_backlight_helper_get_best_backlight (&type); if (filename == NULL) { retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_NO_DEVICES; g_print ("%s: %s\n", "Could not get or set the value of the backlight", "No backlight devices present"); goto out; } /* GetBrightness */ if (get_brightness) { gint value; value = gsd_backlight_helper_get (filename, &error); if (value < 0) { g_print ("%s: %s\n", "Could not get the value of the backlight", error->message); g_error_free (error); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID; goto out; } /* just print the contents to stdout */ g_print ("%d", value); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_SUCCESS; goto out; } /* GetSteps */ if (get_max_brightness) { gint value; value = gsd_backlight_helper_get_max (filename, &error); if (value < 0) { g_print ("%s: %s\n", "Could not get the maximum value of the backlight", error->message); g_error_free (error); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID; goto out; } /* just print the contents to stdout */ g_print ("%d", value); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_SUCCESS; goto out; } /* check calling UID */ uid = getuid (); euid = geteuid (); if (uid != 0 || euid != 0) { g_print ("%s\n", "This program can only be used by the root user"); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID; goto out; } /* SetBrightness */ if (set_brightness != -1) { gboolean ret = FALSE; gint max = gsd_backlight_helper_get_max (filename, &error); if (max < 0) { g_print ("%s: %s\n", "Could not get the maximum value of the backlight", error->message); g_error_free (error); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID; goto out; } if (type == GSD_BACKLIGHT_TYPE_RAW) set_brightness = clamp_minimum (max, set_brightness); ret = gsd_backlight_helper_write (filename, set_brightness, &error); if (!ret) { g_print ("%s: %s\n", "Could not set the value of the backlight", error->message); g_error_free (error); retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_ARGUMENTS_INVALID; goto out; } } /* success */ retval = GSD_BACKLIGHT_HELPER_EXIT_CODE_SUCCESS; out: g_free (filename); return retval; } ./plugins/power/gsd-power-manager.c0000644000004100000410000044161712735467763017604 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2011-2012 Richard Hughes * Copyright (C) 2011 Ritesh Khadgaray * Copyright (C) 2012-2013 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 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 "config.h" #include #include #include #include #define UPOWER_ENABLE_DEPRECATED 1 #include #include #include #include #include #include #include "gsd-power-constants.h" #include "gsm-inhibitor-flag.h" #include "gsm-presence-flag.h" #include "gsm-manager-logout-mode.h" #include "gpm-common.h" #include "gnome-settings-plugin.h" #include "gnome-settings-profile.h" #include "gnome-settings-bus.h" #include "gsd-enums.h" #include "gsd-power-manager.h" #include "gsd-rr.h" #include "gsd-idle-monitor.h" #define GNOME_SESSION_DBUS_NAME "org.gnome.SessionManager" #define GNOME_SESSION_DBUS_PATH_PRESENCE "/org/gnome/SessionManager/Presence" #define GNOME_SESSION_DBUS_INTERFACE_PRESENCE "org.gnome.SessionManager.Presence" #define UPOWER_DBUS_NAME "org.freedesktop.UPower" #define UPOWER_DBUS_PATH "/org/freedesktop/UPower" #define UPOWER_DBUS_PATH_KBDBACKLIGHT "/org/freedesktop/UPower/KbdBacklight" #define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower" #define UPOWER_DBUS_INTERFACE_KBDBACKLIGHT "org.freedesktop.UPower.KbdBacklight" #define GSD_POWER_SETTINGS_SCHEMA "org.gnome.settings-daemon.plugins.power" #define GSD_XRANDR_SETTINGS_SCHEMA "org.gnome.settings-daemon.plugins.xrandr" #define GSD_POWER_DBUS_NAME GSD_DBUS_NAME ".Power" #define GSD_POWER_DBUS_PATH GSD_DBUS_PATH "/Power" #define GSD_POWER_DBUS_INTERFACE GSD_DBUS_BASE_INTERFACE ".Power" #define GSD_POWER_DBUS_INTERFACE_SCREEN GSD_POWER_DBUS_INTERFACE ".Screen" #define GSD_POWER_DBUS_INTERFACE_KEYBOARD GSD_POWER_DBUS_INTERFACE ".Keyboard" #define GSD_POWER_MANAGER_NOTIFY_TIMEOUT_SHORT 10 * 1000 /* ms */ #define GSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG 30 * 1000 /* ms */ #define GSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT 30 /* seconds */ #define SYSTEMD_DBUS_NAME "org.freedesktop.login1" #define SYSTEMD_DBUS_PATH "/org/freedesktop/login1" #define SYSTEMD_DBUS_INTERFACE "org.freedesktop.login1.Manager" /* Keep this in sync with gnome-shell */ #define SCREENSAVER_FADE_TIME 10 /* seconds */ /* Time between notifying the user about a critical action and executing it. * This can be changed with the GSD_ACTION_DELAY constant. */ #ifndef GSD_ACTION_DELAY #define GSD_ACTION_DELAY 20 #endif /* !GSD_ACTION_DELAY */ static const gchar introspection_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; #define GSD_POWER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_POWER_MANAGER, GsdPowerManagerPrivate)) typedef enum { GSD_POWER_IDLE_MODE_NORMAL, GSD_POWER_IDLE_MODE_DIM, GSD_POWER_IDLE_MODE_BLANK, GSD_POWER_IDLE_MODE_SLEEP } GsdPowerIdleMode; struct GsdPowerManagerPrivate { /* D-Bus */ GsdSessionManager *session; guint name_id; GDBusNodeInfo *introspection_data; GDBusConnection *connection; GCancellable *bus_cancellable; GDBusProxy *session_presence_proxy; /* Settings */ GSettings *settings; GSettings *settings_bus; GSettings *settings_screensaver; GSettings *settings_xrandr; gboolean use_time_primary; guint action_percentage; guint action_time; guint critical_percentage; guint critical_time; guint low_percentage; guint low_time; /* Screensaver */ GsdScreenSaver *screensaver_proxy; gboolean screensaver_active; /* State */ gboolean lid_is_closed; UpClient *up_client; gchar *previous_summary; GIcon *previous_icon; GPtrArray *devices_array; UpDevice *device_composite; GsdRRScreen *rr_screen; NotifyNotification *notification_ups_discharging; NotifyNotification *notification_low; NotifyNotification *notification_sleep_warning; NotifyNotification *notification_logout_warning; GsdPowerActionType sleep_action_type; gboolean battery_is_low; /* laptop battery low, or UPS discharging */ /* Brightness */ gboolean backlight_available; gint pre_dim_brightness; /* level, not percentage */ /* Keyboard */ GDBusProxy *upower_kdb_proxy; gint kbd_brightness_max; gint kbd_brightness_old; gint kbd_brightness_pre_dim; /* Sound */ guint32 critical_alert_timeout_id; /* systemd stuff */ GDBusProxy *logind_proxy; gint inhibit_lid_switch_fd; gboolean inhibit_lid_switch_taken; gboolean inhibit_lid_switch_action; gint inhibit_suspend_fd; gboolean inhibit_suspend_taken; guint inhibit_lid_switch_timer_id; gboolean is_virtual_machine; /* Idles */ GsdIdleMonitor *idle_monitor; guint idle_dim_id; guint idle_blank_id; guint idle_sleep_warning_id; guint idle_sleep_id; GsdPowerIdleMode current_idle_mode; guint temporary_unidle_on_ac_id; GsdPowerIdleMode previous_idle_mode; guint xscreensaver_watchdog_timer_id; }; enum { PROP_0, }; static void gsd_power_manager_class_init (GsdPowerManagerClass *klass); static void gsd_power_manager_init (GsdPowerManager *power_manager); static UpDevice *engine_get_composite_device (GsdPowerManager *manager, UpDevice *original_device); static UpDevice *engine_update_composite_device (GsdPowerManager *manager, UpDevice *original_device); static GIcon *engine_get_icon (GsdPowerManager *manager); static gchar *engine_get_summary (GsdPowerManager *manager); static gdouble engine_get_percentage (GsdPowerManager *manager); static void do_power_action_type (GsdPowerManager *manager, GsdPowerActionType action_type); static void do_lid_closed_action (GsdPowerManager *manager); static void inhibit_lid_switch (GsdPowerManager *manager); static void uninhibit_lid_switch (GsdPowerManager *manager); static void main_battery_or_ups_low_changed (GsdPowerManager *manager, gboolean is_low); static void device_properties_changed_cb (UpDevice *device, GParamSpec *pspec, GsdPowerManager *manager); static gboolean idle_is_session_inhibited (GsdPowerManager *manager, guint mask, gboolean *is_inhibited); static void idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode); static void idle_triggered_idle_cb (GsdIdleMonitor *monitor, guint watch_id, gpointer user_data); static void idle_became_active_cb (GsdIdleMonitor *monitor, guint watch_id, gpointer user_data); G_DEFINE_TYPE (GsdPowerManager, gsd_power_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; GQuark gsd_power_manager_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gsd_power_manager_error"); return quark; } static void notify_close_if_showing (NotifyNotification **notification) { if (*notification == NULL) return; notify_notification_close (*notification, NULL); g_clear_object (notification); } typedef enum { WARNING_NONE = 0, WARNING_DISCHARGING = 1, WARNING_LOW = 2, WARNING_CRITICAL = 3, WARNING_ACTION = 4 } GsdPowerManagerWarning; static GVariant * engine_get_icon_property_variant (GsdPowerManager *manager) { GIcon *icon; GVariant *retval; icon = engine_get_icon (manager); if (icon != NULL) { char *str; str = g_icon_to_string (icon); g_object_unref (icon); retval = g_variant_new_string (str); g_free (str); } else { retval = g_variant_new_string (""); } return retval; } static GVariant * engine_get_tooltip_property_variant (GsdPowerManager *manager) { char *tooltip; GVariant *retval; tooltip = engine_get_summary (manager); retval = g_variant_new_string (tooltip != NULL ? tooltip : ""); g_free (tooltip); return retval; } static void engine_emit_changed (GsdPowerManager *manager, gboolean icon_changed, gboolean state_changed) { GVariantBuilder props_builder; GVariant *props_changed = NULL; GError *error = NULL; /* not yet connected to the bus */ if (manager->priv->connection == NULL) return; g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}")); if (icon_changed) g_variant_builder_add (&props_builder, "{sv}", "Icon", engine_get_icon_property_variant (manager)); if (state_changed) g_variant_builder_add (&props_builder, "{sv}", "Tooltip", engine_get_tooltip_property_variant (manager)); g_variant_builder_add (&props_builder, "{sv}", "Percentage", g_variant_new_double (engine_get_percentage (manager))); props_changed = g_variant_new ("(s@a{sv}@as)", GSD_POWER_DBUS_INTERFACE, g_variant_builder_end (&props_builder), g_variant_new_strv (NULL, 0)); g_variant_ref_sink (props_changed); if (!g_dbus_connection_emit_signal (manager->priv->connection, NULL, GSD_POWER_DBUS_PATH, "org.freedesktop.DBus.Properties", "PropertiesChanged", props_changed, &error)) goto out; out: if (error) { g_warning ("%s", error->message); g_clear_error (&error); } if (props_changed) g_variant_unref (props_changed); } static GsdPowerManagerWarning engine_get_warning_csr (GsdPowerManager *manager, UpDevice *device) { gdouble percentage; /* get device properties */ g_object_get (device, "percentage", &percentage, NULL); if (percentage < 26.0f) return WARNING_LOW; else if (percentage < 13.0f) return WARNING_CRITICAL; return WARNING_NONE; } static GsdPowerManagerWarning engine_get_warning_percentage (GsdPowerManager *manager, UpDevice *device) { gdouble percentage; /* get device properties */ g_object_get (device, "percentage", &percentage, NULL); if (percentage <= manager->priv->action_percentage) return WARNING_ACTION; if (percentage <= manager->priv->critical_percentage) return WARNING_CRITICAL; if (percentage <= manager->priv->low_percentage) return WARNING_LOW; return WARNING_NONE; } static GsdPowerManagerWarning engine_get_warning_time (GsdPowerManager *manager, UpDevice *device) { UpDeviceKind kind; gint64 time_to_empty; /* get device properties */ g_object_get (device, "kind", &kind, "time-to-empty", &time_to_empty, NULL); /* this is probably an error condition */ if (time_to_empty == 0) { g_debug ("time zero, falling back to percentage for %s", up_device_kind_to_string (kind)); return engine_get_warning_percentage (manager, device); } if (time_to_empty <= manager->priv->action_time) return WARNING_ACTION; if (time_to_empty <= manager->priv->critical_time) return WARNING_CRITICAL; if (time_to_empty <= manager->priv->low_time) return WARNING_LOW; return WARNING_NONE; } /** * This gets the possible engine state for the device according to the * policy, which could be per-percent, or per-time. **/ static GsdPowerManagerWarning engine_get_warning (GsdPowerManager *manager, UpDevice *device) { UpDeviceKind kind; UpDeviceState state; GsdPowerManagerWarning warning_type; /* get device properties */ g_object_get (device, "kind", &kind, "state", &state, NULL); /* default to no engine */ warning_type = WARNING_NONE; /* if the device in question is on ac, don't give a warning */ if (state == UP_DEVICE_STATE_CHARGING) goto out; if (kind == UP_DEVICE_KIND_MOUSE || kind == UP_DEVICE_KIND_KEYBOARD) { warning_type = engine_get_warning_csr (manager, device); } else if (kind == UP_DEVICE_KIND_UPS || kind == UP_DEVICE_KIND_MEDIA_PLAYER || kind == UP_DEVICE_KIND_TABLET || kind == UP_DEVICE_KIND_COMPUTER || kind == UP_DEVICE_KIND_PDA) { warning_type = engine_get_warning_percentage (manager, device); } else if (kind == UP_DEVICE_KIND_PHONE) { warning_type = engine_get_warning_percentage (manager, device); } else if (kind == UP_DEVICE_KIND_BATTERY) { /* only use the time when it is accurate, and settings is not disabled */ if (manager->priv->use_time_primary) warning_type = engine_get_warning_time (manager, device); else warning_type = engine_get_warning_percentage (manager, device); } /* If we have no important engines, we should test for discharging */ if (warning_type == WARNING_NONE) { if (state == UP_DEVICE_STATE_DISCHARGING) warning_type = WARNING_DISCHARGING; } out: return warning_type; } static gchar * engine_get_summary (GsdPowerManager *manager) { guint i; GPtrArray *array; UpDevice *device; UpDeviceState state; GString *tooltip = NULL; gchar *part; gboolean is_present; /* need to get AC state */ tooltip = g_string_new (""); /* do we have specific device types? */ array = manager->priv->devices_array; for (i=0;ilen;i++) { device = g_ptr_array_index (array, i); g_object_get (device, "is-present", &is_present, "state", &state, NULL); if (!is_present) continue; if (state == UP_DEVICE_STATE_EMPTY) continue; part = gpm_upower_get_device_summary (device); if (part != NULL) g_string_append_printf (tooltip, "%s\n", part); g_free (part); } /* remove the last \n */ g_string_truncate (tooltip, tooltip->len-1); g_debug ("tooltip: %s", tooltip->str); return g_string_free (tooltip, FALSE); } static gdouble engine_get_percentage (GsdPowerManager *manager) { guint i; GPtrArray *array; UpDevice *device; UpDeviceKind kind; gboolean is_present; gdouble percentage; array = manager->priv->devices_array; for (i = 0; i < array->len ; i++) { device = g_ptr_array_index (array, i); /* get device properties */ g_object_get (device, "kind", &kind, "is-present", &is_present, NULL); /* if battery then use composite device to cope with multiple batteries */ if (kind == UP_DEVICE_KIND_BATTERY) device = engine_get_composite_device (manager, device); if (is_present) { /* Doing it here as it could be a composite device */ g_object_get (device, "percentage", &percentage, NULL); return percentage; } } return -1; } static GIcon * engine_get_icon_priv (GsdPowerManager *manager, UpDeviceKind device_kind, GsdPowerManagerWarning warning, gboolean use_state) { guint i; GPtrArray *array; UpDevice *device; GsdPowerManagerWarning warning_temp; UpDeviceKind kind; UpDeviceState state; gboolean is_present; /* do we have specific device types? */ array = manager->priv->devices_array; for (i=0;ilen;i++) { device = g_ptr_array_index (array, i); /* get device properties */ g_object_get (device, "kind", &kind, "state", &state, "is-present", &is_present, NULL); /* if battery then use composite device to cope with multiple batteries */ if (kind == UP_DEVICE_KIND_BATTERY) device = engine_get_composite_device (manager, device); warning_temp = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-warning-old")); if (kind == device_kind && is_present) { if (warning != WARNING_NONE) { if (warning_temp == warning) return gpm_upower_get_device_icon (device, TRUE); continue; } if (use_state) { if (state == UP_DEVICE_STATE_CHARGING || state == UP_DEVICE_STATE_DISCHARGING) return gpm_upower_get_device_icon (device, TRUE); continue; } return gpm_upower_get_device_icon (device, TRUE); } } return NULL; } static GIcon * engine_get_icon (GsdPowerManager *manager) { GIcon *icon = NULL; /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */ icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_CRITICAL, FALSE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_CRITICAL, FALSE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_MOUSE, WARNING_CRITICAL, FALSE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_KEYBOARD, WARNING_CRITICAL, FALSE); if (icon != NULL) return icon; /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */ icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_LOW, FALSE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_LOW, FALSE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_MOUSE, WARNING_LOW, FALSE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_KEYBOARD, WARNING_LOW, FALSE); if (icon != NULL) return icon; /* we try (DIS)CHARGING: BATTERY, UPS */ icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_NONE, TRUE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_NONE, TRUE); if (icon != NULL) return icon; /* we try PRESENT: BATTERY, UPS */ icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_NONE, FALSE); if (icon != NULL) return icon; icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_NONE, FALSE); if (icon != NULL) return icon; /* do not show an icon */ return NULL; } static gboolean engine_recalculate_state_icon (GsdPowerManager *manager) { GIcon *icon; /* show a different icon if we are disconnected */ icon = engine_get_icon (manager); if (g_icon_equal (icon, manager->priv->previous_icon)) { g_clear_object (&icon); return FALSE; } g_clear_object (&manager->priv->previous_icon); manager->priv->previous_icon = icon; g_debug ("Icon changed"); return TRUE; } static gboolean engine_recalculate_state_summary (GsdPowerManager *manager) { char *summary; summary = engine_get_summary (manager); if (g_strcmp0 (manager->priv->previous_summary, summary) == 0) { g_free (summary); return FALSE; } g_free (manager->priv->previous_summary); manager->priv->previous_summary = summary; g_debug ("Summary changed"); return TRUE; } static void engine_recalculate_state (GsdPowerManager *manager) { gboolean icon_changed = FALSE; gboolean state_changed = FALSE; icon_changed = engine_recalculate_state_icon (manager); state_changed = engine_recalculate_state_summary (manager); /* only emit if the icon or summary has changed */ if (icon_changed || state_changed) engine_emit_changed (manager, icon_changed, state_changed); } static UpDevice * engine_get_composite_device (GsdPowerManager *manager, UpDevice *original_device) { guint battery_devices = 0; GPtrArray *array; UpDevice *device; UpDeviceKind kind; UpDeviceKind original_kind; guint i; /* get the type of the original device */ g_object_get (original_device, "kind", &original_kind, NULL); /* find out how many batteries in the system */ array = manager->priv->devices_array; for (i=0;ilen;i++) { device = g_ptr_array_index (array, i); g_object_get (device, "kind", &kind, NULL); if (kind == original_kind) battery_devices++; } /* just use the original device if only one primary battery */ if (battery_devices <= 1) return original_device; /* use the composite device */ device = manager->priv->device_composite; /* return composite device or original device */ return device; } static UpDevice * engine_update_composite_device (GsdPowerManager *manager, UpDevice *original_device) { guint i; gdouble percentage = 0.0; gdouble energy = 0.0; gdouble energy_full = 0.0; gdouble energy_rate = 0.0; gdouble energy_total = 0.0; gdouble energy_full_total = 0.0; gdouble energy_rate_total = 0.0; gint64 time_to_empty = 0; gint64 time_to_full = 0; guint battery_devices = 0; gboolean is_charging = FALSE; gboolean is_discharging = FALSE; gboolean is_fully_charged = TRUE; GPtrArray *array; UpDevice *device; UpDeviceState state; UpDeviceKind kind; UpDeviceKind original_kind; /* get the type of the original device */ g_object_get (original_device, "kind", &original_kind, NULL); /* update the composite device */ array = manager->priv->devices_array; for (i=0;ilen;i++) { device = g_ptr_array_index (array, i); g_object_get (device, "kind", &kind, "state", &state, "energy", &energy, "energy-full", &energy_full, "energy-rate", &energy_rate, NULL); if (kind != original_kind) continue; /* one of these will be charging or discharging */ if (state == UP_DEVICE_STATE_CHARGING) is_charging = TRUE; if (state == UP_DEVICE_STATE_DISCHARGING) is_discharging = TRUE; if (state != UP_DEVICE_STATE_FULLY_CHARGED) is_fully_charged = FALSE; /* sum up composite */ energy_total += energy; energy_full_total += energy_full; energy_rate_total += energy_rate; battery_devices++; } /* just use the original device if only one primary battery */ if (battery_devices == 1) { g_debug ("using original device as only one primary battery"); device = original_device; goto out; } /* use percentage weighted for each battery capacity */ if (energy_full_total > 0.0) percentage = 100.0 * energy_total / energy_full_total; /* set composite state */ if (is_charging) state = UP_DEVICE_STATE_CHARGING; else if (is_discharging) state = UP_DEVICE_STATE_DISCHARGING; else if (is_fully_charged) state = UP_DEVICE_STATE_FULLY_CHARGED; else state = UP_DEVICE_STATE_UNKNOWN; /* calculate a quick and dirty time remaining value */ if (energy_rate_total > 0) { if (state == UP_DEVICE_STATE_DISCHARGING) time_to_empty = 3600 * (energy_total / energy_rate_total); else if (state == UP_DEVICE_STATE_CHARGING) time_to_full = 3600 * ((energy_full_total - energy_total) / energy_rate_total); } /* okay, we can use the composite device */ device = manager->priv->device_composite; g_debug ("printing composite device"); g_object_set (device, "energy", energy, "energy-full", energy_full, "energy-rate", energy_rate, "time-to-empty", time_to_empty, "time-to-full", time_to_full, "percentage", percentage, "state", state, NULL); /* force update of icon */ if (engine_recalculate_state_icon (manager)) engine_emit_changed (manager, TRUE, FALSE); out: /* return composite device or original device */ return device; } static void engine_device_add (GsdPowerManager *manager, UpDevice *device) { GsdPowerManagerWarning warning; UpDeviceState state; UpDeviceKind kind; UpDevice *composite; /* assign warning */ warning = engine_get_warning (manager, device); g_object_set_data (G_OBJECT(device), "engine-warning-old", GUINT_TO_POINTER(warning)); /* get device properties */ g_object_get (device, "kind", &kind, "state", &state, NULL); /* add old state for transitions */ g_debug ("adding %s with state %s", up_device_get_object_path (device), up_device_state_to_string (state)); g_object_set_data (G_OBJECT(device), "engine-state-old", GUINT_TO_POINTER(state)); if (kind == UP_DEVICE_KIND_BATTERY) { g_debug ("updating because we added a device"); composite = engine_update_composite_device (manager, device); /* get the same values for the composite device */ warning = engine_get_warning (manager, composite); g_object_set_data (G_OBJECT(composite), "engine-warning-old", GUINT_TO_POINTER(warning)); g_object_get (composite, "state", &state, NULL); g_object_set_data (G_OBJECT(composite), "engine-state-old", GUINT_TO_POINTER(state)); } g_ptr_array_add (manager->priv->devices_array, g_object_ref(device)); g_signal_connect (device, "notify::state", G_CALLBACK (device_properties_changed_cb), manager); g_signal_connect (device, "notify::warning-level", G_CALLBACK (device_properties_changed_cb), manager); } static gboolean engine_coldplug (GsdPowerManager *manager) { guint i; GPtrArray *array = NULL; UpDevice *device; engine_recalculate_state (manager); /* add to database */ array = up_client_get_devices (manager->priv->up_client); if (array == NULL) goto out; for (i=0;ilen;i++) { device = g_ptr_array_index (array, i); engine_device_add (manager, device); } out: if (array != NULL) g_ptr_array_unref (array); /* never repeat */ return FALSE; } static void engine_device_added_cb (UpClient *client, UpDevice *device, GsdPowerManager *manager) { /* add to list */ g_ptr_array_add (manager->priv->devices_array, g_object_ref (device)); engine_recalculate_state (manager); } static void engine_device_removed_cb (UpClient *client, const char *object_path, GsdPowerManager *manager) { guint i; for (i = 0; i < manager->priv->devices_array->len; i++) { UpDevice *device = g_ptr_array_index (manager->priv->devices_array, i); if (g_strcmp0 (object_path, up_device_get_object_path (device)) == 0) { g_ptr_array_remove_index (manager->priv->devices_array, i); break; } } } static void on_notification_closed (NotifyNotification *notification, gpointer data) { g_object_unref (notification); } static const gchar * get_first_themed_icon_name (GIcon *icon) { const gchar* const *icon_names; const gchar *icon_name = NULL; /* no icon */ if (icon == NULL) goto out; /* just use the first icon */ icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon)); if (icon_names != NULL) icon_name = icon_names[0]; out: return icon_name; } static void create_notification (const char *summary, const char *body, GIcon *icon, NotifyNotification **weak_pointer_location) { NotifyNotification *notification; notification = notify_notification_new (summary, body, icon ? get_first_themed_icon_name (icon) : NULL); *weak_pointer_location = notification; g_object_add_weak_pointer (G_OBJECT (notification), (gpointer *) weak_pointer_location); g_signal_connect (notification, "closed", G_CALLBACK (on_notification_closed), NULL); } static void engine_ups_discharging (GsdPowerManager *manager, UpDevice *device) { const gchar *title; gchar *remaining_text = NULL; gdouble percentage; GIcon *icon = NULL; gint64 time_to_empty; GString *message; UpDeviceKind kind; /* get device properties */ g_object_get (device, "kind", &kind, "percentage", &percentage, "time-to-empty", &time_to_empty, NULL); if (kind != UP_DEVICE_KIND_UPS) return; main_battery_or_ups_low_changed (manager, TRUE); /* only show text if there is a valid time */ if (time_to_empty > 0) remaining_text = gpm_get_timestring (time_to_empty); /* TRANSLATORS: UPS is now discharging */ title = _("UPS Discharging"); message = g_string_new (""); if (remaining_text != NULL) { /* TRANSLATORS: tell the user how much time they have got */ g_string_append_printf (message, _("%s of UPS backup power remaining"), remaining_text); } else { g_string_append (message, gpm_device_to_localised_string (device)); } g_string_append_printf (message, " (%.0f%%)", percentage); icon = gpm_upower_get_device_icon (device, TRUE); /* close any existing notification of this class */ notify_close_if_showing (&manager->priv->notification_ups_discharging); /* create a new notification */ create_notification (title, message->str, icon, &manager->priv->notification_ups_discharging); notify_notification_set_timeout (manager->priv->notification_ups_discharging, GSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG); notify_notification_set_urgency (manager->priv->notification_ups_discharging, NOTIFY_URGENCY_NORMAL); /* TRANSLATORS: this is the notification application name */ notify_notification_set_app_name (manager->priv->notification_ups_discharging, _("Power")); notify_notification_set_hint (manager->priv->notification_ups_discharging, "transient", g_variant_new_boolean (TRUE)); notify_notification_show (manager->priv->notification_ups_discharging, NULL); g_string_free (message, TRUE); if (icon != NULL) g_object_unref (icon); g_free (remaining_text); } static GsdPowerActionType manager_critical_action_get (GsdPowerManager *manager, gboolean is_ups) { GsdPowerActionType policy; GVariant *result = NULL; policy = g_settings_get_enum (manager->priv->settings, "critical-battery-action"); if (policy == GSD_POWER_ACTION_SUSPEND) { if (is_ups == FALSE) { result = g_dbus_proxy_call_sync (manager->priv->logind_proxy, "CanSuspend", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); } } else if (policy == GSD_POWER_ACTION_HIBERNATE) { result = g_dbus_proxy_call_sync (manager->priv->logind_proxy, "CanHibernate", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); } else { /* Other actions need no check */ return policy; } if (result) { const char *s; g_variant_get (result, "(s)", &s); if (g_strcmp0 (s, "yes") != 0) policy = GSD_POWER_ACTION_SHUTDOWN; g_variant_unref (result); } else { policy = GSD_POWER_ACTION_SHUTDOWN; } return policy; } static gboolean manager_critical_action_do (GsdPowerManager *manager, gboolean is_ups) { GsdPowerActionType action_type; /* stop playing the alert as it's too late to do anything now */ play_loop_stop (&manager->priv->critical_alert_timeout_id); action_type = manager_critical_action_get (manager, is_ups); do_power_action_type (manager, action_type); return FALSE; } static gboolean manager_critical_action_do_cb (GsdPowerManager *manager) { manager_critical_action_do (manager, FALSE); return FALSE; } static gboolean manager_critical_ups_action_do_cb (GsdPowerManager *manager) { manager_critical_action_do (manager, TRUE); return FALSE; } static gboolean engine_just_laptop_battery (GsdPowerManager *manager) { UpDevice *device; UpDeviceKind kind; GPtrArray *array; gboolean ret = TRUE; guint i; /* find if there are any other device types that mean we have to * be more specific in our wording */ array = manager->priv->devices_array; for (i=0; ilen; i++) { device = g_ptr_array_index (array, i); g_object_get (device, "kind", &kind, NULL); if (kind != UP_DEVICE_KIND_BATTERY) { ret = FALSE; break; } } return ret; } static void engine_charge_low (GsdPowerManager *manager, UpDevice *device) { const gchar *title = NULL; gboolean ret; gchar *message = NULL; gchar *tmp; gchar *remaining_text; gdouble percentage; GIcon *icon = NULL; gint64 time_to_empty; UpDeviceKind kind; /* get device properties */ g_object_get (device, "kind", &kind, "percentage", &percentage, "time-to-empty", &time_to_empty, NULL); /* check to see if the batteries have not noticed we are on AC */ if (kind == UP_DEVICE_KIND_BATTERY) { if (!up_client_get_on_battery (manager->priv->up_client)) { g_warning ("ignoring low message as we are not on battery power"); goto out; } } if (kind == UP_DEVICE_KIND_BATTERY) { /* if the user has no other batteries, drop the "Laptop" wording */ ret = engine_just_laptop_battery (manager); if (ret) { /* TRANSLATORS: laptop battery low, and we only have one battery */ title = _("Battery low"); } else { /* TRANSLATORS: laptop battery low, and we have more than one kind of battery */ title = _("Laptop battery low"); } tmp = gpm_get_timestring (time_to_empty); remaining_text = g_strconcat ("", tmp, "", NULL); g_free (tmp); /* TRANSLATORS: tell the user how much time they have got */ message = g_strdup_printf (_("Approximately %s remaining (%.0f%%)"), remaining_text, percentage); g_free (remaining_text); main_battery_or_ups_low_changed (manager, TRUE); } else if (kind == UP_DEVICE_KIND_UPS) { /* TRANSLATORS: UPS is starting to get a little low */ title = _("UPS low"); tmp = gpm_get_timestring (time_to_empty); remaining_text = g_strconcat ("", tmp, "", NULL); g_free (tmp); /* TRANSLATORS: tell the user how much time they have got */ message = g_strdup_printf (_("Approximately %s of remaining UPS backup power (%.0f%%)"), remaining_text, percentage); g_free (remaining_text); } else if (kind == UP_DEVICE_KIND_MOUSE) { /* TRANSLATORS: mouse is getting a little low */ title = _("Mouse battery low"); /* TRANSLATORS: tell user more details */ message = g_strdup_printf (_("Wireless mouse is low in power (%.0f%%)"), percentage); } else if (kind == UP_DEVICE_KIND_KEYBOARD) { /* TRANSLATORS: keyboard is getting a little low */ title = _("Keyboard battery low"); /* TRANSLATORS: tell user more details */ message = g_strdup_printf (_("Wireless keyboard is low in power (%.0f%%)"), percentage); } else if (kind == UP_DEVICE_KIND_PDA) { /* TRANSLATORS: PDA is getting a little low */ title = _("PDA battery low"); /* TRANSLATORS: tell user more details */ message = g_strdup_printf (_("PDA is low in power (%.0f%%)"), percentage); } else if (kind == UP_DEVICE_KIND_PHONE) { /* TRANSLATORS: cell phone (mobile) is getting a little low */ title = _("Cell phone battery low"); /* TRANSLATORS: tell user more details */ message = g_strdup_printf (_("Cell phone is low in power (%.0f%%)"), percentage); } else if (kind == UP_DEVICE_KIND_MEDIA_PLAYER) { /* TRANSLATORS: media player, e.g. mp3 is getting a little low */ title = _("Media player battery low"); /* TRANSLATORS: tell user more details */ message = g_strdup_printf (_("Media player is low in power (%.0f%%)"), percentage); } else if (kind == UP_DEVICE_KIND_TABLET) { /* TRANSLATORS: graphics tablet, e.g. wacom is getting a little low */ title = _("Tablet battery low"); /* TRANSLATORS: tell user more details */ message = g_strdup_printf (_("Tablet is low in power (%.0f%%)"), percentage); } else if (kind == UP_DEVICE_KIND_COMPUTER) { /* TRANSLATORS: computer, e.g. ipad is getting a little low */ title = _("Attached computer battery low"); /* TRANSLATORS: tell user more details */ message = g_strdup_printf (_("Attached computer is low in power (%.0f%%)"), percentage); } /* get correct icon */ icon = gpm_upower_get_device_icon (device, TRUE); /* close any existing notification of this class */ notify_close_if_showing (&manager->priv->notification_low); /* create a new notification */ create_notification (title, message, icon, &manager->priv->notification_low); notify_notification_set_timeout (manager->priv->notification_low, GSD_POWER_MANAGER_NOTIFY_TIMEOUT_LONG); notify_notification_set_urgency (manager->priv->notification_low, NOTIFY_URGENCY_NORMAL); notify_notification_set_app_name (manager->priv->notification_low, _("Power")); notify_notification_set_hint (manager->priv->notification_low, "transient", g_variant_new_boolean (TRUE)); notify_notification_show (manager->priv->notification_low, NULL); /* play the sound, using sounds from the naming spec */ ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "battery-low", /* TRANSLATORS: this is the sound description */ CA_PROP_EVENT_DESCRIPTION, _("Battery is low"), NULL); out: if (icon != NULL) g_object_unref (icon); g_free (message); } static void engine_charge_critical (GsdPowerManager *manager, UpDevice *device) { const gchar *title = NULL; gboolean ret; gchar *message = NULL; gdouble percentage; GIcon *icon = NULL; gint64 time_to_empty; GsdPowerActionType policy; UpDeviceKind kind; /* get device properties */ g_object_get (device, "kind", &kind, "percentage", &percentage, "time-to-empty", &time_to_empty, NULL); /* check to see if the batteries have not noticed we are on AC */ if (kind == UP_DEVICE_KIND_BATTERY) { if (!up_client_get_on_battery (manager->priv->up_client)) { g_warning ("ignoring critically low message as we are not on battery power"); goto out; } } if (kind == UP_DEVICE_KIND_BATTERY) { /* if the user has no other batteries, drop the "Laptop" wording */ ret = engine_just_laptop_battery (manager); if (ret) { /* TRANSLATORS: laptop battery critically low, and only have one kind of battery */ title = _("Battery critically low"); } else { /* TRANSLATORS: laptop battery critically low, and we have more than one type of battery */ title = _("Laptop battery critically low"); } /* we have to do different warnings depending on the policy */ policy = manager_critical_action_get (manager, FALSE); /* use different text for different actions */ if (policy == GSD_POWER_ACTION_NOTHING) { /* TRANSLATORS: tell the use to insert the plug, as we're not going to do anything */ message = g_strdup (_("Plug in your AC adapter to avoid losing data.")); } else if (policy == GSD_POWER_ACTION_SUSPEND) { /* TRANSLATORS: give the user a ultimatum */ message = g_strdup_printf (_("Computer will suspend very soon unless it is plugged in.")); } else if (policy == GSD_POWER_ACTION_HIBERNATE) { /* TRANSLATORS: give the user a ultimatum */ message = g_strdup_printf (_("Computer will hibernate very soon unless it is plugged in.")); } else if (policy == GSD_POWER_ACTION_SHUTDOWN) { /* TRANSLATORS: give the user a ultimatum */ message = g_strdup_printf (_("Computer will shutdown very soon unless it is plugged in.")); } main_battery_or_ups_low_changed (manager, TRUE); } else if (kind == UP_DEVICE_KIND_UPS) { gchar *remaining_text; gchar *tmp; /* TRANSLATORS: the UPS is very low */ title = _("UPS critically low"); tmp = gpm_get_timestring (time_to_empty); remaining_text = g_strconcat ("", tmp, "", NULL); g_free (tmp); /* TRANSLATORS: give the user a ultimatum */ message = g_strdup_printf (_("Approximately %s of remaining UPS power (%.0f%%). " "Restore AC power to your computer to avoid losing data."), remaining_text, percentage); g_free (remaining_text); } else if (kind == UP_DEVICE_KIND_MOUSE) { /* TRANSLATORS: the mouse battery is very low */ title = _("Mouse battery low"); /* TRANSLATORS: the device is just going to stop working */ message = g_strdup_printf (_("Wireless mouse is very low in power (%.0f%%). " "This device will soon stop functioning if not charged."), percentage); } else if (kind == UP_DEVICE_KIND_KEYBOARD) { /* TRANSLATORS: the keyboard battery is very low */ title = _("Keyboard battery low"); /* TRANSLATORS: the device is just going to stop working */ message = g_strdup_printf (_("Wireless keyboard is very low in power (%.0f%%). " "This device will soon stop functioning if not charged."), percentage); } else if (kind == UP_DEVICE_KIND_PDA) { /* TRANSLATORS: the PDA battery is very low */ title = _("PDA battery low"); /* TRANSLATORS: the device is just going to stop working */ message = g_strdup_printf (_("PDA is very low in power (%.0f%%). " "This device will soon stop functioning if not charged."), percentage); } else if (kind == UP_DEVICE_KIND_PHONE) { /* TRANSLATORS: the cell battery is very low */ title = _("Cell phone battery low"); /* TRANSLATORS: the device is just going to stop working */ message = g_strdup_printf (_("Cell phone is very low in power (%.0f%%). " "This device will soon stop functioning if not charged."), percentage); } else if (kind == UP_DEVICE_KIND_MEDIA_PLAYER) { /* TRANSLATORS: the cell battery is very low */ title = _("Cell phone battery low"); /* TRANSLATORS: the device is just going to stop working */ message = g_strdup_printf (_("Media player is very low in power (%.0f%%). " "This device will soon stop functioning if not charged."), percentage); } else if (kind == UP_DEVICE_KIND_TABLET) { /* TRANSLATORS: the cell battery is very low */ title = _("Tablet battery low"); /* TRANSLATORS: the device is just going to stop working */ message = g_strdup_printf (_("Tablet is very low in power (%.0f%%). " "This device will soon stop functioning if not charged."), percentage); } else if (kind == UP_DEVICE_KIND_COMPUTER) { /* TRANSLATORS: the cell battery is very low */ title = _("Attached computer battery low"); /* TRANSLATORS: the device is just going to stop working */ message = g_strdup_printf (_("Attached computer is very low in power (%.0f%%). " "The device will soon shutdown if not charged."), percentage); } /* get correct icon */ icon = gpm_upower_get_device_icon (device, TRUE); /* close any existing notification of this class */ notify_close_if_showing (&manager->priv->notification_low); /* create a new notification */ create_notification (title, message, icon, &manager->priv->notification_low); notify_notification_set_timeout (manager->priv->notification_low, NOTIFY_EXPIRES_DEFAULT); notify_notification_set_urgency (manager->priv->notification_low, NOTIFY_URGENCY_CRITICAL); notify_notification_set_app_name (manager->priv->notification_low, _("Power")); notify_notification_show (manager->priv->notification_low, NULL); switch (kind) { case UP_DEVICE_KIND_BATTERY: case UP_DEVICE_KIND_UPS: g_debug ("critical charge level reached, starting sound loop"); play_loop_start (&manager->priv->critical_alert_timeout_id); break; default: /* play the sound, using sounds from the naming spec */ ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "battery-caution", /* TRANSLATORS: this is the sound description */ CA_PROP_EVENT_DESCRIPTION, _("Battery is critically low"), NULL); break; } out: if (icon != NULL) g_object_unref (icon); g_free (message); } static void engine_charge_action (GsdPowerManager *manager, UpDevice *device) { const gchar *title = NULL; gchar *message = NULL; GIcon *icon = NULL; GsdPowerActionType policy; guint timer_id; UpDeviceKind kind; /* get device properties */ g_object_get (device, "kind", &kind, NULL); /* check to see if the batteries have not noticed we are on AC */ if (kind == UP_DEVICE_KIND_BATTERY) { if (!up_client_get_on_battery (manager->priv->up_client)) { g_warning ("ignoring critically low message as we are not on battery power"); goto out; } } if (kind == UP_DEVICE_KIND_BATTERY) { /* TRANSLATORS: laptop battery is really, really, low */ title = _("Laptop battery critically low"); /* we have to do different warnings depending on the policy */ policy = manager_critical_action_get (manager, FALSE); /* use different text for different actions */ if (policy == GSD_POWER_ACTION_NOTHING) { /* TRANSLATORS: computer will shutdown without saving data */ message = g_strdup (_("The battery is below the critical level and " "this computer will power-off when the " "battery becomes completely empty.")); } else if (policy == GSD_POWER_ACTION_SUSPEND) { /* TRANSLATORS: computer will suspend */ message = g_strdup (_("The battery is below the critical level and " "this computer is about to suspend.\n" "NOTE: A small amount of power is required " "to keep your computer in a suspended state.")); } else if (policy == GSD_POWER_ACTION_HIBERNATE) { /* TRANSLATORS: computer will hibernate */ message = g_strdup (_("The battery is below the critical level and " "this computer is about to hibernate.")); } else if (policy == GSD_POWER_ACTION_SHUTDOWN) { /* TRANSLATORS: computer will just shutdown */ message = g_strdup (_("The battery is below the critical level and " "this computer is about to shutdown.")); } /* wait 20 seconds for user-panic */ timer_id = g_timeout_add_seconds (GSD_ACTION_DELAY, (GSourceFunc) manager_critical_action_do_cb, manager); g_source_set_name_by_id (timer_id, "[GsdPowerManager] battery critical-action"); } else if (kind == UP_DEVICE_KIND_UPS) { /* TRANSLATORS: UPS is really, really, low */ title = _("UPS critically low"); /* we have to do different warnings depending on the policy */ policy = manager_critical_action_get (manager, TRUE); /* use different text for different actions */ if (policy == GSD_POWER_ACTION_NOTHING) { /* TRANSLATORS: computer will shutdown without saving data */ message = g_strdup (_("UPS is below the critical level and " "this computer will power-off when the " "UPS becomes completely empty.")); } else if (policy == GSD_POWER_ACTION_HIBERNATE) { /* TRANSLATORS: computer will hibernate */ message = g_strdup (_("UPS is below the critical level and " "this computer is about to hibernate.")); } else if (policy == GSD_POWER_ACTION_SHUTDOWN) { /* TRANSLATORS: computer will just shutdown */ message = g_strdup (_("UPS is below the critical level and " "this computer is about to shutdown.")); } /* wait 20 seconds for user-panic */ timer_id = g_timeout_add_seconds (GSD_ACTION_DELAY, (GSourceFunc) manager_critical_ups_action_do_cb, manager); g_source_set_name_by_id (timer_id, "[GsdPowerManager] ups critical-action"); } /* not all types have actions */ if (title == NULL) return; /* get correct icon */ icon = gpm_upower_get_device_icon (device, TRUE); /* close any existing notification of this class */ notify_close_if_showing (&manager->priv->notification_low); /* create a new notification */ create_notification (title, message, icon, &manager->priv->notification_low); notify_notification_set_timeout (manager->priv->notification_low, NOTIFY_EXPIRES_DEFAULT); notify_notification_set_urgency (manager->priv->notification_low, NOTIFY_URGENCY_CRITICAL); notify_notification_set_app_name (manager->priv->notification_low, _("Power")); /* try to show */ notify_notification_show (manager->priv->notification_low, NULL); /* play the sound, using sounds from the naming spec */ ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "battery-caution", /* TRANSLATORS: this is the sound description */ CA_PROP_EVENT_DESCRIPTION, _("Battery is critically low"), NULL); out: if (icon != NULL) g_object_unref (icon); g_free (message); } static void device_properties_changed_cb (UpDevice *device, GParamSpec *pspec, GsdPowerManager *manager) { UpDeviceKind kind; UpDeviceState state; UpDeviceState state_old; GsdPowerManagerWarning warning_old; GsdPowerManagerWarning warning; /* get device properties */ g_object_get (device, "kind", &kind, NULL); /* if battery then use composite device to cope with multiple batteries */ if (kind == UP_DEVICE_KIND_BATTERY) { g_debug ("updating because %s changed", up_device_get_object_path (device)); device = engine_update_composite_device (manager, device); } /* get device properties (may be composite) */ g_object_get (device, "state", &state, NULL); g_debug ("%s state is now %s", up_device_get_object_path (device), up_device_state_to_string (state)); /* see if any interesting state changes have happened */ state_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-state-old")); if (state_old != state) { if (state == UP_DEVICE_STATE_DISCHARGING) { g_debug ("discharging"); engine_ups_discharging (manager, device); } else if (state == UP_DEVICE_STATE_FULLY_CHARGED || state == UP_DEVICE_STATE_CHARGING) { g_debug ("fully charged or charging, hiding notifications if any"); notify_close_if_showing (&manager->priv->notification_low); notify_close_if_showing (&manager->priv->notification_ups_discharging); main_battery_or_ups_low_changed (manager, FALSE); } /* save new state */ g_object_set_data (G_OBJECT(device), "engine-state-old", GUINT_TO_POINTER(state)); } /* check the warning state has not changed */ warning_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-warning-old")); warning = engine_get_warning (manager, device); if (warning != warning_old) { if (warning == WARNING_LOW) { g_debug ("** EMIT: charge-low"); engine_charge_low (manager, device); } else if (warning == WARNING_CRITICAL) { g_debug ("** EMIT: charge-critical"); engine_charge_critical (manager, device); } else if (warning == WARNING_ACTION) { g_debug ("charge-action"); engine_charge_action (manager, device); } /* save new state */ g_object_set_data (G_OBJECT(device), "engine-warning-old", GUINT_TO_POINTER(warning)); } engine_recalculate_state (manager); } static UpDevice * engine_get_primary_device (GsdPowerManager *manager) { guint i; UpDevice *device = NULL; UpDevice *device_tmp; UpDeviceKind kind; UpDeviceState state; gboolean is_present; for (i=0; ipriv->devices_array->len; i++) { device_tmp = g_ptr_array_index (manager->priv->devices_array, i); /* get device properties */ g_object_get (device_tmp, "kind", &kind, "state", &state, "is-present", &is_present, NULL); /* not present */ if (!is_present) continue; /* not discharging */ if (state != UP_DEVICE_STATE_DISCHARGING) continue; /* not battery */ if (kind != UP_DEVICE_KIND_BATTERY) continue; /* use composite device to cope with multiple batteries */ device = g_object_ref (engine_get_composite_device (manager, device_tmp)); break; } return device; } static void gnome_session_shutdown_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GVariant *result; GError *error = NULL; result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); if (result == NULL) { g_warning ("couldn't shutdown using gnome-session: %s", error->message); g_error_free (error); } else { g_variant_unref (result); } } static void gnome_session_shutdown (GsdPowerManager *manager) { g_dbus_proxy_call (G_DBUS_PROXY (manager->priv->session), "Shutdown", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, gnome_session_shutdown_cb, NULL); } static void gnome_session_logout_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GVariant *result; GError *error = NULL; result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); if (result == NULL) { g_warning ("couldn't log out using gnome-session: %s", error->message); g_error_free (error); } else { g_variant_unref (result); } } static void gnome_session_logout (GsdPowerManager *manager, guint logout_mode) { g_dbus_proxy_call (G_DBUS_PROXY (manager->priv->session), "Logout", g_variant_new ("(u)", logout_mode), G_DBUS_CALL_FLAGS_NONE, -1, NULL, gnome_session_logout_cb, NULL); } static void action_poweroff (GsdPowerManager *manager) { if (manager->priv->logind_proxy == NULL) { g_warning ("no systemd support"); return; } g_dbus_proxy_call (manager->priv->logind_proxy, "PowerOff", g_variant_new ("(b)", FALSE), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, NULL, NULL); } static void action_suspend (GsdPowerManager *manager) { if (manager->priv->logind_proxy == NULL) { g_warning ("no systemd support"); return; } g_dbus_proxy_call (manager->priv->logind_proxy, "Suspend", g_variant_new ("(b)", FALSE), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, NULL, NULL); } static void action_hibernate (GsdPowerManager *manager) { if (manager->priv->logind_proxy == NULL) { g_warning ("no systemd support"); return; } g_dbus_proxy_call (manager->priv->logind_proxy, "Hibernate", g_variant_new ("(b)", FALSE), G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, NULL, NULL); } static void backlight_enable (GsdPowerManager *manager) { gboolean ret; GError *error = NULL; ret = gsd_rr_screen_set_dpms_mode (manager->priv->rr_screen, GSD_RR_DPMS_ON, &error); if (!ret) { g_warning ("failed to turn the panel on: %s", error->message); g_error_free (error); } g_debug ("TESTSUITE: Unblanked screen"); } static void backlight_disable (GsdPowerManager *manager) { gboolean ret; GError *error = NULL; ret = gsd_rr_screen_set_dpms_mode (manager->priv->rr_screen, GSD_RR_DPMS_OFF, &error); if (!ret) { g_warning ("failed to turn the panel off: %s", error->message); g_error_free (error); } g_debug ("TESTSUITE: Blanked screen"); } static void do_power_action_type (GsdPowerManager *manager, GsdPowerActionType action_type) { switch (action_type) { case GSD_POWER_ACTION_SUSPEND: action_suspend (manager); break; case GSD_POWER_ACTION_INTERACTIVE: gnome_session_shutdown (manager); break; case GSD_POWER_ACTION_HIBERNATE: action_hibernate (manager); break; case GSD_POWER_ACTION_SHUTDOWN: /* this is only used on critically low battery where * hibernate is not available and is marginally better * than just powering down the computer mid-write */ action_poweroff (manager); break; case GSD_POWER_ACTION_BLANK: backlight_disable (manager); break; case GSD_POWER_ACTION_NOTHING: break; case GSD_POWER_ACTION_LOGOUT: gnome_session_logout (manager, GSM_MANAGER_LOGOUT_MODE_FORCE); break; } } static GsmInhibitorFlag get_idle_inhibitors_for_action (GsdPowerActionType action_type) { switch (action_type) { case GSD_POWER_ACTION_BLANK: case GSD_POWER_ACTION_SHUTDOWN: case GSD_POWER_ACTION_INTERACTIVE: return GSM_INHIBITOR_FLAG_IDLE; case GSD_POWER_ACTION_HIBERNATE: case GSD_POWER_ACTION_SUSPEND: return GSM_INHIBITOR_FLAG_SUSPEND; /* in addition to idle */ case GSD_POWER_ACTION_NOTHING: return 0; case GSD_POWER_ACTION_LOGOUT: return GSM_INHIBITOR_FLAG_LOGOUT; /* in addition to idle */ } return 0; } static gboolean is_action_inhibited (GsdPowerManager *manager, GsdPowerActionType action_type) { GsmInhibitorFlag flag; gboolean is_inhibited; flag = get_idle_inhibitors_for_action (action_type); if (!flag) return FALSE; idle_is_session_inhibited (manager, flag, &is_inhibited); return is_inhibited; } static gboolean upower_kbd_get_brightness (GsdPowerManager *manager) { GVariant *k_now = NULL; GError *error = NULL; gint now; k_now = g_dbus_proxy_call_sync (manager->priv->upower_kdb_proxy, "GetBrightness", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (k_now == NULL) { if (error->domain != G_DBUS_ERROR || error->code != G_DBUS_ERROR_UNKNOWN_METHOD) { g_warning ("Failed to get brightness: %s", error->message); } g_error_free (error); return -1; } g_variant_get (k_now, "(i)", &now); g_variant_unref (k_now); return now; } static gboolean upower_kbd_set_brightness (GsdPowerManager *manager, guint value, GError **error) { GVariant *retval; /* update h/w value */ retval = g_dbus_proxy_call_sync (manager->priv->upower_kdb_proxy, "SetBrightness", g_variant_new ("(i)", (gint) value), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); if (retval == NULL) return FALSE; g_variant_unref (retval); return TRUE; } static int upower_kbd_toggle (GsdPowerManager *manager, GError **error) { gboolean ret; int value = -1; if (manager->priv->kbd_brightness_old >= 0) { g_debug ("keyboard toggle off"); ret = upower_kbd_set_brightness (manager, manager->priv->kbd_brightness_old, error); if (ret) { /* succeeded, set to -1 since now no old value */ manager->priv->kbd_brightness_old = -1; value = 0; } } else { g_debug ("keyboard toggle on"); /* save the current value to restore later when untoggling */ manager->priv->kbd_brightness_old = upower_kbd_get_brightness (manager); ret = upower_kbd_set_brightness (manager, 0, error); if (!ret) { /* failed, reset back to -1 */ manager->priv->kbd_brightness_old = -1; } else { value = 0; } } if (ret) return value; return -1; } static gboolean suspend_on_lid_close (GsdPowerManager *manager) { GsdXrandrBootBehaviour val; if (manager->priv->inhibit_lid_switch_action) return FALSE; if (!external_monitor_is_connected (manager->priv->rr_screen)) return TRUE; val = g_settings_get_enum (manager->priv->settings_xrandr, "default-monitors-setup"); return val == GSD_XRANDR_BOOT_BEHAVIOUR_DO_NOTHING; } static gboolean inhibit_lid_switch_timer_cb (GsdPowerManager *manager) { if (suspend_on_lid_close (manager)) { g_debug ("no external monitors for a while; uninhibiting lid close"); uninhibit_lid_switch (manager); manager->priv->inhibit_lid_switch_timer_id = 0; return G_SOURCE_REMOVE; } g_debug ("external monitor still there; trying again later"); return G_SOURCE_CONTINUE; } /* Sets up a timer to be triggered some seconds after closing the laptop lid * when the laptop is *not* suspended for some reason. We'll check conditions * again in the timeout handler to see if we can suspend then. */ static void setup_inhibit_lid_switch_timer (GsdPowerManager *manager) { if (manager->priv->inhibit_lid_switch_timer_id != 0) { g_debug ("lid close safety timer already set up"); return; } g_debug ("setting up lid close safety timer"); manager->priv->inhibit_lid_switch_timer_id = g_timeout_add_seconds (GSD_POWER_MANAGER_LID_CLOSE_SAFETY_TIMEOUT, (GSourceFunc) inhibit_lid_switch_timer_cb, manager); g_source_set_name_by_id (manager->priv->inhibit_lid_switch_timer_id, "[GsdPowerManager] lid close safety timer"); } static void restart_inhibit_lid_switch_timer (GsdPowerManager *manager) { if (manager->priv->inhibit_lid_switch_timer_id != 0) { g_debug ("restarting lid close safety timer"); g_source_remove (manager->priv->inhibit_lid_switch_timer_id); manager->priv->inhibit_lid_switch_timer_id = 0; setup_inhibit_lid_switch_timer (manager); } } static void setup_lid_closed_action (GsdPowerManager *manager) { GsdPowerActionType policy; if (up_client_get_on_battery (manager->priv->up_client)) { policy = g_settings_get_enum (manager->priv->settings, "lid-close-battery-action"); } else { policy = g_settings_get_enum (manager->priv->settings, "lid-close-ac-action"); } if (policy == GSD_POWER_ACTION_NOTHING) { inhibit_lid_switch (manager); manager->priv->inhibit_lid_switch_action = TRUE; } else { uninhibit_lid_switch (manager); manager->priv->inhibit_lid_switch_action = FALSE; } } static void do_lid_open_action (GsdPowerManager *manager) { /* play a sound, using sounds from the naming spec */ ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "lid-open", /* TRANSLATORS: this is the sound description */ CA_PROP_EVENT_DESCRIPTION, _("Lid has been opened"), NULL); /* This might already have happened when resuming, but * if we didn't sleep, we'll need to wake it up */ reset_idletime (); } static void lock_screensaver (GsdPowerManager *manager) { gboolean do_lock; do_lock = g_settings_get_boolean (manager->priv->settings_screensaver, "lock-enabled"); if (!do_lock) { g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->screensaver_proxy), "SetActive", g_variant_new ("(b)", TRUE), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); return; } g_dbus_proxy_call_sync (G_DBUS_PROXY (manager->priv->screensaver_proxy), "Lock", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); } static void do_lid_closed_action (GsdPowerManager *manager) { /* play a sound, using sounds from the naming spec */ ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "lid-close", /* TRANSLATORS: this is the sound description */ CA_PROP_EVENT_DESCRIPTION, _("Lid has been closed"), NULL); /* refresh RANDR so we get an accurate view of what monitors are plugged in when the lid is closed */ gsd_rr_screen_refresh (manager->priv->rr_screen, NULL); /* NULL-GError */ restart_inhibit_lid_switch_timer (manager); if (suspend_on_lid_close (manager)) { gboolean is_inhibited; idle_is_session_inhibited (manager, GSM_INHIBITOR_FLAG_SUSPEND, &is_inhibited); if (is_inhibited) { g_debug ("Suspend is inhibited but lid is closed, locking the screen"); /* We put the screensaver on * as we're not suspending, * but the lid is closed */ lock_screensaver (manager); } } else { if (manager->priv->inhibit_lid_switch_action) lock_screensaver (manager); } } static void lid_state_changed_cb (UpClient *client, GParamSpec *pspec, GsdPowerManager *manager) { gboolean tmp; if (!up_client_get_on_battery (client)) { /* if we are playing a critical charge sound loop on AC, stop it */ play_loop_stop (&manager->priv->critical_alert_timeout_id); notify_close_if_showing (&manager->priv->notification_low); main_battery_or_ups_low_changed (manager, FALSE); } setup_lid_closed_action (manager); /* same state */ tmp = up_client_get_lid_is_closed (manager->priv->up_client); if (manager->priv->lid_is_closed == tmp) return; manager->priv->lid_is_closed = tmp; g_debug ("up changed: lid is now %s", tmp ? "closed" : "open"); if (manager->priv->lid_is_closed) do_lid_closed_action (manager); else do_lid_open_action (manager); } static const gchar * idle_mode_to_string (GsdPowerIdleMode mode) { if (mode == GSD_POWER_IDLE_MODE_NORMAL) return "normal"; if (mode == GSD_POWER_IDLE_MODE_DIM) return "dim"; if (mode == GSD_POWER_IDLE_MODE_BLANK) return "blank"; if (mode == GSD_POWER_IDLE_MODE_SLEEP) return "sleep"; return "unknown"; } static const char * idle_watch_id_to_string (GsdPowerManager *manager, guint id) { if (id == manager->priv->idle_dim_id) return "dim"; if (id == manager->priv->idle_blank_id) return "blank"; if (id == manager->priv->idle_sleep_id) return "sleep"; if (id == manager->priv->idle_sleep_warning_id) return "sleep-warning"; return NULL; } static void backlight_emit_changed (GsdPowerManager *manager) { gboolean ret; GError *error = NULL; /* not yet connected to the bus */ if (manager->priv->connection == NULL) return; ret = g_dbus_connection_emit_signal (manager->priv->connection, NULL, GSD_POWER_DBUS_PATH, GSD_POWER_DBUS_INTERFACE_SCREEN, "Changed", NULL, &error); if (!ret) { g_warning ("failed to emit Changed: %s", error->message); g_error_free (error); } } static gboolean display_backlight_dim (GsdPowerManager *manager, gint idle_percentage, GError **error) { gint min; gint max; gint now; gint idle; gboolean ret = FALSE; if (!manager->priv->backlight_available) return TRUE; now = backlight_get_abs (manager->priv->rr_screen, error); if (now < 0) { goto out; } /* is the dim brightness actually *dimmer* than the * brightness we have now? */ min = backlight_get_min (manager->priv->rr_screen); max = backlight_get_max (manager->priv->rr_screen, error); if (max < 0) { goto out; } idle = PERCENTAGE_TO_ABS (min, max, idle_percentage); if (idle > now) { g_debug ("brightness already now %i/%i, so " "ignoring dim to %i/%i", now, max, idle, max); ret = TRUE; goto out; } ret = backlight_set_abs (manager->priv->rr_screen, idle, error); if (!ret) { goto out; } /* save for undim */ manager->priv->pre_dim_brightness = now; out: return ret; } static gboolean kbd_backlight_dim (GsdPowerManager *manager, gint idle_percentage, GError **error) { gboolean ret; gint idle; gint max; gint now; if (manager->priv->upower_kdb_proxy == NULL) return TRUE; now = upower_kbd_get_brightness (manager); max = manager->priv->kbd_brightness_max; idle = PERCENTAGE_TO_ABS (0, max, idle_percentage); if (idle > now) { g_debug ("kbd brightness already now %i/%i, so " "ignoring dim to %i/%i", now, max, idle, max); return TRUE; } ret = upower_kbd_set_brightness (manager, idle, error); if (!ret) return FALSE; /* save for undim */ manager->priv->kbd_brightness_pre_dim = now; return TRUE; } static gboolean is_session_active (GsdPowerManager *manager) { GVariant *variant; gboolean is_session_active = FALSE; variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (manager->priv->session), "SessionIsActive"); if (variant) { is_session_active = g_variant_get_boolean (variant); g_variant_unref (variant); } return is_session_active; } static void idle_set_mode (GsdPowerManager *manager, GsdPowerIdleMode mode) { gboolean ret = FALSE; GError *error = NULL; gint idle_percentage; GsdPowerActionType action_type; gboolean is_active = FALSE; /* Ignore attempts to set "less idle" modes */ if (mode <= manager->priv->current_idle_mode && mode != GSD_POWER_IDLE_MODE_NORMAL) { g_debug ("Not going to 'less idle' mode %s (current: %s)", idle_mode_to_string (mode), idle_mode_to_string (manager->priv->current_idle_mode)); return; } /* ensure we're still on an active console */ is_active = is_session_active (manager); if (!is_active) { g_debug ("ignoring state transition to %s as inactive", idle_mode_to_string (mode)); return; } /* don't do any power saving if we're a VM */ if (manager->priv->is_virtual_machine) { g_debug ("ignoring state transition to %s as virtual machine", idle_mode_to_string (mode)); return; } manager->priv->current_idle_mode = mode; g_debug ("Doing a state transition: %s", idle_mode_to_string (mode)); /* if we're moving to an idle mode, make sure * we add a watch to take us back to normal */ if (mode != GSD_POWER_IDLE_MODE_NORMAL) { gsd_idle_monitor_add_user_active_watch (manager->priv->idle_monitor, idle_became_active_cb, manager, NULL); } /* save current brightness, and set dim level */ if (mode == GSD_POWER_IDLE_MODE_DIM) { /* display backlight */ idle_percentage = g_settings_get_int (manager->priv->settings, "idle-brightness"); ret = display_backlight_dim (manager, idle_percentage, &error); if (!ret) { g_warning ("failed to set dim backlight to %i%%: %s", idle_percentage, error->message); g_clear_error (&error); } /* keyboard backlight */ ret = kbd_backlight_dim (manager, idle_percentage, &error); if (!ret) { g_warning ("failed to set dim kbd backlight to %i%%: %s", idle_percentage, error->message); g_clear_error (&error); } /* turn off screen and kbd */ } else if (mode == GSD_POWER_IDLE_MODE_BLANK) { backlight_disable (manager); /* only toggle keyboard if present and not already toggled */ if (manager->priv->upower_kdb_proxy && manager->priv->kbd_brightness_old == -1) { if (upower_kbd_toggle (manager, &error) < 0) { g_warning ("failed to turn the kbd backlight off: %s", error->message); g_error_free (error); } } /* sleep */ } else if (mode == GSD_POWER_IDLE_MODE_SLEEP) { if (up_client_get_on_battery (manager->priv->up_client)) { action_type = g_settings_get_enum (manager->priv->settings, "sleep-inactive-battery-type"); } else { action_type = g_settings_get_enum (manager->priv->settings, "sleep-inactive-ac-type"); } do_power_action_type (manager, action_type); /* turn on screen and restore user-selected brightness level */ } else if (mode == GSD_POWER_IDLE_MODE_NORMAL) { backlight_enable (manager); /* reset brightness if we dimmed */ if (manager->priv->pre_dim_brightness >= 0) { ret = backlight_set_abs (manager->priv->rr_screen, manager->priv->pre_dim_brightness, &error); if (!ret) { g_warning ("failed to restore backlight to %i: %s", manager->priv->pre_dim_brightness, error->message); g_clear_error (&error); } else { manager->priv->pre_dim_brightness = -1; } } /* only toggle keyboard if present and already toggled off */ if (manager->priv->upower_kdb_proxy && manager->priv->kbd_brightness_old != -1) { if (upower_kbd_toggle (manager, &error) < 0) { g_warning ("failed to turn the kbd backlight on: %s", error->message); g_clear_error (&error); } } /* reset kbd brightness if we dimmed */ if (manager->priv->kbd_brightness_pre_dim >= 0) { ret = upower_kbd_set_brightness (manager, manager->priv->kbd_brightness_pre_dim, &error); if (!ret) { g_warning ("failed to restore kbd backlight to %i: %s", manager->priv->kbd_brightness_pre_dim, error->message); g_error_free (error); } manager->priv->kbd_brightness_pre_dim = -1; } } } static gboolean idle_is_session_inhibited (GsdPowerManager *manager, GsmInhibitorFlag mask, gboolean *is_inhibited) { GVariant *variant; GsmInhibitorFlag inhibited_actions; /* not yet connected to gnome-session */ if (manager->priv->session == NULL) return FALSE; variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (manager->priv->session), "InhibitedActions"); if (!variant) return FALSE; inhibited_actions = g_variant_get_uint32 (variant); g_variant_unref (variant); *is_inhibited = (inhibited_actions & mask); return TRUE; } static void clear_idle_watch (GsdIdleMonitor *monitor, guint *id) { if (*id == 0) return; gsd_idle_monitor_remove_watch (monitor, *id); *id = 0; } static void idle_configure (GsdPowerManager *manager) { gboolean is_idle_inhibited; GsdPowerActionType action_type; guint timeout_blank; guint timeout_sleep; guint timeout_dim; gboolean on_battery; if (!idle_is_session_inhibited (manager, GSM_INHIBITOR_FLAG_IDLE, &is_idle_inhibited)) { /* Session isn't available yet, postpone */ return; } /* are we inhibited from going idle */ if (!is_session_active (manager) || is_idle_inhibited) { g_debug ("inhibited or inactive, so using normal state"); idle_set_mode (manager, GSD_POWER_IDLE_MODE_NORMAL); clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_blank_id); clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_sleep_id); clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_dim_id); clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_sleep_warning_id); notify_close_if_showing (&manager->priv->notification_sleep_warning); return; } /* set up blank callback only when the screensaver is on, * as it's what will drive the blank */ on_battery = up_client_get_on_battery (manager->priv->up_client); timeout_blank = 0; if (manager->priv->screensaver_active) { /* The tail is wagging the dog. * The screensaver coming on will blank the screen. * If an event occurs while the screensaver is on, * the aggressive idle watch will handle it */ timeout_blank = SCREENSAVER_TIMEOUT_BLANK; } clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_blank_id); if (timeout_blank != 0) { g_debug ("setting up blank callback for %is", timeout_blank); manager->priv->idle_blank_id = gsd_idle_monitor_add_idle_watch (manager->priv->idle_monitor, timeout_blank * 1000, idle_triggered_idle_cb, manager, NULL); } /* only do the sleep timeout when the session is idle * and we aren't inhibited from sleeping (or logging out, etc.) */ action_type = g_settings_get_enum (manager->priv->settings, on_battery ? "sleep-inactive-battery-type" : "sleep-inactive-ac-type"); timeout_sleep = 0; if (!is_action_inhibited (manager, action_type)) { timeout_sleep = g_settings_get_int (manager->priv->settings, on_battery ? "sleep-inactive-battery-timeout" : "sleep-inactive-ac-timeout"); } clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_sleep_id); clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_sleep_warning_id); if (timeout_sleep != 0) { g_debug ("setting up sleep callback %is", timeout_sleep); manager->priv->idle_sleep_id = gsd_idle_monitor_add_idle_watch (manager->priv->idle_monitor, timeout_sleep * 1000, idle_triggered_idle_cb, manager, NULL); if (action_type == GSD_POWER_ACTION_LOGOUT || action_type == GSD_POWER_ACTION_SUSPEND || action_type == GSD_POWER_ACTION_HIBERNATE) { guint timeout_sleep_warning; manager->priv->sleep_action_type = action_type; timeout_sleep_warning = timeout_sleep * IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER; if (timeout_sleep_warning < MINIMUM_IDLE_DIM_DELAY) timeout_sleep_warning = 0; g_debug ("setting up sleep warning callback %is", timeout_sleep_warning); manager->priv->idle_sleep_warning_id = gsd_idle_monitor_add_idle_watch (manager->priv->idle_monitor, timeout_sleep_warning * 1000, idle_triggered_idle_cb, manager, NULL); } } if (manager->priv->idle_sleep_warning_id == 0) notify_close_if_showing (&manager->priv->notification_sleep_warning); /* set up dim callback for when the screen lock is not active, * but only if we actually want to dim. */ timeout_dim = 0; if (manager->priv->screensaver_active) { /* Don't dim when the screen lock is active */ } else if (!on_battery) { /* Don't dim when charging */ } else if (manager->priv->battery_is_low) { /* Aggressively blank when battery is low */ timeout_dim = SCREENSAVER_TIMEOUT_BLANK; } else { if (g_settings_get_boolean (manager->priv->settings, "idle-dim")) { timeout_dim = g_settings_get_uint (manager->priv->settings_bus, "idle-delay"); if (timeout_dim == 0) { timeout_dim = IDLE_DIM_BLANK_DISABLED_MIN; } else { timeout_dim *= IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER; /* Don't bother dimming if the idle-delay is * too low, we'll do that when we bring down the * screen lock */ if (timeout_dim < MINIMUM_IDLE_DIM_DELAY) timeout_dim = 0; } } } clear_idle_watch (manager->priv->idle_monitor, &manager->priv->idle_dim_id); if (timeout_dim != 0) { g_debug ("setting up dim callback for %is", timeout_dim); manager->priv->idle_dim_id = gsd_idle_monitor_add_idle_watch (manager->priv->idle_monitor, timeout_dim * 1000, idle_triggered_idle_cb, manager, NULL); } } static void main_battery_or_ups_low_changed (GsdPowerManager *manager, gboolean is_low) { if (is_low == manager->priv->battery_is_low) return; manager->priv->battery_is_low = is_low; idle_configure (manager); } static gboolean temporary_unidle_done_cb (GsdPowerManager *manager) { idle_set_mode (manager, manager->priv->previous_idle_mode); manager->priv->temporary_unidle_on_ac_id = 0; return FALSE; } static void set_temporary_unidle_on_ac (GsdPowerManager *manager, gboolean enable) { if (!enable) { if (manager->priv->temporary_unidle_on_ac_id != 0) { g_source_remove (manager->priv->temporary_unidle_on_ac_id); manager->priv->temporary_unidle_on_ac_id = 0; idle_set_mode (manager, manager->priv->previous_idle_mode); } } else { /* Don't overwrite the previous idle mode when an unidle is * already on-going */ if (manager->priv->temporary_unidle_on_ac_id != 0) { g_source_remove (manager->priv->temporary_unidle_on_ac_id); } else { manager->priv->previous_idle_mode = manager->priv->current_idle_mode; idle_set_mode (manager, GSD_POWER_IDLE_MODE_NORMAL); } manager->priv->temporary_unidle_on_ac_id = g_timeout_add_seconds (POWER_UP_TIME_ON_AC, (GSourceFunc) temporary_unidle_done_cb, manager); } } static void up_client_on_battery_cb (UpClient *client, GParamSpec *pspec, GsdPowerManager *manager) { idle_configure (manager); if (manager->priv->lid_is_closed) return; if (manager->priv->current_idle_mode == GSD_POWER_IDLE_MODE_BLANK || manager->priv->current_idle_mode == GSD_POWER_IDLE_MODE_DIM || manager->priv->temporary_unidle_on_ac_id != 0) set_temporary_unidle_on_ac (manager, TRUE); } static void gsd_power_manager_finalize (GObject *object) { GsdPowerManager *manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_POWER_MANAGER (object)); manager = GSD_POWER_MANAGER (object); g_return_if_fail (manager->priv != NULL); g_clear_object (&manager->priv->connection); if (manager->priv->name_id != 0) g_bus_unown_name (manager->priv->name_id); G_OBJECT_CLASS (gsd_power_manager_parent_class)->finalize (object); } static void gsd_power_manager_class_init (GsdPowerManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_power_manager_finalize; g_type_class_add_private (klass, sizeof (GsdPowerManagerPrivate)); } static void session_presence_proxy_ready_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); manager->priv->session_presence_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (manager->priv->session_presence_proxy == NULL) { g_warning ("Could not connect to gnome-sesson: %s", error->message); g_error_free (error); return; } } static void handle_screensaver_active (GsdPowerManager *manager, GVariant *parameters) { gboolean active; g_variant_get (parameters, "(b)", &active); g_debug ("Received screensaver ActiveChanged signal: %d (old: %d)", active, manager->priv->screensaver_active); if (manager->priv->screensaver_active != active) { manager->priv->screensaver_active = active; idle_configure (manager); /* Setup blank as soon as the screensaver comes on, * and its fade has finished. * * See also idle_configure() */ if (active) idle_set_mode (manager, GSD_POWER_IDLE_MODE_BLANK); } } static void screensaver_signal_cb (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { if (g_strcmp0 (signal_name, "ActiveChanged") == 0) handle_screensaver_active (GSD_POWER_MANAGER (user_data), parameters); } static void power_keyboard_proxy_ready_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GVariant *k_max = NULL; GError *error = NULL; GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); manager->priv->upower_kdb_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (manager->priv->upower_kdb_proxy == NULL) { g_warning ("Could not connect to UPower: %s", error->message); g_error_free (error); goto out; } k_max = g_dbus_proxy_call_sync (manager->priv->upower_kdb_proxy, "GetMaxBrightness", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (k_max == NULL) { if (error->domain != G_DBUS_ERROR || error->code != G_DBUS_ERROR_UNKNOWN_METHOD) { g_warning ("Failed to get max brightness: %s", error->message); } g_error_free (error); goto out; } g_variant_get (k_max, "(i)", &manager->priv->kbd_brightness_max); /* set brightness to max if not currently set so is something * sensible */ if (upower_kbd_get_brightness (manager) < 0) { gboolean ret; ret = upower_kbd_set_brightness (manager, manager->priv->kbd_brightness_max, &error); if (!ret) { g_warning ("failed to initialize kbd backlight to %i: %s", manager->priv->kbd_brightness_max, error->message); g_error_free (error); } } out: if (k_max != NULL) g_variant_unref (k_max); } static void show_sleep_warning (GsdPowerManager *manager) { /* close any existing notification of this class */ notify_close_if_showing (&manager->priv->notification_sleep_warning); /* create a new notification */ switch (manager->priv->sleep_action_type) { case GSD_POWER_ACTION_LOGOUT: create_notification (_("Automatic logout"), _("You will soon log out because of inactivity."), NULL, &manager->priv->notification_sleep_warning); break; case GSD_POWER_ACTION_SUSPEND: create_notification (_("Automatic suspend"), _("Computer will suspend very soon because of inactivity."), NULL, &manager->priv->notification_sleep_warning); break; case GSD_POWER_ACTION_HIBERNATE: create_notification (_("Automatic hibernation"), _("Computer will suspend very soon because of inactivity."), NULL, &manager->priv->notification_sleep_warning); break; default: g_assert_not_reached (); break; } notify_notification_set_timeout (manager->priv->notification_sleep_warning, NOTIFY_EXPIRES_DEFAULT); notify_notification_set_urgency (manager->priv->notification_sleep_warning, NOTIFY_URGENCY_CRITICAL); notify_notification_set_app_name (manager->priv->notification_sleep_warning, _("Power")); notify_notification_show (manager->priv->notification_sleep_warning, NULL); if (manager->priv->sleep_action_type == GSD_POWER_ACTION_LOGOUT) set_temporary_unidle_on_ac (manager, TRUE); } static void idle_triggered_idle_cb (GsdIdleMonitor *monitor, guint watch_id, gpointer user_data) { GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); const char *id_name; id_name = idle_watch_id_to_string (manager, watch_id); if (id_name == NULL) g_debug ("idletime watch: %i", watch_id); else g_debug ("idletime watch: %s (%i)", id_name, watch_id); if (watch_id == manager->priv->idle_dim_id) { idle_set_mode (manager, GSD_POWER_IDLE_MODE_DIM); } else if (watch_id == manager->priv->idle_blank_id) { idle_set_mode (manager, GSD_POWER_IDLE_MODE_BLANK); } else if (watch_id == manager->priv->idle_sleep_id) { idle_set_mode (manager, GSD_POWER_IDLE_MODE_SLEEP); } else if (watch_id == manager->priv->idle_sleep_warning_id) { show_sleep_warning (manager); } } static void idle_became_active_cb (GsdIdleMonitor *monitor, guint watch_id, gpointer user_data) { GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); g_debug ("idletime reset"); set_temporary_unidle_on_ac (manager, FALSE); /* close any existing notification about idleness */ notify_close_if_showing (&manager->priv->notification_sleep_warning); idle_set_mode (manager, GSD_POWER_IDLE_MODE_NORMAL); } static void engine_settings_key_changed_cb (GSettings *settings, const gchar *key, GsdPowerManager *manager) { if (g_strcmp0 (key, "use-time-for-policy") == 0) { manager->priv->use_time_primary = g_settings_get_boolean (settings, key); return; } if (g_str_has_prefix (key, "sleep-inactive") || g_str_equal (key, "idle-delay") || g_str_equal (key, "idle-dim")) { idle_configure (manager); return; } if (g_str_has_prefix (key, "lid-close")) { setup_lid_closed_action (manager); return; } } static void engine_session_properties_changed_cb (GDBusProxy *session, GVariant *changed, char **invalidated, GsdPowerManager *manager) { GVariant *v; v = g_variant_lookup_value (changed, "SessionIsActive", G_VARIANT_TYPE_BOOLEAN); if (v) { gboolean active; active = g_variant_get_boolean (v); g_debug ("Received session is active change: now %s", active ? "active" : "inactive"); /* when doing the fast-user-switch into a new account, * ensure the new account is undimmed and with the backlight on */ if (active) idle_set_mode (manager, GSD_POWER_IDLE_MODE_NORMAL); g_variant_unref (v); } v = g_variant_lookup_value (changed, "InhibitedActions", G_VARIANT_TYPE_UINT32); if (v) { g_variant_unref (v); g_debug ("Received gnome session inhibitor change"); idle_configure (manager); } } static void inhibit_lid_switch_done (GObject *source, GAsyncResult *result, gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY (source); GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); GError *error = NULL; GVariant *res; GUnixFDList *fd_list = NULL; gint idx; res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error); if (res == NULL) { g_warning ("Unable to inhibit lid switch: %s", error->message); g_error_free (error); } else { g_variant_get (res, "(h)", &idx); manager->priv->inhibit_lid_switch_fd = g_unix_fd_list_get (fd_list, idx, &error); if (manager->priv->inhibit_lid_switch_fd == -1) { g_warning ("Failed to receive system inhibitor fd: %s", error->message); g_error_free (error); } g_debug ("System inhibitor fd is %d", manager->priv->inhibit_lid_switch_fd); g_object_unref (fd_list); g_variant_unref (res); } } static void inhibit_lid_switch (GsdPowerManager *manager) { GVariant *params; if (manager->priv->inhibit_lid_switch_taken) { g_debug ("already inhibited lid-switch"); return; } g_debug ("Adding lid switch system inhibitor"); manager->priv->inhibit_lid_switch_taken = TRUE; params = g_variant_new ("(ssss)", "handle-lid-switch", g_get_user_name (), "Multiple displays attached", "block"); g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy, "Inhibit", params, 0, G_MAXINT, NULL, NULL, inhibit_lid_switch_done, manager); } static void uninhibit_lid_switch (GsdPowerManager *manager) { if (manager->priv->inhibit_lid_switch_fd == -1) { g_debug ("no lid-switch inhibitor"); return; } g_debug ("Removing lid switch system inhibitor"); close (manager->priv->inhibit_lid_switch_fd); manager->priv->inhibit_lid_switch_fd = -1; manager->priv->inhibit_lid_switch_taken = FALSE; } static void inhibit_suspend_done (GObject *source, GAsyncResult *result, gpointer user_data) { GDBusProxy *proxy = G_DBUS_PROXY (source); GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); GError *error = NULL; GVariant *res; GUnixFDList *fd_list = NULL; gint idx; res = g_dbus_proxy_call_with_unix_fd_list_finish (proxy, &fd_list, result, &error); if (res == NULL) { g_warning ("Unable to inhibit suspend: %s", error->message); g_error_free (error); } else { g_variant_get (res, "(h)", &idx); manager->priv->inhibit_suspend_fd = g_unix_fd_list_get (fd_list, idx, &error); if (manager->priv->inhibit_suspend_fd == -1) { g_warning ("Failed to receive system inhibitor fd: %s", error->message); g_error_free (error); } g_debug ("System inhibitor fd is %d", manager->priv->inhibit_suspend_fd); g_object_unref (fd_list); g_variant_unref (res); } } /* We take a delay inhibitor here, which causes logind to send a * PrepareForSleep signal, which gives us a chance to lock the screen * and do some other preparations. */ static void inhibit_suspend (GsdPowerManager *manager) { if (manager->priv->inhibit_suspend_taken) { g_debug ("already inhibited lid-switch"); return; } g_debug ("Adding suspend delay inhibitor"); manager->priv->inhibit_suspend_taken = TRUE; g_dbus_proxy_call_with_unix_fd_list (manager->priv->logind_proxy, "Inhibit", g_variant_new ("(ssss)", "sleep", g_get_user_name (), "GNOME needs to lock the screen", "delay"), 0, G_MAXINT, NULL, NULL, inhibit_suspend_done, manager); } static void uninhibit_suspend (GsdPowerManager *manager) { if (manager->priv->inhibit_suspend_fd == -1) { g_debug ("no suspend delay inhibitor"); return; } g_debug ("Removing suspend delay inhibitor"); close (manager->priv->inhibit_suspend_fd); manager->priv->inhibit_suspend_fd = -1; manager->priv->inhibit_suspend_taken = FALSE; } static void on_randr_event (GsdRRScreen *screen, gpointer user_data) { GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); if (suspend_on_lid_close (manager)) { restart_inhibit_lid_switch_timer (manager); return; } /* when a second monitor is plugged in, we take the * handle-lid-switch inhibitor lock of logind to prevent * it from suspending. * * Uninhibiting is done in the inhibit_lid_switch_timer, * since we want to give users a few seconds when unplugging * and replugging an external monitor, not suspend right away. */ inhibit_lid_switch (manager); setup_inhibit_lid_switch_timer (manager); } #ifdef GSD_MOCK static gboolean received_sigusr2 (GsdPowerManager *manager) { on_randr_event (NULL, manager); return TRUE; } #endif /* GSD_MOCK */ static void handle_suspend_actions (GsdPowerManager *manager) { backlight_disable (manager); uninhibit_suspend (manager); } static void handle_resume_actions (GsdPowerManager *manager) { /* close existing notifications on resume, the system power * state is probably different now */ notify_close_if_showing (&manager->priv->notification_low); notify_close_if_showing (&manager->priv->notification_ups_discharging); main_battery_or_ups_low_changed (manager, FALSE); /* ensure we turn the panel back on after resume */ backlight_enable (manager); /* And work-around Xorg bug: * https://bugs.freedesktop.org/show_bug.cgi?id=59576 */ reset_idletime (); /* set up the delay again */ inhibit_suspend (manager); } static void logind_proxy_signal_cb (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); gboolean is_about_to_suspend; if (g_strcmp0 (signal_name, "PrepareForSleep") != 0) return; g_variant_get (parameters, "(b)", &is_about_to_suspend); if (is_about_to_suspend) { handle_suspend_actions (manager); } else { handle_resume_actions (manager); } } gboolean gsd_power_manager_start (GsdPowerManager *manager, GError **error) { g_debug ("Starting power manager"); gnome_settings_profile_start (NULL); /* coldplug the list of screens */ manager->priv->rr_screen = gsd_rr_screen_new (gdk_screen_get_default (), error); if (manager->priv->rr_screen == NULL) { g_debug ("Couldn't detect any screens, disabling plugin"); return FALSE; } /* Check for XTEST support */ if (supports_xtest () == FALSE) { g_debug ("XTEST extension required, disabling plugin"); return FALSE; } /* Set up the logind proxy */ manager->priv->logind_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL, SYSTEMD_DBUS_NAME, SYSTEMD_DBUS_PATH, SYSTEMD_DBUS_INTERFACE, NULL, error); if (manager->priv->logind_proxy == NULL) { g_debug ("No systemd (logind) support, disabling plugin"); return FALSE; } g_signal_connect (manager->priv->logind_proxy, "g-signal", G_CALLBACK (logind_proxy_signal_cb), manager); /* Set up a delay inhibitor to be informed about suspend attempts */ inhibit_suspend (manager); /* track the active session */ manager->priv->session = gnome_settings_bus_get_session_proxy (); g_signal_connect (manager->priv->session, "g-properties-changed", G_CALLBACK (engine_session_properties_changed_cb), manager); manager->priv->screensaver_proxy = gnome_settings_bus_get_screen_saver_proxy (); g_signal_connect (manager->priv->screensaver_proxy, "g-signal", G_CALLBACK (screensaver_signal_cb), manager); manager->priv->kbd_brightness_old = -1; manager->priv->kbd_brightness_pre_dim = -1; manager->priv->pre_dim_brightness = -1; manager->priv->settings = g_settings_new (GSD_POWER_SETTINGS_SCHEMA); g_signal_connect (manager->priv->settings, "changed", G_CALLBACK (engine_settings_key_changed_cb), manager); manager->priv->settings_screensaver = g_settings_new ("org.gnome.desktop.screensaver"); manager->priv->settings_bus = g_settings_new ("org.gnome.desktop.session"); g_signal_connect (manager->priv->settings_bus, "changed", G_CALLBACK (engine_settings_key_changed_cb), manager); manager->priv->settings_xrandr = g_settings_new (GSD_XRANDR_SETTINGS_SCHEMA); manager->priv->up_client = up_client_new (); manager->priv->lid_is_closed = up_client_get_lid_is_closed (manager->priv->up_client); g_signal_connect (manager->priv->up_client, "device-added", G_CALLBACK (engine_device_added_cb), manager); g_signal_connect (manager->priv->up_client, "device-removed", G_CALLBACK (engine_device_removed_cb), manager); g_signal_connect_after (manager->priv->up_client, "notify::lid-is-closed", G_CALLBACK (lid_state_changed_cb), manager); g_signal_connect (manager->priv->up_client, "notify::on-battery", G_CALLBACK (up_client_on_battery_cb), manager); g_signal_connect_after (manager->priv->up_client, "notify::on-battery", G_CALLBACK (lid_state_changed_cb), manager); /* connect to UPower for keyboard backlight control */ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, UPOWER_DBUS_NAME, UPOWER_DBUS_PATH_KBDBACKLIGHT, UPOWER_DBUS_INTERFACE_KBDBACKLIGHT, NULL, power_keyboard_proxy_ready_cb, manager); /* connect to the session */ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, 0, NULL, GNOME_SESSION_DBUS_NAME, GNOME_SESSION_DBUS_PATH_PRESENCE, GNOME_SESSION_DBUS_INTERFACE_PRESENCE, NULL, session_presence_proxy_ready_cb, manager); manager->priv->devices_array = g_ptr_array_new_with_free_func (g_object_unref); /* create a fake virtual composite battery */ manager->priv->device_composite = up_device_new (); g_object_set (manager->priv->device_composite, "kind", UP_DEVICE_KIND_BATTERY, "is-rechargeable", TRUE, "native-path", "dummy:composite_battery", "power-supply", TRUE, "is-present", TRUE, NULL); /* get percentage policy */ manager->priv->low_percentage = g_settings_get_int (manager->priv->settings, "percentage-low"); manager->priv->critical_percentage = g_settings_get_int (manager->priv->settings, "percentage-critical"); manager->priv->action_percentage = g_settings_get_int (manager->priv->settings, "percentage-action"); /* get time policy */ manager->priv->low_time = g_settings_get_int (manager->priv->settings, "time-low"); manager->priv->critical_time = g_settings_get_int (manager->priv->settings, "time-critical"); manager->priv->action_time = g_settings_get_int (manager->priv->settings, "time-action"); /* we can disable this if the time remaining is inaccurate or just plain wrong */ manager->priv->use_time_primary = g_settings_get_boolean (manager->priv->settings, "use-time-for-policy"); /* create IDLETIME watcher */ manager->priv->idle_monitor = g_object_ref (gsd_idle_monitor_get_core ()); /* set up the screens */ g_signal_connect (manager->priv->rr_screen, "changed", G_CALLBACK (on_randr_event), manager); on_randr_event (manager->priv->rr_screen, manager); #ifdef GSD_MOCK g_unix_signal_add (SIGUSR2, (GSourceFunc) received_sigusr2, manager); #endif /* GSD_MOCK */ /* check whether a backlight is available */ manager->priv->backlight_available = backlight_available (manager->priv->rr_screen); /* ensure the default dpms timeouts are cleared */ backlight_enable (manager); /* coldplug the engine */ engine_coldplug (manager); idle_configure (manager); manager->priv->xscreensaver_watchdog_timer_id = gsd_power_enable_screensaver_watchdog (); /* don't blank inside a VM */ manager->priv->is_virtual_machine = gsd_power_is_hardware_a_vm (); setup_lid_closed_action (manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_power_manager_stop (GsdPowerManager *manager) { GPtrArray *devices; int i; g_debug ("Stopping power manager"); if (manager->priv->inhibit_lid_switch_timer_id != 0) { g_source_remove (manager->priv->inhibit_lid_switch_timer_id); manager->priv->inhibit_lid_switch_timer_id = 0; } if (manager->priv->bus_cancellable != NULL) { g_cancellable_cancel (manager->priv->bus_cancellable); g_object_unref (manager->priv->bus_cancellable); manager->priv->bus_cancellable = NULL; } if (manager->priv->introspection_data) { g_dbus_node_info_unref (manager->priv->introspection_data); manager->priv->introspection_data = NULL; } g_signal_handlers_disconnect_by_data (manager->priv->up_client, manager); g_clear_object (&manager->priv->session); g_clear_object (&manager->priv->settings); g_clear_object (&manager->priv->settings_screensaver); g_clear_object (&manager->priv->settings_bus); g_clear_object (&manager->priv->up_client); if (manager->priv->inhibit_lid_switch_fd != -1) { close (manager->priv->inhibit_lid_switch_fd); manager->priv->inhibit_lid_switch_fd = -1; manager->priv->inhibit_lid_switch_taken = FALSE; manager->priv->inhibit_lid_switch_action = FALSE; } if (manager->priv->inhibit_suspend_fd != -1) { close (manager->priv->inhibit_suspend_fd); manager->priv->inhibit_suspend_fd = -1; manager->priv->inhibit_suspend_taken = FALSE; } g_clear_object (&manager->priv->logind_proxy); if (manager->priv->rr_screen) { g_signal_handlers_disconnect_by_data (manager->priv->rr_screen, manager); g_clear_object (&manager->priv->rr_screen); } devices = manager->priv->devices_array; for (i = 0; i < devices->len; i++) g_signal_handlers_disconnect_by_data (g_ptr_array_index (devices, i), manager); g_ptr_array_unref (devices); manager->priv->devices_array = NULL; g_clear_object (&manager->priv->device_composite); g_clear_object (&manager->priv->previous_icon); g_clear_pointer (&manager->priv->previous_summary, g_free); g_clear_object (&manager->priv->session_presence_proxy); g_clear_object (&manager->priv->screensaver_proxy); play_loop_stop (&manager->priv->critical_alert_timeout_id); g_clear_object (&manager->priv->idle_monitor); if (manager->priv->xscreensaver_watchdog_timer_id > 0) { g_source_remove (manager->priv->xscreensaver_watchdog_timer_id); manager->priv->xscreensaver_watchdog_timer_id = 0; } } static void gsd_power_manager_init (GsdPowerManager *manager) { manager->priv = GSD_POWER_MANAGER_GET_PRIVATE (manager); manager->priv->inhibit_lid_switch_fd = -1; manager->priv->inhibit_suspend_fd = -1; manager->priv->inhibit_lid_switch_action = FALSE; manager->priv->bus_cancellable = g_cancellable_new (); } /* returns new level */ static void handle_method_call_keyboard (GsdPowerManager *manager, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation) { gint now; gint step; gint value = -1; gboolean ret; guint percentage; GError *error = NULL; if (g_strcmp0 (method_name, "StepUp") == 0) { g_debug ("keyboard step up"); now = upower_kbd_get_brightness (manager); step = BRIGHTNESS_STEP_AMOUNT (manager->priv->kbd_brightness_max); value = MIN (now + step, manager->priv->kbd_brightness_max); ret = upower_kbd_set_brightness (manager, value, &error); } else if (g_strcmp0 (method_name, "StepDown") == 0) { g_debug ("keyboard step down"); now = upower_kbd_get_brightness (manager); step = BRIGHTNESS_STEP_AMOUNT (manager->priv->kbd_brightness_max); value = MAX (now - step, 0); ret = upower_kbd_set_brightness (manager, value, &error); } else if (g_strcmp0 (method_name, "Toggle") == 0) { value = upower_kbd_toggle (manager, &error); ret = (value >= 0); } else { g_assert_not_reached (); } /* return value */ if (!ret) { g_dbus_method_invocation_take_error (invocation, error); } else { percentage = ABS_TO_PERCENTAGE (0, manager->priv->kbd_brightness_max, value); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", percentage)); } } static void handle_method_call_screen (GsdPowerManager *manager, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation) { gboolean ret = FALSE; gint value = -1; guint value_tmp; GError *error = NULL; if (!manager->priv->backlight_available) { g_set_error_literal (&error, GSD_POWER_MANAGER_ERROR, GSD_POWER_MANAGER_ERROR_FAILED, "Screen backlight not available"); goto out; } if (g_strcmp0 (method_name, "GetPercentage") == 0) { g_debug ("screen get percentage"); value = backlight_get_percentage (manager->priv->rr_screen, &error); } else if (g_strcmp0 (method_name, "SetPercentage") == 0) { g_debug ("screen set percentage"); g_variant_get (parameters, "(u)", &value_tmp); ret = backlight_set_percentage (manager->priv->rr_screen, value_tmp, &error); if (ret) { value = value_tmp; backlight_emit_changed (manager); } } else if (g_strcmp0 (method_name, "StepUp") == 0) { g_debug ("screen step up"); value = backlight_step_up (manager->priv->rr_screen, &error); if (value != -1) backlight_emit_changed (manager); } else if (g_strcmp0 (method_name, "StepDown") == 0) { g_debug ("screen step down"); value = backlight_step_down (manager->priv->rr_screen, &error); if (value != -1) backlight_emit_changed (manager); } else { g_assert_not_reached (); } out: /* return value */ if (value < 0) { g_dbus_method_invocation_take_error (invocation, error); } else { g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", value)); } } static GVariant * device_to_variant_blob (UpDevice *device) { const gchar *object_path; gchar *device_icon; gdouble percentage; GIcon *icon; guint64 time_empty, time_full; guint64 time_state = 0; GVariant *value; UpDeviceKind kind; UpDeviceState state; icon = gpm_upower_get_device_icon (device, TRUE); device_icon = g_icon_to_string (icon); g_object_get (device, "kind", &kind, "percentage", &percentage, "state", &state, "time-to-empty", &time_empty, "time-to-full", &time_full, NULL); /* only return time for these simple states */ if (state == UP_DEVICE_STATE_DISCHARGING) time_state = time_empty; else if (state == UP_DEVICE_STATE_CHARGING) time_state = time_full; /* get an object path, even for the composite device */ object_path = up_device_get_object_path (device); if (object_path == NULL) object_path = GSD_DBUS_PATH; /* format complex object */ value = g_variant_new ("(susdut)", object_path, kind, device_icon, percentage, state, time_state); g_free (device_icon); g_object_unref (icon); return value; } static void handle_method_call_main (GsdPowerManager *manager, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation) { GPtrArray *array; guint i; GVariantBuilder *builder; GVariant *tuple = NULL; GVariant *value = NULL; UpDevice *device; /* return object */ if (g_strcmp0 (method_name, "GetPrimaryDevice") == 0) { /* get the virtual device */ device = engine_get_primary_device (manager); if (device == NULL) { g_dbus_method_invocation_return_dbus_error (invocation, "org.gnome.SettingsDaemon.Power.Failed", "There is no primary device."); return; } /* return the value */ value = device_to_variant_blob (device); tuple = g_variant_new_tuple (&value, 1); g_dbus_method_invocation_return_value (invocation, tuple); g_object_unref (device); return; } /* return array */ if (g_strcmp0 (method_name, "GetDevices") == 0) { /* create builder */ builder = g_variant_builder_new (G_VARIANT_TYPE("a(susdut)")); /* add each tuple to the array */ array = manager->priv->devices_array; for (i=0; ilen; i++) { device = g_ptr_array_index (array, i); value = device_to_variant_blob (device); g_variant_builder_add_value (builder, value); } /* return the value */ value = g_variant_builder_end (builder); tuple = g_variant_new_tuple (&value, 1); g_dbus_method_invocation_return_value (invocation, tuple); g_variant_builder_unref (builder); return; } g_assert_not_reached (); } static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); /* Check session pointer as a proxy for whether the manager is in the start or stop state */ if (manager->priv->session == NULL) { return; } g_debug ("Calling method '%s.%s' for Power", interface_name, method_name); if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE) == 0) { handle_method_call_main (manager, method_name, parameters, invocation); } else if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_SCREEN) == 0) { handle_method_call_screen (manager, method_name, parameters, invocation); } else if (g_strcmp0 (interface_name, GSD_POWER_DBUS_INTERFACE_KEYBOARD) == 0) { handle_method_call_keyboard (manager, method_name, parameters, invocation); } else { g_warning ("not recognised interface: %s", interface_name); } } static GVariant * handle_get_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { GsdPowerManager *manager = GSD_POWER_MANAGER (user_data); GVariant *retval = NULL; /* Check session pointer as a proxy for whether the manager is in the start or stop state */ if (manager->priv->session == NULL) { return NULL; } if (g_strcmp0 (property_name, "Icon") == 0) { retval = engine_get_icon_property_variant (manager); } else if (g_strcmp0 (property_name, "Tooltip") == 0) { retval = engine_get_tooltip_property_variant (manager); } else if (g_strcmp0 (property_name, "Percentage") == 0) { gdouble percentage; percentage = engine_get_percentage (manager); if (percentage >= 0) retval = g_variant_new_double (percentage); } return retval; } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, handle_get_property, NULL, /* SetProperty */ }; static void on_bus_gotten (GObject *source_object, GAsyncResult *res, GsdPowerManager *manager) { GDBusConnection *connection; GDBusInterfaceInfo **infos; GError *error = NULL; guint i; connection = g_bus_get_finish (res, &error); if (connection == NULL) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Could not get session bus: %s", error->message); g_error_free (error); return; } manager->priv->connection = connection; infos = manager->priv->introspection_data->interfaces; for (i = 0; infos[i] != NULL; i++) { g_dbus_connection_register_object (connection, GSD_POWER_DBUS_PATH, infos[i], &interface_vtable, manager, NULL, NULL); } manager->priv->name_id = g_bus_own_name_on_connection (connection, GSD_POWER_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, NULL, NULL, NULL); } static void register_manager_dbus (GsdPowerManager *manager) { manager->priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (manager->priv->introspection_data != NULL); g_bus_get (G_BUS_TYPE_SESSION, manager->priv->bus_cancellable, (GAsyncReadyCallback) on_bus_gotten, manager); } GsdPowerManager * gsd_power_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_POWER_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); register_manager_dbus (manager_object); } return GSD_POWER_MANAGER (manager_object); } ./plugins/power/gsd-backlight-linux.h0000644000004100000410000000214012735467744020111 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2010-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ typedef enum { GSD_BACKLIGHT_TYPE_FIRMWARE, GSD_BACKLIGHT_TYPE_PLATFORM, GSD_BACKLIGHT_TYPE_RAW, } GsdBacklightType; char *gsd_backlight_helper_get_best_backlight (GsdBacklightType *type); ./plugins/power/gsm-manager-logout-mode.h0000644000004100000410000000223312735467744020703 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 William Jon McCann * * 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. * */ #ifndef __GSM_MANAGER_LOGOUT_MODE_H #define __GSM_MANAGER_LOGOUT_MODE_H G_BEGIN_DECLS typedef enum { GSM_MANAGER_LOGOUT_MODE_NORMAL = 0, GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION, GSM_MANAGER_LOGOUT_MODE_FORCE } GsmManagerLogoutMode; G_END_DECLS #endif /* __GSM_MANAGER_LOGOUT_MODE_H */ ./plugins/power/gsm-inhibitor-flag.h0000644000004100000410000000240412735467744017736 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 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 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. */ #ifndef __GSM_INHIBITOR_FLAG_H__ #define __GSM_INHIBITOR_FLAG_H__ #include G_BEGIN_DECLS typedef enum { GSM_INHIBITOR_FLAG_LOGOUT = 1 << 0, GSM_INHIBITOR_FLAG_SWITCH_USER = 1 << 1, GSM_INHIBITOR_FLAG_SUSPEND = 1 << 2, GSM_INHIBITOR_FLAG_IDLE = 1 << 3, GSM_INHIBITOR_FLAG_AUTOMOUNT = 1 << 4 } GsmInhibitorFlag; G_END_DECLS #endif /* __GSM_INHIBITOR_FLAG_H__ */ ./plugins/power/gsm-presence-flag.h0000644000004100000410000000221212735467744017550 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2009 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 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. */ #ifndef __GSM_PRESENCE_FLAG_H__ #define __GSM_PRESENCE_FLAG_H__ G_BEGIN_DECLS typedef enum { GSM_PRESENCE_STATUS_AVAILABLE = 0, GSM_PRESENCE_STATUS_INVISIBLE, GSM_PRESENCE_STATUS_BUSY, GSM_PRESENCE_STATUS_IDLE, } GsmPresenceStatus; G_END_DECLS #endif /* __GSM_PRESENCE_FLAG_H__ */ ./plugins/power/gsdpowerconstants.py0000644000004100000410000000113212735467744020241 0ustar www-datawww-data # File auto-generated from script http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/power/gsd-power-constants-update.pl # Modified by the GTK+ Team and others 1997-2012. See the AUTHORS # file for a list of people on the GTK+ Team. See the ChangeLog # files for a list of changes. These files are distributed with # GTK+ at ftp://ftp.gtk.org/pub/gtk/. SCREENSAVER_TIMEOUT_BLANK = 15; IDLE_DIM_BLANK_DISABLED_MIN = 60; IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER = 4.0/5.0; MINIMUM_IDLE_DIM_DELAY = 10; POWER_UP_TIME_ON_AC = 15; GSD_MOCK_DEFAULT_BRIGHTNESS = 50; GSD_MOCK_MAX_BRIGHTNESS = 100; ./plugins/power/gsd-power-plugin.c0000644000004100000410000000201512735467744017450 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-power-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdPower, gsd_power) ./plugins/power/com.ubuntu.unity-settings-daemon.plugins.power.policy.in.in0000644000004100000410000000223012735467744027513 0ustar www-datawww-data Unity Settings Daemon http://git.gnome.org/browse/gnome-settings-daemon battery <_description>Modify the laptop brightness <_message>Authentication is required to modify the laptop brightness no no yes @libexecdir@/usd-backlight-helper ./plugins/power/test-power.c0000644000004100000410000000030512735467744016356 0ustar www-datawww-data#define NEW gsd_power_manager_new #define START gsd_power_manager_start #define STOP gsd_power_manager_stop #define MANAGER GsdPowerManager #include "gsd-power-manager.h" #include "test-plugin.h" ./plugins/power/power.gnome-settings-plugin.in0000644000004100000410000000025012735467744022022 0ustar www-datawww-data[GNOME Settings Plugin] Module=power IAge=0 Priority=1 _Name=Power _Description=Power plugin Authors=Richard Hughes Copyright=Copyright © 2011 Richard Hughes Website= ./plugins/power/gsd-power-enums-update.c0000644000004100000410000000160612735467744020566 0ustar www-datawww-data#include #include static void output_enum_values (GType class_type) { GEnumClass *eclass; guint i; eclass = G_ENUM_CLASS (g_type_class_peek (class_type)); for (i = 0; i < eclass->n_values; i++) { GEnumValue *value = &(eclass->values[i]); g_print ("%s = %d;\n", value->value_name, value->value); } } static void output_flags_values (GType class_type) { GFlagsClass *fclass; guint i; fclass = G_FLAGS_CLASS (g_type_class_peek (class_type)); for (i = 0; i < fclass->n_values; i++) { GFlagsValue *value = &(fclass->values[i]); g_print ("%s = %d;\n", value->value_name, value->value); } } int main (int argc, char **argv) { g_type_class_ref (GSD_POWER_TYPE_INHIBITOR_FLAG); g_type_class_ref (GSD_POWER_TYPE_PRESENCE_STATUS); output_flags_values (GSD_POWER_TYPE_INHIBITOR_FLAG); output_enum_values (GSD_POWER_TYPE_PRESENCE_STATUS); return 0; } ./plugins/power/gsd-backlight-linux.c0000644000004100000410000000454112735467744020113 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2010-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "config.h" #include "gsd-backlight-linux.h" #ifdef HAVE_GUDEV #include static gchar * gsd_backlight_helper_get_type (GList *devices, const gchar *type) { const gchar *type_tmp; GList *d; for (d = devices; d != NULL; d = d->next) { type_tmp = g_udev_device_get_sysfs_attr (d->data, "type"); if (g_strcmp0 (type_tmp, type) == 0) return g_strdup (g_udev_device_get_sysfs_path (d->data)); } return NULL; } #endif /* HAVE_GUDEV */ char * gsd_backlight_helper_get_best_backlight (GsdBacklightType *type) { #ifdef HAVE_GUDEV gchar *path = NULL; GList *devices; GUdevClient *client; client = g_udev_client_new (NULL); devices = g_udev_client_query_by_subsystem (client, "backlight"); if (devices == NULL) goto out; /* search the backlight devices and prefer the types: * firmware -> platform -> raw */ path = gsd_backlight_helper_get_type (devices, "firmware"); if (path != NULL) { if (type) *type = GSD_BACKLIGHT_TYPE_FIRMWARE; goto out; } path = gsd_backlight_helper_get_type (devices, "platform"); if (path != NULL) { if (type) *type = GSD_BACKLIGHT_TYPE_PLATFORM; goto out; } path = gsd_backlight_helper_get_type (devices, "raw"); if (path != NULL) { if (type) *type = GSD_BACKLIGHT_TYPE_RAW; goto out; } out: g_object_unref (client); g_list_foreach (devices, (GFunc) g_object_unref, NULL); g_list_free (devices); return path; #endif /* HAVE_GUDEV */ return NULL; } ./plugins/power/test.py0000755000004100000410000006616112735467744015451 0ustar www-datawww-data#!/usr/bin/env python '''GNOME settings daemon tests for power plugin.''' __author__ = 'Martin Pitt ' __copyright__ = '(C) 2013 Canonical Ltd.' __license__ = 'GPL v2 or later' import unittest import subprocess import sys import time import os import os.path import signal project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) builddir = os.environ.get('BUILDDIR', os.path.dirname(__file__)) sys.path.insert(0, os.path.join(project_root, 'tests')) sys.path.insert(0, builddir) import gsdtestcase import gsdpowerconstants import gsdpowerenums import dbus from gi.repository import Gio class PowerPluginTest(gsdtestcase.GSDTestCase): '''Test the power plugin''' def setUp(self): self.daemon_death_expected = False self.session_log_write = open(os.path.join(self.workdir, 'gnome-session.log'), 'wb') self.session = subprocess.Popen(['gnome-session', '-f', '-a', os.path.join(self.workdir, 'autostart'), '--session=dummy', '--debug'], stdout=self.session_log_write, stderr=subprocess.STDOUT) # wait until the daemon is on the bus try: self.wait_for_bus_object('org.gnome.SessionManager', '/org/gnome/SessionManager') except: # on failure, print log with open(self.session_log_write.name) as f: print('----- session log -----\n%s\n------' % f.read()) raise self.session_log = open(self.session_log_write.name) self.obj_session_mgr = self.session_bus_con.get_object( 'org.gnome.SessionManager', '/org/gnome/SessionManager') # start mock upowerd (self.upowerd, self.obj_upower) = self.spawn_server_template( 'upower', {'OnBattery': True, 'LidIsClosed': False}, stdout=subprocess.PIPE) gsdtestcase.set_nonblock(self.upowerd.stdout) # start mock gnome-shell screensaver (self.screensaver, self.obj_screensaver) = self.spawn_server_template( 'gnome_screensaver', stdout=subprocess.PIPE) gsdtestcase.set_nonblock(self.screensaver.stdout) self.start_logind() # Set up the gnome-session presence obj_session_presence = self.session_bus_con.get_object( 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence') self.obj_session_presence_props = dbus.Interface(obj_session_presence, dbus.PROPERTIES_IFACE) # ensure that our tests don't lock the screen when the screensaver # gets active self.settings_screensaver = Gio.Settings('org.gnome.desktop.screensaver') self.settings_screensaver['lock-enabled'] = False self.settings_gsd_power = Gio.Settings('org.gnome.settings-daemon.plugins.power') # start power plugin self.settings_gsd_power['active'] = False Gio.Settings.sync() self.plugin_log_write = open(os.path.join(self.workdir, 'plugin_power.log'), 'wb') # avoid painfully long delays of actions for tests env = os.environ.copy() env['GSD_DISABLE_BACKLIGHT_HELPER'] = '1' self.daemon = subprocess.Popen( [os.path.join(builddir, 'usd-test-power')], # comment out this line if you want to see the logs in real time stdout=self.plugin_log_write, stderr=subprocess.STDOUT, env=env) # you can use this for reading the current daemon log in tests self.plugin_log = open(self.plugin_log_write.name) # wait until plugin is ready timeout = 100 while timeout > 0: time.sleep(0.1) timeout -= 1 log = self.plugin_log.read() if 'System inhibitor fd is' in log: break # always start with zero idle time self.reset_idle_timer() # flush notification log try: self.p_notify.stdout.read() except IOError: pass def tearDown(self): daemon_running = self.daemon.poll() == None if daemon_running: self.daemon.terminate() self.daemon.wait() self.plugin_log.close() self.plugin_log_write.flush() self.plugin_log_write.close() self.upowerd.terminate() self.upowerd.wait() self.screensaver.terminate() self.screensaver.wait() self.stop_session() self.stop_logind() # reset all changed gsettings, so that tests are independent from each # other for schema in [self.settings_gsd_power, self.settings_session, self.settings_screensaver]: for k in schema.list_keys(): schema.reset(k) Gio.Settings.sync() try: os.unlink('GSD_MOCK_EXTERNAL_MONITOR') except OSError: pass try: os.unlink('GSD_MOCK_brightness') except OSError: pass # we check this at the end so that the other cleanup always happens self.assertTrue(daemon_running or self.daemon_death_expected, 'daemon died during the test') def stop_session(self): '''Stop GNOME session''' assert self.session self.session.terminate() self.session.wait() self.session_log_write.flush() self.session_log_write.close() self.session_log.close() def get_status(self): return self.obj_session_presence_props.Get('org.gnome.SessionManager.Presence', 'status') def get_brightness(self): f = open('GSD_MOCK_brightness', 'r') ret = f.read() f.close() return int(ret) def set_has_external_monitor(self, external): f = open('GSD_MOCK_EXTERNAL_MONITOR', 'w') if external: f.write('1') else: f.write('0') f.close () os.kill(self.daemon.pid, signal.SIGUSR2) def check_for_logout(self, timeout): '''Check that logout is requested. Fail after the tiven timeout. ''' # check that it request suspend while timeout > 0: time.sleep(1) timeout -= 1 # check that it requested suspend try: log = self.session_log.read() except IOError: break if log and (b'GsmManager: requesting logout' in log): break else: self.fail('timed out waiting for gnome-session logout call') def check_no_logout(self, seconds): '''Check that no logout is requested in the given time''' # wait for specified time to ensure it didn't do anything time.sleep(seconds) # check that it did not logout log = self.session_log.read() if log: self.assertFalse(b'GsmManager: requesting logout' in log, 'unexpected logout request') def check_for_suspend(self, timeout): '''Check that Suspend() or Hibernate() is requested. Fail after the tiven timeout. ''' # check that it request suspend while timeout > 0: time.sleep(1) timeout -= 1 # check that it requested suspend try: log = self.logind.stdout.read() except IOError: break if log and (b' Suspend ' in log or b' Hibernate ' in log): break else: self.fail('timed out waiting for logind Suspend() call') def check_no_suspend(self, seconds): '''Check that no Suspend or Hibernate is requested in the given time''' # wait for specified time to ensure it didn't do anything time.sleep(seconds) # check that it did not suspend or hibernate log = self.logind.stdout.read() if log: self.assertFalse(b' Suspend' in log, 'unexpected Suspend request') self.assertFalse(b' Hibernate' in log, 'unexpected Hibernate request') def check_no_dim(self, seconds): '''Check that mode is not set to dim in the given time''' # wait for specified time to ensure it didn't do anything time.sleep(seconds) # check that we don't dim log = self.plugin_log.read() if log: self.assertFalse(b'Doing a state transition: dim' in log, 'unexpected dim request') def check_dim(self, timeout): '''Check that mode is set to dim in the given time''' # wait for specified time to ensure it didn't do anything while timeout > 0: time.sleep(1) timeout -= 1 # check that it requested dim log = self.plugin_log.read() if 'Doing a state transition: dim' in log: break else: self.fail('timed out waiting for dim') def check_undim(self, timeout): '''Check that mode is set to normal in the given time''' # wait for specified time to ensure it didn't do anything while timeout > 0: time.sleep(1) timeout -= 1 # check that it requested normal log = self.plugin_log.read() if 'Doing a state transition: normal' in log: break else: self.fail('timed out waiting for normal mode') def check_blank(self, timeout): '''Check that blank is requested. Fail after the given timeout. ''' # check that it request blank while timeout > 0: time.sleep(1) timeout -= 1 # check that it requested blank log = self.plugin_log.read() if 'TESTSUITE: Blanked screen' in log: break else: self.fail('timed out waiting for blank') def check_unblank(self, timeout): '''Check that unblank is requested. Fail after the given timeout. ''' # check that it request blank while timeout > 0: time.sleep(1) timeout -= 1 # check that it requested unblank log = self.plugin_log.read() if 'TESTSUITE: Unblanked screen' in log: break else: self.fail('timed out waiting for unblank') def check_no_blank(self, seconds): '''Check that no blank is requested in the given time''' # wait for specified time to ensure it didn't blank time.sleep(seconds) # check that it did not blank log = self.plugin_log.read() self.assertFalse('TESTSUITE: Blanked screen' in log, 'unexpected blank request') def test_sleep_inactive_blank(self): '''screensaver/blank interaction''' # create suspend inhibitor which should have no effect on the idle inhibit_id = self.obj_session_mgr.Inhibit( 'testsuite', dbus.UInt32(0), 'for testing', dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND)) self.obj_screensaver.SetActive(True) self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') # blank is supposed to happen straight away self.check_blank(2) # wiggle the mouse now and check for unblank; this is expected to pop up # the locked screen saver self.reset_idle_timer() self.check_unblank(2) self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness') # Check for no blank before the normal blank timeout self.check_no_blank(gsdpowerconstants.SCREENSAVER_TIMEOUT_BLANK - 4) self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') # and check for blank after the blank timeout self.check_blank(10) # Drop inhibitor self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id)) def test_session_idle_delay(self): '''verify that session idle delay works as expected when changed''' # Verify that idle is set after 5 seconds self.settings_session['idle-delay'] = 5 self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) time.sleep(7) self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) # Raise the idle delay, and see that we stop being idle # and get idle again after the timeout self.settings_session['idle-delay'] = 10 self.reset_idle_timer() time.sleep(5) os.kill(self.session.pid, signal.SIGUSR2) self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) time.sleep(10) self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) # Lower the delay again, and see that we get idle as we should self.settings_session['idle-delay'] = 5 self.reset_idle_timer() time.sleep(2) os.kill(self.session.pid, signal.SIGUSR2) self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) time.sleep(5) self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) def test_idle_time_reset_on_resume(self): '''Check that the IDLETIME is reset when resuming''' # Go idle self.settings_session['idle-delay'] = 5 self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) time.sleep(7) self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_IDLE) # Go to sleep self.obj_logind.EmitSignal('', 'PrepareForSleep', 'b', [True], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(1) # Wake up self.obj_logind.EmitSignal('', 'PrepareForSleep', 'b', [False], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(1) # And check we're not idle self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) def test_sleep_inactive_battery(self): '''sleep-inactive-battery-timeout''' self.settings_session['idle-delay'] = 2 self.settings_gsd_power['sleep-inactive-battery-timeout'] = 5 self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' # wait for idle delay; should not yet suspend self.check_no_suspend(2) # suspend should happen after inactive sleep timeout + 1 s notification # delay + 1 s error margin self.check_for_suspend(7) def test_sleep_inhibition(self): '''Does not sleep under idle inhibition''' idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) self.settings_session['idle-delay'] = idle_delay self.settings_gsd_power['sleep-inactive-battery-timeout'] = 5 self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' # create inhibitor inhibit_id = self.obj_session_mgr.Inhibit( 'testsuite', dbus.UInt32(0), 'for testing', dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_IDLE | gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND)) self.check_no_suspend(idle_delay + 2) self.check_no_dim(0) # Check that we didn't go to idle either self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id)) def test_lock_on_lid_close(self): '''Check that we do lock on lid closing, if the machine will not suspend''' self.settings_screensaver['lock-enabled'] = True # create inhibitor inhibit_id = self.obj_session_mgr.Inhibit( 'testsuite', dbus.UInt32(0), 'for testing', dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND)) # Close the lid self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') # Check that we've blanked time.sleep(2) self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') self.check_blank(2) # Drop the inhibit and see whether we suspend self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id)) self.check_for_suspend(5) def test_blank_on_lid_close(self): '''Check that we do blank on lid closing, if the machine will not suspend''' # create inhibitor inhibit_id = self.obj_session_mgr.Inhibit( 'testsuite', dbus.UInt32(0), 'for testing', dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND)) # Close the lid self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') # Check that we've blanked self.check_blank(4) # Drop the inhibit and see whether we suspend self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id)) self.check_for_suspend(5) def test_unblank_on_lid_open(self): '''Check that we do unblank on lid opening, if the machine will not suspend''' # create inhibitor inhibit_id = self.obj_session_mgr.Inhibit( 'testsuite', dbus.UInt32(0), 'for testing', dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_SUSPEND)) # Close the lid self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') # Check that we've blanked self.check_blank(2) # Reopen the lid self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', False) self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') # Check for unblanking self.check_unblank(2) # Drop the inhibit self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id)) def test_dim(self): '''Check that we do go to dim''' idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) self.settings_session['idle-delay'] = idle_delay self.settings_gsd_power['sleep-inactive-battery-timeout'] = idle_delay + 1 self.settings_gsd_power['sleep-inactive-battery-type'] = 'suspend' # This is an absolute percentage, and our brightness is 0..100 dim_level = self.settings_gsd_power['idle-brightness']; # Check that we're not idle self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) # Wait and check we're not idle, but dimmed self.check_dim(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY) self.assertTrue(self.get_brightness() == dim_level, 'incorrect dim brightness') self.assertEqual(self.get_status(), gsdpowerenums.GSM_PRESENCE_STATUS_AVAILABLE) # Bring down the screensaver self.obj_screensaver.SetActive(True) self.assertTrue(self.obj_screensaver.GetActive(), 'screensaver not turned on') # Check that we blank self.check_blank(2) # Go to sleep self.obj_logind.EmitSignal('', 'PrepareForSleep', 'b', [True], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(1) # Wake up self.obj_logind.EmitSignal('', 'PrepareForSleep', 'b', [False], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(1) # And check that we have the pre-dim brightness self.assertTrue(self.get_brightness() == gsdpowerconstants.GSD_MOCK_DEFAULT_BRIGHTNESS , 'incorrect unblanked brightness') def test_no_suspend_lid_close(self): '''Check that we don't suspend on lid close with an external monitor''' # Add an external monitor self.set_has_external_monitor(True) time.sleep (1) # Close the lid self.obj_upower.Set('org.freedesktop.UPower', 'LidIsClosed', True) self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') # Check for no suspend, and for no screen blanking self.check_no_suspend (10) self.check_no_blank(0) # Unplug the external monitor self.set_has_external_monitor(False) self.check_for_suspend (10) def test_action_critical_battery(self): '''action on critical battery''' # add a fake battery with 30%/2 hours charge to upower bat_path = self.obj_upower.AddDischargingBattery('mock_BAT', 'Mock Bat', 30.0, 1200) obj_bat = self.system_bus_con.get_object('org.freedesktop.UPower', bat_path) self.obj_upower.EmitSignal('', 'DeviceAdded', 's', [bat_path], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(1) # now change battery to critical charge obj_bat.Set('org.freedesktop.UPower.Device', 'TimeToEmpty', dbus.Int64(30, variant_level=1), dbus_interface=dbus.PROPERTIES_IFACE) obj_bat.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') self.obj_upower.EmitSignal('', 'DeviceChanged', 's', [obj_bat.object_path], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(0.5) # we should have gotten a notification now notify_log = self.p_notify.stdout.read() self.check_for_suspend(5) # verify notification self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" 0 "battery-.*" ".*battery critical.*"') def test_action_critical_battery_on_start(self): '''action on critical battery on startup''' # add a fake battery with 2%/1 minute charge to upower bat_path = self.obj_upower.AddDischargingBattery('mock_BAT', 'Mock Bat', 2.0, 60) obj_bat = self.system_bus_con.get_object('org.freedesktop.UPower', bat_path) self.obj_upower.EmitSignal('', 'DeviceAdded', 's', [bat_path], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(5) # we should have gotten a notification now notify_log = self.p_notify.stdout.read() self.check_for_suspend(5) # verify notification self.assertRegex(notify_log, b'[0-9.]+ Notify "Power" 0 "battery-.*" ".*battery critical.*"') def test_action_multiple_batteries(self): '''critical actions for multiple batteries''' # add two fake batteries to upower bat1_path = self.obj_upower.AddDischargingBattery('mock_BAT1', 'Bat0', 30.0, 1200) obj_bat1 = self.system_bus_con.get_object('org.freedesktop.UPower', bat1_path) self.obj_upower.EmitSignal('', 'DeviceAdded', 's', [bat1_path], dbus_interface='org.freedesktop.DBus.Mock') bat2_path = self.obj_upower.AddDischargingBattery('mock_BAT2', 'Bat2', 40.0, 1600) obj_bat2 = self.system_bus_con.get_object('org.freedesktop.UPower', bat2_path) self.obj_upower.EmitSignal('', 'DeviceAdded', 's', [bat2_path], dbus_interface='org.freedesktop.DBus.Mock') time.sleep(1) # now change one battery to critical charge obj_bat1.Set('org.freedesktop.UPower.Device', 'TimeToEmpty', dbus.Int64(30, variant_level=1), dbus_interface=dbus.PROPERTIES_IFACE) obj_bat1.Set('org.freedesktop.UPower.Device', 'Energy', dbus.Double(0.5, variant_level=1), dbus_interface=dbus.PROPERTIES_IFACE) obj_bat1.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') self.obj_upower.EmitSignal('', 'DeviceChanged', 's', [bat1_path], dbus_interface='org.freedesktop.DBus.Mock') # wait long enough to ensure it didn't do anything (as we still have # the second battery) self.check_no_suspend(5) # now change the other battery to critical charge as well obj_bat2.Set('org.freedesktop.UPower.Device', 'TimeToEmpty', dbus.Int64(25, variant_level=1), dbus_interface=dbus.PROPERTIES_IFACE) obj_bat2.Set('org.freedesktop.UPower.Device', 'Energy', dbus.Double(0.4, variant_level=1), dbus_interface=dbus.PROPERTIES_IFACE) obj_bat2.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') self.obj_upower.EmitSignal('', 'DeviceChanged', 's', [bat2_path], dbus_interface='org.freedesktop.DBus.Mock') self.check_for_suspend(5) def test_forced_logout(self): '''Test forced logout''' self.daemon_death_expected = True idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) self.settings_session['idle-delay'] = idle_delay self.settings_gsd_power['sleep-inactive-battery-timeout'] = idle_delay + 1 self.settings_gsd_power['sleep-inactive-battery-type'] = 'logout' self.check_for_logout(idle_delay + 2) # The notification should have been received before the logout, but it's saved anyway notify_log = self.p_notify.stdout.read() self.assertTrue(b'You will soon log out because of inactivity.' in notify_log) def test_forced_logout_inhibition(self): '''Test we don't force logout when inhibited''' idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) self.settings_session['idle-delay'] = idle_delay self.settings_gsd_power['sleep-inactive-battery-timeout'] = idle_delay + 1 self.settings_gsd_power['sleep-inactive-battery-type'] = 'logout' # create suspend inhibitor which should stop us logging out inhibit_id = self.obj_session_mgr.Inhibit( 'testsuite', dbus.UInt32(0), 'for testing', dbus.UInt32(gsdpowerenums.GSM_INHIBITOR_FLAG_LOGOUT)) self.check_no_logout(idle_delay + 3) # Drop inhibitor self.obj_session_mgr.Uninhibit(dbus.UInt32(inhibit_id)) def test_unindle_on_ac_plug(self): idle_delay = round(gsdpowerconstants.MINIMUM_IDLE_DIM_DELAY / gsdpowerconstants.IDLE_DELAY_TO_IDLE_DIM_MULTIPLIER) self.settings_session['idle-delay'] = idle_delay # Wait for idle self.check_dim(idle_delay + 2) # Plug in the AC self.obj_upower.Set('org.freedesktop.UPower', 'OnBattery', False) self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') # Check that we undim self.check_undim(gsdpowerconstants.POWER_UP_TIME_ON_AC / 2) # And wait a little more to see us dim again self.check_dim(idle_delay + 2) # Unplug the AC self.obj_upower.Set('org.freedesktop.UPower', 'OnBattery', True) self.obj_upower.EmitSignal('', 'Changed', '', [], dbus_interface='org.freedesktop.DBus.Mock') # Check that we undim self.check_undim(gsdpowerconstants.POWER_UP_TIME_ON_AC / 2) # And wait a little more to see us dim again self.check_dim(idle_delay + 2) # avoid writing to stderr unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) ./plugins/keyboard/0000755000004100000410000000000012735467763014550 5ustar www-datawww-data./plugins/keyboard/test-keyboard.c0000644000004100000410000000032412735467744017467 0ustar www-datawww-data#define NEW gsd_keyboard_manager_new #define START gsd_keyboard_manager_start #define STOP gsd_keyboard_manager_stop #define MANAGER GsdKeyboardManager #include "gsd-keyboard-manager.h" #include "test-plugin.h" ./plugins/keyboard/gsd-keyboard-manager.c0000644000004100000410000026120012735467763020700 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright © 2001 Ximian, Inc. * Copyright (C) 2007 William Jon McCann * Written by Sergey V. Oudaltsov * * 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 "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GNOME_DESKTOP_USE_UNSTABLE_API #include #include #ifdef HAVE_IBUS #include #endif #ifdef HAVE_FCITX #include #include #endif #include #include "gnome-settings-bus.h" #include "gnome-settings-profile.h" #include "gsd-keyboard-manager.h" #include "gsd-input-helper.h" #include "gsd-enums.h" #include "gsd-settings-migrate.h" #include "gsd-xkb-utils.h" #ifdef HAVE_FCITX #include "input-method-engines.c" #endif #define GSD_KEYBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_KEYBOARD_MANAGER, GsdKeyboardManagerPrivate)) #define GSD_KEYBOARD_DIR "org.gnome.settings-daemon.peripherals.keyboard" #define GSETTINGS_KEYBOARD_SCHEMA "org.gnome.desktop.peripherals.keyboard" #define KEY_REPEAT "repeat" #define KEY_CLICK "click" #define KEY_INTERVAL "repeat-interval" #define KEY_DELAY "delay" #define KEY_CLICK_VOLUME "click-volume" #define KEY_REMEMBER_NUMLOCK_STATE "remember-numlock-state" #define KEY_NUMLOCK_STATE "numlock-state" #define KEY_BELL_VOLUME "bell-volume" #define KEY_BELL_PITCH "bell-pitch" #define KEY_BELL_DURATION "bell-duration" #define KEY_BELL_MODE "bell-mode" #define KEY_BELL_CUSTOM_FILE "bell-custom-file" #define GNOME_DESKTOP_INTERFACE_DIR "org.gnome.desktop.interface" #define ENV_GTK_IM_MODULE "GTK_IM_MODULE" #define KEY_GTK_IM_MODULE "gtk-im-module" #define GTK_IM_MODULE_SIMPLE "gtk-im-context-simple" #define GTK_IM_MODULE_IBUS "ibus" #define GTK_IM_MODULE_FCITX "fcitx" #define GNOME_DESKTOP_INPUT_SOURCES_DIR "org.gnome.desktop.input-sources" #define KEY_CURRENT_INPUT_SOURCE "current" #define KEY_INPUT_SOURCES "sources" #define KEY_KEYBOARD_OPTIONS "xkb-options" #define INPUT_SOURCE_TYPE_XKB "xkb" #define INPUT_SOURCE_TYPE_IBUS "ibus" #define INPUT_SOURCE_TYPE_FCITX "fcitx" #define FCITX_XKB_PREFIX "fcitx-keyboard-" #define DEFAULT_LANGUAGE "en_US" #define DEFAULT_LAYOUT "us" #define GSD_KEYBOARD_DBUS_NAME "org.gnome.SettingsDaemon.Keyboard" #define GSD_KEYBOARD_DBUS_PATH "/org/gnome/SettingsDaemon/Keyboard" struct GsdKeyboardManagerPrivate { guint start_idle_id; GSettings *settings; GSettings *gsettings; GSettings *input_sources_settings; GSettings *interface_settings; GnomeXkbInfo *xkb_info; GDBusProxy *localed; GCancellable *cancellable; #ifdef HAVE_IBUS IBusBus *ibus; GHashTable *ibus_engines; GCancellable *ibus_cancellable; gboolean is_ibus_active; #endif #ifdef HAVE_FCITX FcitxInputMethod *fcitx; GCancellable *fcitx_cancellable; gulong fcitx_signal_id; gboolean is_fcitx_active; #endif gint xkb_event_base; GsdNumLockState old_state; GdkDeviceManager *device_manager; guint device_added_id; guint device_removed_id; GDBusConnection *dbus_connection; GDBusNodeInfo *dbus_introspection; guint dbus_own_name_id; GSList *dbus_register_object_ids; GDBusMethodInvocation *invocation; guint pending_ops; gint active_input_source; }; static void gsd_keyboard_manager_class_init (GsdKeyboardManagerClass *klass); static void gsd_keyboard_manager_init (GsdKeyboardManager *keyboard_manager); static void gsd_keyboard_manager_finalize (GObject *object); static gboolean apply_input_sources_settings (GSettings *settings, gpointer keys, gint n_keys, GsdKeyboardManager *manager); static void set_gtk_im_module (GsdKeyboardManager *manager, GVariant *sources); static void maybe_return_from_set_input_source (GsdKeyboardManager *manager); static void increment_set_input_source_ops (GsdKeyboardManager *manager); G_DEFINE_TYPE (GsdKeyboardManager, gsd_keyboard_manager, G_TYPE_OBJECT) static const gchar introspection_xml[] = "" " " " " " " " " " " /* Ubuntu-specific */ " " " " " " " " " " ""; static gpointer manager_object = NULL; static void init_builder_with_sources (GVariantBuilder *builder, GSettings *settings) { const gchar *type; const gchar *id; GVariantIter iter; GVariant *sources; sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); g_variant_builder_init (builder, G_VARIANT_TYPE ("a(ss)")); g_variant_iter_init (&iter, sources); while (g_variant_iter_next (&iter, "(&s&s)", &type, &id)) g_variant_builder_add (builder, "(ss)", type, id); g_variant_unref (sources); } static gboolean schema_is_installed (const gchar *name) { const gchar * const *schemas; const gchar * const *s; schemas = g_settings_list_schemas (); for (s = schemas; *s; ++s) if (g_str_equal (*s, name)) return TRUE; return FALSE; } #ifdef HAVE_IBUS static void clear_ibus (GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; g_cancellable_cancel (priv->ibus_cancellable); g_clear_object (&priv->ibus_cancellable); g_clear_pointer (&priv->ibus_engines, g_hash_table_destroy); g_clear_object (&priv->ibus); } static void fetch_ibus_engines_result (GObject *object, GAsyncResult *result, GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; GList *list, *l; GError *error = NULL; /* engines shouldn't be there yet */ g_return_if_fail (priv->ibus_engines == NULL); g_clear_object (&priv->ibus_cancellable); list = ibus_bus_list_engines_async_finish (priv->ibus, result, &error); if (!list && error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Couldn't finish IBus request: %s", error->message); g_error_free (error); clear_ibus (manager); return; } /* Maps IBus engine ids to engine description objects */ priv->ibus_engines = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); for (l = list; l; l = l->next) { IBusEngineDesc *engine = l->data; const gchar *engine_id = ibus_engine_desc_get_name (engine); g_hash_table_replace (priv->ibus_engines, (gpointer)engine_id, engine); } g_list_free (list); apply_input_sources_settings (priv->input_sources_settings, NULL, 0, manager); } static void fetch_ibus_engines (GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; /* engines shouldn't be there yet */ g_return_if_fail (priv->ibus_engines == NULL); g_return_if_fail (priv->ibus_cancellable == NULL); priv->ibus_cancellable = g_cancellable_new (); ibus_bus_list_engines_async (priv->ibus, -1, priv->ibus_cancellable, (GAsyncReadyCallback)fetch_ibus_engines_result, manager); } static void maybe_start_ibus (GsdKeyboardManager *manager) { if (!manager->priv->ibus) { ibus_init (); manager->priv->ibus = ibus_bus_new_async (); g_signal_connect_swapped (manager->priv->ibus, "connected", G_CALLBACK (fetch_ibus_engines), manager); g_signal_connect_swapped (manager->priv->ibus, "disconnected", G_CALLBACK (clear_ibus), manager); } /* IBus doesn't export API in the session bus. The only thing * we have there is a well known name which we can use as a * sure-fire way to activate it. */ g_bus_unwatch_name (g_bus_watch_name (G_BUS_TYPE_SESSION, IBUS_SERVICE_IBUS, G_BUS_NAME_WATCHER_FLAGS_AUTO_START, NULL, NULL, NULL, NULL)); } static void set_ibus_engine_finish (GObject *object, GAsyncResult *res, GsdKeyboardManager *manager) { gboolean result; IBusBus *ibus = IBUS_BUS (object); GsdKeyboardManagerPrivate *priv = manager->priv; GError *error = NULL; g_clear_object (&priv->ibus_cancellable); result = ibus_bus_set_global_engine_async_finish (ibus, res, &error); if (!result) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Couldn't set IBus engine: %s", error->message); g_error_free (error); return; } maybe_return_from_set_input_source (manager); } static void set_ibus_engine (GsdKeyboardManager *manager, const gchar *engine_id) { GsdKeyboardManagerPrivate *priv = manager->priv; g_return_if_fail (priv->ibus != NULL); g_return_if_fail (priv->ibus_engines != NULL); g_cancellable_cancel (priv->ibus_cancellable); g_clear_object (&priv->ibus_cancellable); priv->ibus_cancellable = g_cancellable_new (); increment_set_input_source_ops (manager); ibus_bus_set_global_engine_async (priv->ibus, engine_id, -1, priv->ibus_cancellable, (GAsyncReadyCallback)set_ibus_engine_finish, manager); } static void set_ibus_xkb_engine (GsdKeyboardManager *manager) { IBusEngineDesc *engine; GsdKeyboardManagerPrivate *priv = manager->priv; if (!priv->ibus_engines) return; /* All the "xkb:..." IBus engines simply "echo" back symbols, despite their naming implying differently, so we always set one in order for XIM applications to work given that we set XMODIFIERS=@im=ibus in the first place so that they can work without restarting when/if the user adds an IBus input source. */ engine = g_hash_table_lookup (priv->ibus_engines, "xkb:us::eng"); if (!engine) return; set_ibus_engine (manager, ibus_engine_desc_get_name (engine)); } static gboolean need_ibus (GVariant *sources) { GVariantIter iter; const gchar *type; g_variant_iter_init (&iter, sources); while (g_variant_iter_next (&iter, "(&s&s)", &type, NULL)) if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) return TRUE; return FALSE; } static void set_gtk_im_module (GsdKeyboardManager *manager, GVariant *sources) { GsdKeyboardManagerPrivate *priv = manager->priv; const gchar *new_module; gchar *current_module; if (!sources || need_ibus (sources)) new_module = GTK_IM_MODULE_IBUS; else new_module = GTK_IM_MODULE_SIMPLE; current_module = g_settings_get_string (priv->interface_settings, KEY_GTK_IM_MODULE); if (!g_str_equal (current_module, new_module)) g_settings_set_string (priv->interface_settings, KEY_GTK_IM_MODULE, new_module); g_free (current_module); } /* XXX: See upstream bug: * https://codereview.appspot.com/6586075/ */ static gchar * layout_from_ibus_layout (const gchar *ibus_layout) { const gchar *p; /* we get something like "layout(variant)[option1,option2]" */ p = ibus_layout; while (*p) { if (*p == '(' || *p == '[') break; p += 1; } return g_strndup (ibus_layout, p - ibus_layout); } static gchar * variant_from_ibus_layout (const gchar *ibus_layout) { const gchar *a, *b; /* we get something like "layout(variant)[option1,option2]" */ a = ibus_layout; while (*a) { if (*a == '(') break; a += 1; } if (!*a) return NULL; a += 1; b = a; while (*b) { if (*b == ')') break; b += 1; } if (!*b) return NULL; return g_strndup (a, b - a); } static gchar ** options_from_ibus_layout (const gchar *ibus_layout) { const gchar *a, *b; GPtrArray *opt_array; /* we get something like "layout(variant)[option1,option2]" */ a = ibus_layout; while (*a) { if (*a == '[') break; a += 1; } if (!*a) return NULL; opt_array = g_ptr_array_new (); do { a += 1; b = a; while (*b) { if (*b == ',' || *b == ']') break; b += 1; } if (!*b) goto out; g_ptr_array_add (opt_array, g_strndup (a, b - a)); a = b; } while (*a && *a == ','); out: g_ptr_array_add (opt_array, NULL); return (gchar **) g_ptr_array_free (opt_array, FALSE); } static const gchar * engine_from_locale (void) { const gchar *locale; const gchar *locale_engine[][2] = { { "as_IN", "m17n:as:phonetic" }, { "bn_IN", "m17n:bn:inscript" }, { "gu_IN", "m17n:gu:inscript" }, { "hi_IN", "m17n:hi:inscript" }, { "ja_JP", "mozc" }, { "kn_IN", "m17n:kn:kgp" }, { "ko_KR", "hangul" }, { "mai_IN", "m17n:mai:inscript" }, { "ml_IN", "m17n:ml:inscript" }, { "mr_IN", "m17n:mr:inscript" }, { "or_IN", "m17n:or:inscript" }, { "pa_IN", "m17n:pa:inscript" }, { "sd_IN", "m17n:sd:inscript" }, { "ta_IN", "m17n:ta:tamil99" }, { "te_IN", "m17n:te:inscript" }, { "zh_CN", "pinyin" }, { "zh_HK", "cangjie3" }, { "zh_TW", "chewing" }, }; gint i; locale = setlocale (LC_CTYPE, NULL); if (!locale) return NULL; for (i = 0; i < G_N_ELEMENTS (locale_engine); ++i) if (g_str_has_prefix (locale, locale_engine[i][0])) return locale_engine[i][1]; return NULL; } static void add_sources_from_locale (GsdKeyboardManager *manager, GSettings *settings) { const gchar *locale_engine; GVariantBuilder builder; locale_engine = engine_from_locale (); if (!locale_engine) return; init_builder_with_sources (&builder, settings); #ifdef HAVE_IBUS if (manager->priv->is_ibus_active) { g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_IBUS, locale_engine); } #endif #ifdef HAVE_FCITX if (manager->priv->is_fcitx_active) { g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_FCITX, locale_engine); } #endif g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); } static void convert_ibus (GSettings *settings) { GVariantBuilder builder; GSettings *ibus_settings; gchar **engines, **e; if (!schema_is_installed ("org.freedesktop.ibus.general")) return; init_builder_with_sources (&builder, settings); ibus_settings = g_settings_new ("org.freedesktop.ibus.general"); engines = g_settings_get_strv (ibus_settings, "preload-engines"); for (e = engines; *e; ++e) { if (g_str_has_prefix (*e, "xkb:")) continue; g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_IBUS, *e); } g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); g_strfreev (engines); g_object_unref (ibus_settings); } #endif /* HAVE_IBUS */ static gboolean xkb_set_keyboard_autorepeat_rate (guint delay, guint interval) { return XkbSetAutoRepeatRate (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XkbUseCoreKbd, delay, interval); } static gboolean check_xkb_extension (GsdKeyboardManager *manager) { Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); int opcode, error_base, major, minor; gboolean have_xkb; have_xkb = XkbQueryExtension (dpy, &opcode, &manager->priv->xkb_event_base, &error_base, &major, &minor); return have_xkb; } static void xkb_init (GsdKeyboardManager *manager) { Display *dpy; dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XkbSelectEventDetails (dpy, XkbUseCoreKbd, XkbStateNotify, XkbModifierLockMask, XkbModifierLockMask); } static unsigned numlock_NumLock_modifier_mask (void) { Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); return XkbKeysymToModifiers (dpy, XK_Num_Lock); } static void numlock_set_xkb_state (GsdNumLockState new_state) { unsigned int num_mask; Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); if (new_state != GSD_NUM_LOCK_STATE_ON && new_state != GSD_NUM_LOCK_STATE_OFF) return; num_mask = numlock_NumLock_modifier_mask (); XkbLockModifiers (dpy, XkbUseCoreKbd, num_mask, new_state == GSD_NUM_LOCK_STATE_ON ? num_mask : 0); } static const char * num_lock_state_to_string (GsdNumLockState numlock_state) { switch (numlock_state) { case GSD_NUM_LOCK_STATE_UNKNOWN: return "GSD_NUM_LOCK_STATE_UNKNOWN"; case GSD_NUM_LOCK_STATE_ON: return "GSD_NUM_LOCK_STATE_ON"; case GSD_NUM_LOCK_STATE_OFF: return "GSD_NUM_LOCK_STATE_OFF"; default: return "UNKNOWN"; } } static GdkFilterReturn xkb_events_filter (GdkXEvent *xev_, GdkEvent *gdkev_, gpointer user_data) { XEvent *xev = (XEvent *) xev_; XkbEvent *xkbev = (XkbEvent *) xev; GsdKeyboardManager *manager = (GsdKeyboardManager *) user_data; if (xev->type != manager->priv->xkb_event_base || xkbev->any.xkb_type != XkbStateNotify) return GDK_FILTER_CONTINUE; if (xkbev->state.changed & XkbModifierLockMask) { unsigned num_mask = numlock_NumLock_modifier_mask (); unsigned locked_mods = xkbev->state.locked_mods; GsdNumLockState numlock_state; numlock_state = (num_mask & locked_mods) ? GSD_NUM_LOCK_STATE_ON : GSD_NUM_LOCK_STATE_OFF; if (numlock_state != manager->priv->old_state) { g_debug ("New num-lock state '%s' != Old num-lock state '%s'", num_lock_state_to_string (numlock_state), num_lock_state_to_string (manager->priv->old_state)); g_settings_set_enum (manager->priv->settings, KEY_NUMLOCK_STATE, numlock_state); manager->priv->old_state = numlock_state; } } return GDK_FILTER_CONTINUE; } static void install_xkb_filter (GsdKeyboardManager *manager) { gdk_window_add_filter (NULL, xkb_events_filter, manager); } static void remove_xkb_filter (GsdKeyboardManager *manager) { gdk_window_remove_filter (NULL, xkb_events_filter, manager); } static void free_xkb_component_names (XkbComponentNamesRec *p) { g_return_if_fail (p != NULL); free (p->keymap); free (p->keycodes); free (p->types); free (p->compat); free (p->symbols); free (p->geometry); g_free (p); } static void upload_xkb_description (const gchar *rules_file_path, XkbRF_VarDefsRec *var_defs, XkbComponentNamesRec *comp_names) { Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XkbDescRec *xkb_desc; gchar *rules_file; /* The layout we want is always in the first XKB group index * so we should enforce it to make sure we never end up with * the wrong one. */ XkbLockGroup (display, XkbUseCoreKbd, 0); /* Upload it to the X server using the same method as setxkbmap */ xkb_desc = XkbGetKeyboardByName (display, XkbUseCoreKbd, comp_names, XkbGBN_AllComponentsMask, XkbGBN_AllComponentsMask & (~XkbGBN_GeometryMask), True); if (!xkb_desc) { g_warning ("Couldn't upload new XKB keyboard description"); return; } XkbFreeKeyboard (xkb_desc, 0, True); rules_file = g_path_get_basename (rules_file_path); if (!XkbRF_SetNamesProp (display, rules_file, var_defs)) g_warning ("Couldn't update the XKB root window property"); g_free (rules_file); } static gchar * build_xkb_group_string (const gchar *user, const gchar *locale, const gchar *latin) { gchar *string; gsize length = 0; guint commas = 2; if (latin) length += strlen (latin); else commas -= 1; if (locale) length += strlen (locale); else commas -= 1; length += strlen (user) + commas + 1; string = malloc (length); if (locale && latin) sprintf (string, "%s,%s,%s", user, locale, latin); else if (locale) sprintf (string, "%s,%s", user, locale); else if (latin) sprintf (string, "%s,%s", user, latin); else sprintf (string, "%s", user); return string; } static gboolean layout_equal (const gchar *layout_a, const gchar *variant_a, const gchar *layout_b, const gchar *variant_b) { return !g_strcmp0 (layout_a, layout_b) && !g_strcmp0 (variant_a, variant_b); } static void get_locale_layout (GsdKeyboardManager *manager, const gchar **layout, const gchar **variant) { const gchar *locale; const gchar *type; const gchar *id; gboolean got_info; *layout = NULL; *variant = NULL; locale = setlocale (LC_MESSAGES, NULL); /* If LANG is empty, default to en_US */ if (!locale) locale = DEFAULT_LANGUAGE; got_info = gnome_get_input_source_from_locale (locale, &type, &id); if (!got_info) if (!gnome_get_input_source_from_locale (DEFAULT_LANGUAGE, &type, &id)) return; if (!g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) return; gnome_xkb_info_get_layout_info (manager->priv->xkb_info, id, NULL, NULL, layout, variant); } static void replace_layout_and_variant (GsdKeyboardManager *manager, XkbRF_VarDefsRec *xkb_var_defs, const gchar *layout, const gchar *variant) { /* Toolkits need to know about both a latin layout to handle * accelerators which are usually defined like Ctrl+C and a * layout with the symbols for the language used in UI strings * to handle mnemonics like Alt+Ф, so we try to find and add * them in XKB group slots after the layout which the user * actually intends to type with. */ const gchar *latin_layout = "us"; const gchar *latin_variant = ""; const gchar *locale_layout = NULL; const gchar *locale_variant = NULL; if (!layout) return; if (!variant) variant = ""; get_locale_layout (manager, &locale_layout, &locale_variant); /* We want to minimize the number of XKB groups if we have * duplicated layout+variant pairs. * * Also, if a layout doesn't have a variant we still have to * include it in the variants string because the number of * variants must agree with the number of layouts. For * instance: * * layouts: "us,ru,us" * variants: "dvorak,," */ if (layout_equal (latin_layout, latin_variant, locale_layout, locale_variant) || layout_equal (latin_layout, latin_variant, layout, variant)) { latin_layout = NULL; latin_variant = NULL; } if (layout_equal (locale_layout, locale_variant, layout, variant)) { locale_layout = NULL; locale_variant = NULL; } free (xkb_var_defs->layout); xkb_var_defs->layout = build_xkb_group_string (layout, locale_layout, latin_layout); free (xkb_var_defs->variant); xkb_var_defs->variant = build_xkb_group_string (variant, locale_variant, latin_variant); } static gchar * build_xkb_options_string (gchar **options) { gchar *string; if (*options) { gint i; gsize len; gchar *ptr; /* First part, getting length */ len = 1 + strlen (options[0]); for (i = 1; options[i] != NULL; i++) len += strlen (options[i]); len += (i - 1); /* commas */ /* Second part, building string */ string = malloc (len); ptr = g_stpcpy (string, *options); for (i = 1; options[i] != NULL; i++) { ptr = g_stpcpy (ptr, ","); ptr = g_stpcpy (ptr, options[i]); } } else { string = malloc (1); *string = '\0'; } return string; } static gchar ** append_options (gchar **a, gchar **b) { gchar **c, **p; if (!a && !b) return NULL; else if (!a) return g_strdupv (b); else if (!b) return g_strdupv (a); c = g_new0 (gchar *, g_strv_length (a) + g_strv_length (b) + 1); p = c; while (*a) { *p = g_strdup (*a); p += 1; a += 1; } while (*b) { *p = g_strdup (*b); p += 1; b += 1; } return c; } static void strip_xkb_option (gchar **options, const gchar *prefix) { guint last; gchar **p = options; if (!p) return; while (*p) { if (g_str_has_prefix (*p, prefix)) { last = g_strv_length (options) - 1; g_free (*p); *p = options[last]; options[last] = NULL; } else { p += 1; } } } static gchar * prepare_xkb_options (GsdKeyboardManager *manager, guint n_sources, gchar **extra_options) { gchar **options; gchar **settings_options; gchar *options_str; settings_options = g_settings_get_strv (manager->priv->input_sources_settings, KEY_KEYBOARD_OPTIONS); options = append_options (settings_options, extra_options); g_strfreev (settings_options); /* We might set up different layouts in different groups - see * replace_layout_and_variant(). But we don't want the X * server group switching feature to actually switch * them. Regularly, if we have at least two input sources, * gnome-shell will tell us to switch input source at that * point so we fix that automatically. But when there's only * one source, gnome-shell short circuits as an optimization * and doesn't call us so we can't set the group switching XKB * option in the first place otherwise the X server's switch * will take effect and we get a broken configuration. */ if (n_sources < 2 || g_strcmp0 (g_getenv ("XDG_CURRENT_DESKTOP"), "Unity") == 0) strip_xkb_option (options, "grp:"); options_str = build_xkb_options_string (options); g_strfreev (options); return options_str; } static void apply_xkb_settings (GsdKeyboardManager *manager, const gchar *layout, const gchar *variant, gchar *options) { XkbRF_RulesRec *xkb_rules; XkbRF_VarDefsRec *xkb_var_defs; gchar *rules_file_path; gsd_xkb_get_var_defs (&rules_file_path, &xkb_var_defs); free (xkb_var_defs->options); xkb_var_defs->options = options; replace_layout_and_variant (manager, xkb_var_defs, layout, variant); gdk_error_trap_push (); xkb_rules = XkbRF_Load (rules_file_path, NULL, True, True); if (xkb_rules) { XkbComponentNamesRec *xkb_comp_names; xkb_comp_names = g_new0 (XkbComponentNamesRec, 1); XkbRF_GetComponents (xkb_rules, xkb_var_defs, xkb_comp_names); upload_xkb_description (rules_file_path, xkb_var_defs, xkb_comp_names); free_xkb_component_names (xkb_comp_names); XkbRF_Free (xkb_rules, True); } else { g_warning ("Couldn't load XKB rules"); } if (gdk_error_trap_pop ()) g_warning ("Error loading XKB rules"); gsd_xkb_free_var_defs (xkb_var_defs); g_free (rules_file_path); XkbLockModifiers (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XkbUseCoreKbd, LockMask, 0); } static void user_notify_is_loaded_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { ActUser *user = ACT_USER (object); GSettings *settings = user_data; if (act_user_is_loaded (user)) { GVariant *sources; GVariantIter iter; const gchar *type; const gchar *name; GVariantBuilder builder; g_signal_handlers_disconnect_by_data (user, user_data); sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{ss}")); g_variant_iter_init (&iter, sources); while (g_variant_iter_next (&iter, "(&s&s)", &type, &name)) { g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{ss}")); g_variant_builder_add (&builder, "{ss}", type, name); g_variant_builder_close (&builder); } g_variant_unref (sources); sources = g_variant_ref_sink (g_variant_builder_end (&builder)); act_user_set_input_sources (user, sources); g_variant_unref (sources); } } static void manager_notify_is_loaded_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { ActUserManager *manager = ACT_USER_MANAGER (object); gboolean loaded; g_object_get (manager, "is-loaded", &loaded, NULL); if (loaded) { ActUser *user; g_signal_handlers_disconnect_by_data (manager, user_data); user = act_user_manager_get_user (manager, g_get_user_name ()); if (act_user_is_loaded (user)) user_notify_is_loaded_cb (G_OBJECT (user), NULL, user_data); else g_signal_connect (user, "notify::is-loaded", user_notify_is_loaded_cb, user_data); } } #ifdef HAVE_FCITX static gchar * get_xkb_name (const gchar *name) { gchar *xkb_name; gchar *separator; if (g_str_has_prefix (name, FCITX_XKB_PREFIX)) name += strlen (FCITX_XKB_PREFIX); xkb_name = g_strdup (name); separator = strchr (xkb_name, '-'); if (separator) *separator = '+'; return xkb_name; } static gchar * get_fcitx_name (const gchar *name) { gchar *fcitx_name = g_strdup (name); gchar *separator = strchr (fcitx_name, '+'); if (separator) *separator = '-'; return fcitx_name; } static gboolean input_source_is_fcitx_engine (const gchar *type, const gchar *name, const gchar *engine) { if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { if (g_str_has_prefix (engine, FCITX_XKB_PREFIX)) { gboolean equal; gchar *fcitx_name = get_fcitx_name (name); equal = g_str_equal (fcitx_name, engine + strlen (FCITX_XKB_PREFIX)); g_free (fcitx_name); return equal; } } else if (g_str_equal (type, INPUT_SOURCE_TYPE_FCITX)) { return g_str_equal (name, engine); } return FALSE; } static void fcitx_engine_changed (GsdKeyboardManager *manager, GParamSpec *pspec, FcitxInputMethod *fcitx) { GSettings *settings = manager->priv->input_sources_settings; gchar *engine = fcitx_input_method_get_current_im (manager->priv->fcitx); if (engine) { GVariant *sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); guint current = g_settings_get_uint (settings, KEY_CURRENT_INPUT_SOURCE); gboolean update = TRUE; if (current >= 0 && current < g_variant_n_children (sources)) { const gchar *type; const gchar *name; g_variant_get_child (sources, current, "(&s&s)", &type, &name); update = !input_source_is_fcitx_engine (type, name, engine); } if (update) { gsize i; for (i = 0; i < g_variant_n_children (sources); i++) { const gchar *type; const gchar *name; if (i == current) continue; g_variant_get_child (sources, i, "(&s&s)", &type, &name); if (input_source_is_fcitx_engine (type, name, engine)) { g_settings_set_uint (settings, KEY_CURRENT_INPUT_SOURCE, i); break; } } } g_variant_unref (sources); g_free (engine); } } static gboolean should_update_input_sources (GVariant *sources, GPtrArray *engines) { GVariantIter iter; const gchar *type; const gchar *name; guint i = 0; g_variant_iter_init (&iter, sources); while (g_variant_iter_next (&iter, "(&s&s)", &type, &name)) { if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB) || g_str_equal (type, INPUT_SOURCE_TYPE_FCITX)) { FcitxIMItem *engine; /* get the next enabled fcitx engine */ for (; i < engines->len; i++) { engine = g_ptr_array_index (engines, i); if (engine->enable) break; } /* there should be an enabled engine and it should match, otherwise we need to update */ if (i++ >= engines->len || !input_source_is_fcitx_engine (type, name, engine->unique_name)) return TRUE; } } /* if there are more enabled engines, we need to update */ for (; i < engines->len; i++) { FcitxIMItem *engine = g_ptr_array_index (engines, i); if (engine->enable) return TRUE; } return FALSE; } static void update_input_sources_from_fcitx_engines (GsdKeyboardManager *manager, FcitxInputMethod *fcitx) { GPtrArray *engines = fcitx_input_method_get_imlist (manager->priv->fcitx); if (engines) { GVariant *sources = g_settings_get_value (manager->priv->input_sources_settings, KEY_INPUT_SOURCES); if (should_update_input_sources (sources, engines)) { GVariantBuilder builder; gboolean update = FALSE; guint i; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); for (i = 0; i < engines->len; i++) { FcitxIMItem *engine = g_ptr_array_index (engines, i); if (engine->enable) { if (g_str_has_prefix (engine->unique_name, FCITX_XKB_PREFIX)) { gchar *name = get_xkb_name (engine->unique_name); g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_XKB, name); g_free (name); } else { g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_FCITX, engine->unique_name); } update = TRUE; } } if (update) g_settings_set_value (manager->priv->input_sources_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); else g_variant_builder_clear (&builder); } g_variant_unref (sources); g_ptr_array_unref (engines); } } #endif static gboolean apply_input_source (GsdKeyboardManager *manager, guint current) { GsdKeyboardManagerPrivate *priv = manager->priv; GVariant *sources; guint n_sources; const gchar *type = NULL; const gchar *id = NULL; gchar *layout = NULL; gchar *variant = NULL; gchar **options = NULL; sources = g_settings_get_value (priv->input_sources_settings, KEY_INPUT_SOURCES); n_sources = g_variant_n_children (sources); if (n_sources < 1) goto exit; else if (current >= n_sources) current = n_sources - 1; priv->active_input_source = current; #ifdef HAVE_IBUS if (priv->is_ibus_active) { maybe_start_ibus (manager); } #endif g_variant_get_child (sources, current, "(&s&s)", &type, &id); if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { const gchar *l, *v; gnome_xkb_info_get_layout_info (priv->xkb_info, id, NULL, NULL, &l, &v); layout = g_strdup (l); variant = g_strdup (v); if (!layout || !layout[0]) { g_warning ("Couldn't find XKB input source '%s'", id); goto exit; } #ifdef HAVE_FCITX if (priv->is_fcitx_active && priv->fcitx) { gchar *name = g_strdup_printf (FCITX_XKB_PREFIX "%s", id); gchar *fcitx_name = get_fcitx_name (name); fcitx_input_method_set_current_im (priv->fcitx, fcitx_name); g_free (fcitx_name); g_free (name); } #endif #ifdef HAVE_IBUS if (priv->is_ibus_active) { set_gtk_im_module (manager, sources); set_ibus_xkb_engine (manager); } #endif } else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { #ifdef HAVE_IBUS if (priv->is_ibus_active) { IBusEngineDesc *engine_desc = NULL; if (priv->ibus_engines) engine_desc = g_hash_table_lookup (priv->ibus_engines, id); else goto exit; /* we'll be called again when ibus is up and running */ if (engine_desc) { const gchar *ibus_layout; ibus_layout = ibus_engine_desc_get_layout (engine_desc); if (ibus_layout) { layout = layout_from_ibus_layout (ibus_layout); variant = variant_from_ibus_layout (ibus_layout); options = options_from_ibus_layout (ibus_layout); } } else { g_warning ("Couldn't find IBus input source '%s'", id); goto exit; } /* NULL here is a shortcut for "I already know I need the IBus module". */ set_gtk_im_module (manager, NULL); set_ibus_engine (manager, id); } else { g_warning ("IBus input source type specified but IBus is not active"); } #else g_warning ("IBus input source type specified but IBus support was not compiled"); #endif } else if (g_str_equal (type, INPUT_SOURCE_TYPE_FCITX)) { #ifdef HAVE_FCITX if (priv->is_fcitx_active) { if (priv->fcitx) { gchar *name = g_strdup (id); fcitx_input_method_set_current_im (priv->fcitx, name); g_free (name); } else { g_warning ("Fcitx input method framework unavailable"); } } else { g_warning ("Fcitx input source type specified but Fcitx is not active"); } #else g_warning ("Fcitx input source type specified but Fcitx support was not compiled"); #endif } else { g_warning ("Unknown input source type '%s'", type); } #ifdef HAVE_FCITX if (priv->is_fcitx_active && priv->fcitx && !priv->fcitx_signal_id) { priv->fcitx_signal_id = g_signal_connect_swapped (manager->priv->fcitx, "notify::current-im", G_CALLBACK (fcitx_engine_changed), manager); g_signal_connect_swapped (manager->priv->fcitx, "imlist-changed", G_CALLBACK (update_input_sources_from_fcitx_engines), manager); } #endif exit: apply_xkb_settings (manager, layout, variant, prepare_xkb_options (manager, n_sources, options)); maybe_return_from_set_input_source (manager); g_variant_unref (sources); g_free (layout); g_free (variant); g_strfreev (options); return TRUE; } #ifdef HAVE_FCITX static const gchar * get_fcitx_engine_for_ibus_engine (const gchar *ibus_engine) { const struct Engine *engine; if (!ibus_engine) return NULL; engine = get_engine_for_ibus_engine (ibus_engine, strlen (ibus_engine)); return engine ? engine->fcitx_engine : NULL; } static void enable_fcitx_engines (GsdKeyboardManager *manager, gboolean migrate) { GsdKeyboardManagerPrivate *priv = manager->priv; GPtrArray *engines; GVariant *sources; GVariantIter iter; const gchar *type; const gchar *name; gboolean changed; guint i; guint j; engines = fcitx_input_method_get_imlist (priv->fcitx); if (!engines) { g_warning ("Cannot update Fcitx engine list"); return; } sources = g_settings_get_value (priv->input_sources_settings, KEY_INPUT_SOURCES); changed = FALSE; i = 0; g_variant_iter_init (&iter, sources); while (g_variant_iter_next (&iter, "(&s&s)", &type, &name)) { if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB)) { gchar *fcitx_name = get_fcitx_name (name); for (j = i; j < engines->len; j++) { FcitxIMItem *engine = g_ptr_array_index (engines, j); if (g_str_has_prefix (engine->unique_name, FCITX_XKB_PREFIX) && g_str_equal (engine->unique_name + strlen (FCITX_XKB_PREFIX), fcitx_name)) { if (!engine->enable) { engine->enable = TRUE; changed = TRUE; } break; } } g_free (fcitx_name); /* j is either the index of the engine "fcitx-keyboard-" * or engines->len, meaning it wasn't found. */ } else if (migrate && g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) { const gchar *fcitx_name = get_fcitx_engine_for_ibus_engine (name); if (!fcitx_name) continue; for (j = i; j < engines->len; j++) { FcitxIMItem *engine = g_ptr_array_index (engines, j); if (g_str_equal (engine->unique_name, fcitx_name)) { if (!engine->enable) { engine->enable = TRUE; changed = TRUE; } break; } } /* j is either the index of the engine "" * or engines->len, meaning it wasn't found. */ } else if (g_str_equal (type, INPUT_SOURCE_TYPE_FCITX)) { for (j = i; j < engines->len; j++) { FcitxIMItem *engine = g_ptr_array_index (engines, j); if (g_str_equal (engine->unique_name, name)) { if (!engine->enable) { engine->enable = TRUE; changed = TRUE; } break; } } /* j is either the index of the engine "" * or engines->len, meaning it wasn't found. */ } else { continue; } if (j < engines->len) { if (j != i) { gpointer ptr = engines->pdata[i]; engines->pdata[i] = engines->pdata[j]; engines->pdata[j] = ptr; changed = TRUE; } i++; } else { g_warning ("Fcitx engine not found for %s", name); } } /* we should have i enabled fcitx engines: disable everything else. */ for (; i < engines->len; i++) { FcitxIMItem *engine = g_ptr_array_index (engines, i); if (engine->enable) { engine->enable = FALSE; changed = TRUE; } } if (changed) { fcitx_input_method_set_imlist (priv->fcitx, engines); } g_variant_unref (sources); g_ptr_array_unref (engines); } struct _FcitxShareStateConfig { FcitxGenericConfig config; gint share_state; }; typedef struct _FcitxShareStateConfig FcitxShareStateConfig; static CONFIG_BINDING_BEGIN (FcitxShareStateConfig) CONFIG_BINDING_REGISTER ("Program", "ShareStateAmongWindow", share_state) CONFIG_BINDING_END () static CONFIG_DESC_DEFINE (get_fcitx_config_desc, "config.desc") static void update_share_state_from_per_window (GsdKeyboardManager *manager) { FcitxConfigFileDesc *config_file_desc = get_fcitx_config_desc (); if (config_file_desc) { /* Set Fcitx' share state setting based on the GSettings per-window option. */ GSettings *settings = g_settings_new ("org.gnome.libgnomekbd.desktop"); gboolean per_window = g_settings_get_boolean (settings, "group-per-window"); FcitxShareStateConfig config = { { NULL } }; /* Load the user's Fcitx configuration. */ FILE *file = FcitxXDGGetFileUserWithPrefix (NULL, "config", "r", NULL); FcitxConfigFile *config_file = FcitxConfigParseConfigFileFp (file, config_file_desc); FcitxShareStateConfigConfigBind (&config, config_file, config_file_desc); if (file) fclose (file); config.share_state = per_window ? 0 : 1; /* Save the user's Fcitx configuration. */ file = FcitxXDGGetFileUserWithPrefix (NULL, "config", "w", NULL); FcitxConfigSaveConfigFileFp (file, &config.config, config_file_desc); if (file) fclose (file); fcitx_input_method_reload_config (manager->priv->fcitx); g_object_unref (settings); } } static void migrate_fcitx_engines (GsdKeyboardManager *manager) { GPtrArray *engines = fcitx_input_method_get_imlist (manager->priv->fcitx); if (engines) { gboolean migrate = FALSE; gboolean second = FALSE; guint i; /* Migrate if there are at least two enabled fcitx engines or * there is one fcitx engine which is not a keyboard layout. * These are our criteria for determining which direction to * sync fcitx engines with input sources. */ for (i = 0; i < engines->len; i++) { FcitxIMItem *engine = g_ptr_array_index (engines, i); if (engine->enable) { if (second || !g_str_has_prefix (engine->unique_name, FCITX_XKB_PREFIX)) { migrate = TRUE; break; } second = TRUE; } } if (migrate) { /* Migrate Fcitx to GSettings. */ update_input_sources_from_fcitx_engines (manager, manager->priv->fcitx); } else { /* Migrate GSettings to Fcitx. */ enable_fcitx_engines (manager, TRUE); update_share_state_from_per_window (manager); } g_ptr_array_unref (engines); } } #endif static gboolean apply_input_sources_settings (GSettings *settings, gpointer keys, gint n_keys, GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; GVariant *sources; guint n_sources; guint current; ActUserManager *user_manager; gboolean user_manager_loaded; #ifdef HAVE_FCITX if (priv->is_fcitx_active) { gboolean sources_changed = FALSE; if (keys) { GQuark *quarks = keys; gint i; for (i = 0; i < n_keys; i++) { if (quarks[i] == g_quark_try_string (KEY_INPUT_SOURCES)) { sources_changed = TRUE; break; } } } else { sources_changed = TRUE; } if (sources_changed) { enable_fcitx_engines (manager, FALSE); } } #endif sources = g_settings_get_value (priv->input_sources_settings, KEY_INPUT_SOURCES); n_sources = g_variant_n_children (sources); if (n_sources < 1) { apply_xkb_settings (manager, NULL, NULL, prepare_xkb_options (manager, 0, NULL)); maybe_return_from_set_input_source (manager); goto exit; } current = g_settings_get_uint (priv->input_sources_settings, KEY_CURRENT_INPUT_SOURCE); if (current >= n_sources) { g_settings_set_uint (priv->input_sources_settings, KEY_CURRENT_INPUT_SOURCE, n_sources - 1); goto exit; } user_manager = act_user_manager_get_default (); g_object_get (user_manager, "is-loaded", &user_manager_loaded, NULL); if (user_manager_loaded) manager_notify_is_loaded_cb (G_OBJECT (user_manager), NULL, priv->input_sources_settings); else g_signal_connect (user_manager, "notify::is-loaded", G_CALLBACK (manager_notify_is_loaded_cb), priv->input_sources_settings); apply_input_source (manager, current); exit: g_variant_unref (sources); /* Prevent individual "changed" signal invocations since we don't need them. */ return TRUE; } static void apply_bell (GsdKeyboardManager *manager) { GSettings *settings; XKeyboardControl kbdcontrol; gboolean click; int bell_volume; int bell_pitch; int bell_duration; GsdBellMode bell_mode; int click_volume; g_debug ("Applying the bell settings"); settings = manager->priv->settings; click = g_settings_get_boolean (settings, KEY_CLICK); click_volume = g_settings_get_int (settings, KEY_CLICK_VOLUME); bell_pitch = g_settings_get_int (settings, KEY_BELL_PITCH); bell_duration = g_settings_get_int (settings, KEY_BELL_DURATION); bell_mode = g_settings_get_enum (settings, KEY_BELL_MODE); bell_volume = (bell_mode == GSD_BELL_MODE_ON) ? 50 : 0; /* as percentage from 0..100 inclusive */ if (click_volume < 0) { click_volume = 0; } else if (click_volume > 100) { click_volume = 100; } kbdcontrol.key_click_percent = click ? click_volume : 0; kbdcontrol.bell_percent = bell_volume; kbdcontrol.bell_pitch = bell_pitch; kbdcontrol.bell_duration = bell_duration; gdk_error_trap_push (); XChangeKeyboardControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), KBKeyClickPercent | KBBellPercent | KBBellPitch | KBBellDuration, &kbdcontrol); XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); gdk_error_trap_pop_ignored (); } static void apply_numlock (GsdKeyboardManager *manager) { GSettings *settings; gboolean rnumlock; g_debug ("Applying the num-lock settings"); settings = manager->priv->settings; rnumlock = g_settings_get_boolean (settings, KEY_REMEMBER_NUMLOCK_STATE); manager->priv->old_state = g_settings_get_enum (manager->priv->settings, KEY_NUMLOCK_STATE); gdk_error_trap_push (); if (rnumlock) { g_debug ("Remember num-lock is set, so applying setting '%s'", num_lock_state_to_string (manager->priv->old_state)); numlock_set_xkb_state (manager->priv->old_state); } XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); gdk_error_trap_pop_ignored (); } static void apply_repeat (GsdKeyboardManager *manager) { GSettings *settings; gboolean repeat; guint interval; guint delay; g_debug ("Applying the repeat settings"); settings = manager->priv->gsettings; repeat = g_settings_get_boolean (settings, KEY_REPEAT); interval = g_settings_get_uint (settings, KEY_INTERVAL); delay = g_settings_get_uint (settings, KEY_DELAY); gdk_error_trap_push (); if (repeat) { gboolean rate_set = FALSE; XAutoRepeatOn (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); /* Use XKB in preference */ rate_set = xkb_set_keyboard_autorepeat_rate (delay, interval); if (!rate_set) g_warning ("Neither XKeyboard not Xfree86's keyboard extensions are available,\n" "no way to support keyboard autorepeat rate settings"); } else { XAutoRepeatOff (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); } XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE); gdk_error_trap_pop_ignored (); } static void apply_all_settings (GsdKeyboardManager *manager) { apply_repeat (manager); apply_bell (manager); apply_numlock (manager); } static void settings_changed (GSettings *settings, const char *key, GsdKeyboardManager *manager) { if (g_strcmp0 (key, KEY_CLICK) == 0|| g_strcmp0 (key, KEY_CLICK_VOLUME) == 0 || g_strcmp0 (key, KEY_BELL_PITCH) == 0 || g_strcmp0 (key, KEY_BELL_DURATION) == 0 || g_strcmp0 (key, KEY_BELL_MODE) == 0) { g_debug ("Bell setting '%s' changed, applying bell settings", key); apply_bell (manager); } else if (g_strcmp0 (key, KEY_REMEMBER_NUMLOCK_STATE) == 0) { g_debug ("Remember Num-Lock state '%s' changed, applying num-lock settings", key); apply_numlock (manager); } else if (g_strcmp0 (key, KEY_NUMLOCK_STATE) == 0) { g_debug ("Num-Lock state '%s' changed, will apply at next startup", key); } else { g_warning ("Unhandled settings change, key '%s'", key); } } static void gsettings_changed (GSettings *settings, const char *key, GsdKeyboardManager *manager) { if (g_strcmp0 (key, KEY_REPEAT) == 0 || g_strcmp0 (key, KEY_INTERVAL) == 0 || g_strcmp0 (key, KEY_DELAY) == 0) { g_debug ("Key repeat setting '%s' changed, applying key repeat settings", key); apply_repeat (manager); } else if (g_strcmp0 (key, KEY_BELL_CUSTOM_FILE) == 0){ g_debug ("Ignoring '%s' setting change", KEY_BELL_CUSTOM_FILE); } else { g_warning ("Unhandled settings change, key '%s'", key); } } static void device_added_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdKeyboardManager *manager) { GdkInputSource source; source = gdk_device_get_source (device); if (source == GDK_SOURCE_KEYBOARD) { g_debug ("New keyboard plugged in, applying all settings"); apply_numlock (manager); apply_input_sources_settings (manager->priv->input_sources_settings, NULL, 0, manager); run_custom_command (device, COMMAND_DEVICE_ADDED); } } static void device_removed_cb (GdkDeviceManager *device_manager, GdkDevice *device, GsdKeyboardManager *manager) { GdkInputSource source; source = gdk_device_get_source (device); if (source == GDK_SOURCE_KEYBOARD) { run_custom_command (device, COMMAND_DEVICE_REMOVED); } } static void set_devicepresence_handler (GsdKeyboardManager *manager) { GdkDeviceManager *device_manager; device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); manager->priv->device_added_id = g_signal_connect (G_OBJECT (device_manager), "device-added", G_CALLBACK (device_added_cb), manager); manager->priv->device_removed_id = g_signal_connect (G_OBJECT (device_manager), "device-removed", G_CALLBACK (device_removed_cb), manager); manager->priv->device_manager = device_manager; } static void get_sources_from_xkb_config (GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; GVariantBuilder builder; GVariant *v; gint i, n; gchar **layouts = NULL; gchar **variants = NULL; gboolean have_default_layout = FALSE; v = g_dbus_proxy_get_cached_property (priv->localed, "X11Layout"); if (v) { const gchar *s = g_variant_get_string (v, NULL); if (*s) layouts = g_strsplit (s, ",", -1); g_variant_unref (v); } if (!layouts) return; v = g_dbus_proxy_get_cached_property (priv->localed, "X11Variant"); if (v) { const gchar *s = g_variant_get_string (v, NULL); if (*s) variants = g_strsplit (s, ",", -1); g_variant_unref (v); } if (variants && variants[0]) n = MIN (g_strv_length (layouts), g_strv_length (variants)); else n = g_strv_length (layouts); init_builder_with_sources (&builder, priv->input_sources_settings); for (i = 0; i < n && layouts[i][0]; ++i) { gchar *id; if (variants && variants[i] && variants[i][0]) id = g_strdup_printf ("%s+%s", layouts[i], variants[i]); else id = g_strdup (layouts[i]); if (g_str_equal (id, DEFAULT_LAYOUT)) have_default_layout = TRUE; g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_XKB, id); g_free (id); } if (!have_default_layout) g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_XKB, DEFAULT_LAYOUT); g_settings_set_value (priv->input_sources_settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); g_strfreev (layouts); g_strfreev (variants); } static void get_options_from_xkb_config (GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; GVariant *v; gchar **options = NULL; v = g_dbus_proxy_get_cached_property (priv->localed, "X11Options"); if (v) { const gchar *s = g_variant_get_string (v, NULL); if (*s) options = g_strsplit (s, ",", -1); g_variant_unref (v); } if (!options) return; g_settings_set_strv (priv->input_sources_settings, KEY_KEYBOARD_OPTIONS, (const gchar * const*) options); g_strfreev (options); } static void convert_libgnomekbd_options (GSettings *settings) { GPtrArray *opt_array; GSettings *libgnomekbd_settings; gchar **options, **o; if (!schema_is_installed ("org.gnome.libgnomekbd.keyboard")) return; opt_array = g_ptr_array_new_with_free_func (g_free); libgnomekbd_settings = g_settings_new ("org.gnome.libgnomekbd.keyboard"); options = g_settings_get_strv (libgnomekbd_settings, "options"); for (o = options; *o; ++o) { gchar **strv; strv = g_strsplit (*o, "\t", 2); if (strv[0] && strv[1]) g_ptr_array_add (opt_array, g_strdup (strv[1])); g_strfreev (strv); } g_ptr_array_add (opt_array, NULL); g_settings_set_strv (settings, KEY_KEYBOARD_OPTIONS, (const gchar * const*) opt_array->pdata); g_strfreev (options); g_object_unref (libgnomekbd_settings); g_ptr_array_free (opt_array, TRUE); } static void convert_libgnomekbd_layouts (GSettings *settings) { GVariantBuilder builder; GSettings *libgnomekbd_settings; gchar **layouts, **l; if (!schema_is_installed ("org.gnome.libgnomekbd.keyboard")) return; init_builder_with_sources (&builder, settings); libgnomekbd_settings = g_settings_new ("org.gnome.libgnomekbd.keyboard"); layouts = g_settings_get_strv (libgnomekbd_settings, "layouts"); for (l = layouts; *l; ++l) { gchar *id; gchar **strv; strv = g_strsplit (*l, "\t", 2); if (strv[0] && !strv[1]) id = g_strdup (strv[0]); else if (strv[0] && strv[1]) id = g_strdup_printf ("%s+%s", strv[0], strv[1]); else id = NULL; if (id) g_variant_builder_add (&builder, "(ss)", INPUT_SOURCE_TYPE_XKB, id); g_free (id); g_strfreev (strv); } g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); g_strfreev (layouts); g_object_unref (libgnomekbd_settings); } static gboolean should_migrate (const gchar *id) { gboolean migrate = FALSE; gchar *stamp_dir_path = NULL; gchar *stamp_file_path = NULL; GError *error = NULL; stamp_dir_path = g_build_filename (g_get_user_data_dir (), PACKAGE_NAME, NULL); if (g_mkdir_with_parents (stamp_dir_path, 0755)) { g_warning ("Failed to create directory %s: %s", stamp_dir_path, g_strerror (errno)); goto out; } stamp_file_path = g_build_filename (stamp_dir_path, id, NULL); if (g_file_test (stamp_file_path, G_FILE_TEST_EXISTS)) goto out; migrate = TRUE; if (!g_file_set_contents (stamp_file_path, "", 0, &error)) { g_warning ("%s", error->message); g_error_free (error); } out: g_free (stamp_file_path); g_free (stamp_dir_path); return migrate; } static void maybe_convert_old_settings (GSettings *settings) { if (should_migrate ("input-sources-converted")) { GVariant *sources; gchar **options; sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); if (g_variant_n_children (sources) < 1) { convert_libgnomekbd_layouts (settings); #ifdef HAVE_IBUS convert_ibus (settings); #endif } g_variant_unref (sources); options = g_settings_get_strv (settings, KEY_KEYBOARD_OPTIONS); if (g_strv_length (options) < 1) convert_libgnomekbd_options (settings); g_strfreev (options); } } static void maybe_create_initial_settings (GsdKeyboardManager *manager) { GSettings *settings; GVariant *sources; gchar **options; settings = manager->priv->input_sources_settings; if (g_getenv ("RUNNING_UNDER_GDM")) { GVariantBuilder builder; /* clean the settings and get them from the "system" */ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)")); g_settings_set_value (settings, KEY_INPUT_SOURCES, g_variant_builder_end (&builder)); get_sources_from_xkb_config (manager); g_settings_set_strv (settings, KEY_KEYBOARD_OPTIONS, NULL); get_options_from_xkb_config (manager); return; } maybe_convert_old_settings (settings); /* if we still don't have anything do some educated guesses */ sources = g_settings_get_value (settings, KEY_INPUT_SOURCES); if (g_variant_n_children (sources) < 1) { get_sources_from_xkb_config (manager); #if defined(HAVE_IBUS) || defined(HAVE_FCITX) add_sources_from_locale (manager, settings); #endif } g_variant_unref (sources); options = g_settings_get_strv (settings, KEY_KEYBOARD_OPTIONS); if (g_strv_length (options) < 1) get_options_from_xkb_config (manager); g_strfreev (options); } static void set_input_source_return (GDBusMethodInvocation *invocation) { g_dbus_method_invocation_return_value (invocation, NULL); } static void maybe_return_from_set_input_source (GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; if (!priv->invocation) return; if (priv->pending_ops > 0) { priv->pending_ops -= 1; return; } g_clear_pointer (&priv->invocation, set_input_source_return); } static void increment_set_input_source_ops (GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; if (!priv->invocation) return; priv->pending_ops += 1; } static void set_input_source (GsdKeyboardManager *manager, gboolean persist) { GsdKeyboardManagerPrivate *priv = manager->priv; guint idx; g_variant_get (g_dbus_method_invocation_get_parameters (priv->invocation), "(u)", &idx); if (persist) { if (idx == priv->active_input_source) { if (idx == g_settings_get_uint (priv->input_sources_settings, KEY_CURRENT_INPUT_SOURCE)) { maybe_return_from_set_input_source (manager); return; } } g_settings_set_uint (priv->input_sources_settings, KEY_CURRENT_INPUT_SOURCE, idx); } else { if (idx == priv->active_input_source) { maybe_return_from_set_input_source (manager); return; } apply_input_source (manager, idx); } } static void handle_dbus_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv = manager->priv; gboolean is_set_input_source; gboolean is_activate_input_source; is_set_input_source = g_str_equal (method_name, "SetInputSource"); is_activate_input_source = g_str_equal (method_name, "ActivateInputSource"); if (is_set_input_source || is_activate_input_source) { if (priv->invocation) { #ifdef HAVE_IBUS if (priv->is_ibus_active) { /* This can only happen if there's an * ibus_bus_set_global_engine_async() call * going on. */ g_cancellable_cancel (priv->ibus_cancellable); } #endif g_clear_pointer (&priv->invocation, set_input_source_return); priv->pending_ops = 0; } priv->invocation = invocation; set_input_source (manager, is_set_input_source); } } static void on_bus_name_lost (GDBusConnection *connection, const gchar *name, gpointer data) { g_warning ("DBus name %s lost", name); } static void got_session_bus (GObject *source, GAsyncResult *res, GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *priv; GDBusConnection *connection; GDBusInterfaceInfo **interfaces; GError *error = NULL; GDBusInterfaceVTable vtable = { (GDBusInterfaceMethodCallFunc) handle_dbus_method_call, NULL, NULL, }; connection = g_bus_get_finish (res, &error); if (!connection) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Couldn't get session bus: %s", error->message); g_error_free (error); return; } priv = manager->priv; priv->dbus_connection = connection; interfaces = priv->dbus_introspection->interfaces; if (interfaces) { for (; *interfaces; interfaces++) { guint id = g_dbus_connection_register_object (priv->dbus_connection, GSD_KEYBOARD_DBUS_PATH, *interfaces, &vtable, manager, NULL, &error); if (!id) { GSList *ids; for (ids = priv->dbus_register_object_ids; ids; ids = g_slist_next (ids)) g_dbus_connection_unregister_object (priv->dbus_connection, GPOINTER_TO_UINT (ids->data)); g_slist_free (priv->dbus_register_object_ids); priv->dbus_register_object_ids = NULL; g_warning ("Error registering object: %s", error->message); g_error_free (error); return; } priv->dbus_register_object_ids = g_slist_prepend (priv->dbus_register_object_ids, GUINT_TO_POINTER (id)); } } priv->dbus_own_name_id = g_bus_own_name_on_connection (priv->dbus_connection, GSD_KEYBOARD_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, on_bus_name_lost, NULL, NULL); } static void register_manager_dbus (GsdKeyboardManager *manager) { GError *error = NULL; manager->priv->dbus_introspection = g_dbus_node_info_new_for_xml (introspection_xml, &error); if (error) { g_warning ("Error creating introspection data: %s", error->message); g_error_free (error); return; } g_bus_get (G_BUS_TYPE_SESSION, manager->priv->cancellable, (GAsyncReadyCallback) got_session_bus, manager); } static void localed_proxy_ready (GObject *source, GAsyncResult *res, gpointer data) { GsdKeyboardManager *manager = data; GDBusProxy *proxy; GError *error = NULL; proxy = g_dbus_proxy_new_finish (res, &error); if (!proxy) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } g_warning ("Failed to contact localed: %s", error->message); g_error_free (error); goto out; } manager->priv->localed = proxy; maybe_create_initial_settings (manager); out: apply_input_sources_settings (manager->priv->input_sources_settings, NULL, 0, manager); register_manager_dbus (manager); } #ifdef HAVE_FCITX static void fcitx_appeared (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GsdKeyboardManager *manager = user_data; apply_input_sources_settings (manager->priv->input_sources_settings, NULL, 0, manager); } static void fcitx_vanished (GDBusConnection *connection, const gchar *name, gpointer user_data) { GsdKeyboardManager *manager = user_data; g_signal_handlers_disconnect_by_data (manager->priv->fcitx, manager); manager->priv->fcitx_signal_id = 0; } #endif static gboolean start_keyboard_idle_cb (GsdKeyboardManager *manager) { const gchar *module; GError *error = NULL; gnome_settings_profile_start (NULL); g_debug ("Starting keyboard manager"); module = g_getenv (ENV_GTK_IM_MODULE); #ifdef HAVE_IBUS manager->priv->is_ibus_active = g_strcmp0 (module, GTK_IM_MODULE_IBUS) == 0; #endif #ifdef HAVE_FCITX manager->priv->is_fcitx_active = g_strcmp0 (module, GTK_IM_MODULE_FCITX) == 0; #endif manager->priv->settings = g_settings_new (GSD_KEYBOARD_DIR); manager->priv->gsettings = g_settings_new (GSETTINGS_KEYBOARD_SCHEMA); xkb_init (manager); set_devicepresence_handler (manager); manager->priv->input_sources_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR); manager->priv->interface_settings = g_settings_new (GNOME_DESKTOP_INTERFACE_DIR); manager->priv->xkb_info = gnome_xkb_info_new (); #ifdef HAVE_FCITX if (manager->priv->is_fcitx_active) { manager->priv->fcitx_cancellable = g_cancellable_new (); manager->priv->fcitx = fcitx_input_method_new (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, 0, manager->priv->fcitx_cancellable, &error); g_clear_object (&manager->priv->fcitx_cancellable); if (manager->priv->fcitx) { manager->priv->fcitx_signal_id = 0; g_bus_watch_name (G_BUS_TYPE_SESSION, "org.fcitx.Fcitx", G_BUS_NAME_WATCHER_FLAGS_NONE, fcitx_appeared, fcitx_vanished, manager, NULL); if (should_migrate ("fcitx-engines-migrated")) migrate_fcitx_engines (manager); else enable_fcitx_engines (manager, FALSE); } else { g_warning ("Fcitx input method framework unavailable: %s", error->message); g_error_free (error); } } #endif manager->priv->cancellable = g_cancellable_new (); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.locale1", "/org/freedesktop/locale1", "org.freedesktop.locale1", manager->priv->cancellable, localed_proxy_ready, manager); /* apply current settings before we install the callback */ g_debug ("Started the keyboard plugin, applying all settings"); apply_all_settings (manager); g_signal_connect (G_OBJECT (manager->priv->settings), "changed", G_CALLBACK (settings_changed), manager); g_signal_connect (G_OBJECT (manager->priv->gsettings), "changed", G_CALLBACK (gsettings_changed), manager); g_signal_connect (G_OBJECT (manager->priv->input_sources_settings), "change-event", G_CALLBACK (apply_input_sources_settings), manager); install_xkb_filter (manager); gnome_settings_profile_end (NULL); manager->priv->start_idle_id = 0; return FALSE; } gboolean gsd_keyboard_manager_start (GsdKeyboardManager *manager, GError **error) { gnome_settings_profile_start (NULL); if (check_xkb_extension (manager) == FALSE) { g_debug ("XKB is not supported, not applying any settings"); return TRUE; } manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_keyboard_idle_cb, manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_keyboard_manager_stop (GsdKeyboardManager *manager) { GsdKeyboardManagerPrivate *p = manager->priv; GSList *ids; g_debug ("Stopping keyboard manager"); if (p->dbus_own_name_id) { g_bus_unown_name (p->dbus_own_name_id); p->dbus_own_name_id = 0; } for (ids = p->dbus_register_object_ids; ids; ids = g_slist_next (ids)) g_dbus_connection_unregister_object (p->dbus_connection, GPOINTER_TO_UINT (ids->data)); g_slist_free (p->dbus_register_object_ids); p->dbus_register_object_ids = NULL; g_cancellable_cancel (p->cancellable); g_clear_object (&p->cancellable); g_clear_object (&p->settings); g_clear_object (&p->input_sources_settings); g_clear_object (&p->interface_settings); g_clear_object (&p->xkb_info); g_clear_object (&p->localed); #ifdef HAVE_FCITX if (p->is_fcitx_active) { if (p->fcitx_cancellable) { g_cancellable_cancel (p->fcitx_cancellable); } g_clear_object (&p->fcitx_cancellable); g_clear_object (&p->fcitx); } #endif #ifdef HAVE_IBUS if (p->is_ibus_active) { clear_ibus (manager); } #endif if (p->device_manager != NULL) { g_signal_handler_disconnect (p->device_manager, p->device_added_id); g_signal_handler_disconnect (p->device_manager, p->device_removed_id); p->device_manager = NULL; } remove_xkb_filter (manager); g_clear_pointer (&p->invocation, set_input_source_return); g_clear_pointer (&p->dbus_introspection, g_dbus_node_info_unref); g_clear_object (&p->dbus_connection); } static void gsd_keyboard_manager_class_init (GsdKeyboardManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_keyboard_manager_finalize; g_type_class_add_private (klass, sizeof (GsdKeyboardManagerPrivate)); } static void gsd_keyboard_manager_init (GsdKeyboardManager *manager) { manager->priv = GSD_KEYBOARD_MANAGER_GET_PRIVATE (manager); manager->priv->active_input_source = -1; } static void gsd_keyboard_manager_finalize (GObject *object) { GsdKeyboardManager *keyboard_manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_KEYBOARD_MANAGER (object)); keyboard_manager = GSD_KEYBOARD_MANAGER (object); g_return_if_fail (keyboard_manager->priv != NULL); if (keyboard_manager->priv->start_idle_id != 0) g_source_remove (keyboard_manager->priv->start_idle_id); G_OBJECT_CLASS (gsd_keyboard_manager_parent_class)->finalize (object); } static void migrate_keyboard_settings (void) { GsdSettingsMigrateEntry entries[] = { { "repeat", "repeat", NULL }, { "repeat-interval", "repeat-interval", NULL }, { "delay", "delay", NULL } }; gsd_settings_migrate_check ("org.gnome.settings-daemon.peripherals.keyboard.deprecated", "/org/gnome/settings-daemon/peripherals/keyboard/", "org.gnome.desktop.peripherals.keyboard", "/org/gnome/desktop/peripherals/keyboard/", entries, G_N_ELEMENTS (entries)); } GsdKeyboardManager * gsd_keyboard_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { migrate_keyboard_settings (); manager_object = g_object_new (GSD_TYPE_KEYBOARD_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_KEYBOARD_MANAGER (manager_object); } ./plugins/keyboard/kbd-capslock-on.png0000644000004100000410000000272012735467744020225 0ustar www-datawww-dataPNG  IHDR@@iqsRGBbKGD pHYsnNtIME:=PIDATxKhE{#4YSZ|!u!nJk(iDtck] ҅(E4ޒB >jF%!M VHLdr{M3g93"#P 40q`0ӬGN`iR`!/_ԅ^+pH\ڜa$xZKr{IO%8۴3q|Bu \S0PQo1Rw 䁻y9+P,[`h@[`z 4+WD9 fC#۪p,/ tq vQ)|_z|xWÃ[%dZK`n5ckGJ(i2Ljʥ5n=hD͒<R^E綃d f ; @WsGآ 04Lv<&Sc)- `*ףɴ YL0,+0nIrd=Ydei!n1Y[@ 8qSmeN#Cn'dnP <2f. VY@1ǜ(T>W4h1`SfmZ῱yt ,w ~_F`tx둟eӽ2\9*8^ *!bs&00Č,uIh:ogMJ' ][bÚGVk r}Pw! ";*1iUIG U Io WQT7J0r\{,ֹ@( |ؙ$p\8y @ҍ/P@)}1qh#jvI^1 -q'HL r,62er_ 9Smj<"Id{L.K<^>%-{Tv!/Җ„:'N(KSB/M%6 l絹 3)\R&/ stTSO8`*K598C[{vT ql/SU_BcoN|H0IENDB`./plugins/keyboard/gsd-xkb-utils.c0000644000004100000410000000460112735467744017411 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 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 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, see . * */ #include "config.h" #include #include #include #include #include #include #include "gsd-xkb-utils.h" #ifndef XKB_RULES_FILE #define XKB_RULES_FILE "evdev" #endif #ifndef XKB_LAYOUT #define XKB_LAYOUT "us" #endif #ifndef XKB_MODEL #define XKB_MODEL "pc105+inet" #endif void gsd_xkb_get_var_defs (char **rules, XkbRF_VarDefsRec **var_defs) { Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); char *tmp; g_return_if_fail (rules != NULL); g_return_if_fail (var_defs != NULL); *rules = NULL; *var_defs = g_new0 (XkbRF_VarDefsRec, 1); gdk_error_trap_push (); /* Get it from the X property or fallback on defaults */ if (!XkbRF_GetNamesProp (display, rules, *var_defs) || !*rules) { *rules = strdup (XKB_RULES_FILE); (*var_defs)->model = strdup (XKB_MODEL); (*var_defs)->layout = strdup (XKB_LAYOUT); (*var_defs)->variant = NULL; (*var_defs)->options = NULL; } gdk_error_trap_pop_ignored (); tmp = *rules; if (*rules[0] == '/') *rules = g_strdup (*rules); else *rules = g_build_filename (XKB_BASE, "rules", *rules, NULL); free (tmp); } void gsd_xkb_free_var_defs (XkbRF_VarDefsRec *var_defs) { g_return_if_fail (var_defs != NULL); free (var_defs->model); free (var_defs->layout); free (var_defs->variant); free (var_defs->options); g_free (var_defs); } ./plugins/keyboard/Makefile.am0000644000004100000410000000474112735467744016611 0ustar www-datawww-dataNULL = plugin_name = keyboard plugin_LTLIBRARIES = \ libkeyboard.la \ $(NULL) themedir = $(pkgdatadir)/icons/hicolor size = 64x64 context = devices iconsdir = $(themedir)/$(size)/$(context) icons_DATA = \ kbd-capslock-off.png kbd-numlock-off.png kbd-scrolllock-off.png \ kbd-capslock-on.png kbd-numlock-on.png kbd-scrolllock-on.png if HAVE_FCITX BUILT_SOURCES = input-method-engines.c endif input-method-engines.c: $(srcdir)/input-method-engines.gperf $(AM_V_GEN) gperf --output-file=input-method-engines.c $< libkeyboard_la_SOURCES = \ gsd-keyboard-plugin.c \ gsd-keyboard-manager.h \ gsd-keyboard-manager.c \ gsd-xkb-utils.h \ gsd-xkb-utils.c \ $(NULL) libkeyboard_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/data \ -I$(top_srcdir)/plugins/common \ -DDATADIR=\""$(pkgdatadir)"\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DXKB_BASE=\""$(XKB_BASE)"\" \ $(AM_CPPFLAGS) libkeyboard_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(KEYBOARD_CFLAGS) \ $(AM_CFLAGS) libkeyboard_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) \ $(NULL) libkeyboard_la_LIBADD = \ $(top_builddir)/plugins/common/libcommon.la \ $(SETTINGS_PLUGIN_LIBS) \ $(XF86MISC_LIBS) \ $(KEYBOARD_LIBS) \ $(NULL) libexec_PROGRAMS = usd-test-keyboard usd_test_keyboard_SOURCES = \ test-keyboard.c \ gsd-keyboard-manager.h \ gsd-keyboard-manager.c \ gsd-xkb-utils.h \ gsd-xkb-utils.c \ $(NULL) usd_test_keyboard_CFLAGS = $(libkeyboard_la_CFLAGS) usd_test_keyboard_CPPFLAGS = $(libkeyboard_la_CPPFLAGS) usd_test_keyboard_LDADD = $(libkeyboard_la_LIBADD) $(top_builddir)/gnome-settings-daemon/libgsd.la plugin_in_files = \ keyboard.gnome-settings-plugin.in \ $(NULL) plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) if HAVE_IBUS noinst_PROGRAMS = test-keyboard-ibus-utils test_keyboard_ibus_utils_SOURCES = test-keyboard-ibus-utils.c test_keyboard_ibus_utils_CFLAGS = $(libkeyboard_la_CFLAGS) test_keyboard_ibus_utils_CPPFLAGS = $(libkeyboard_la_CPPFLAGS) test_keyboard_ibus_utils_LDADD = $(libkeyboard_la_LIBADD) $(top_builddir)/gnome-settings-daemon/libgsd.la check-local: test-keyboard-ibus-utils $(builddir)/test-keyboard-ibus-utils > /dev/null endif EXTRA_DIST = \ $(icons_DATA) \ $(plugin_in_files) \ $(ui_DATA) \ $(NULL) CLEANFILES = \ $(plugin_DATA) \ $(NULL) DISTCLEANFILES = \ $(plugin_DATA) \ $(NULL) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/keyboard/kbd-scrolllock-on.png0000644000004100000410000000247112735467744020600 0ustar www-datawww-dataPNG  IHDR@@iqsRGBbKGD pHYsnNtIME)zIDATxkTW?o#qDPl ĝBlڐvZB$[Хk.ϡJ"EQ$Cmi)͹3o2<<;sν{~G>( `Iiy_ x% ln |#p0B8#0+ }}y PX48)PhMG~[Nu6^J L NA{;-ЛCϚ0TR'^UnL`ZR!hk, \lX`4%80}\RiКdkg6D FpaNj:By@_"}'Fy =9,&NKc6Q>I T&]'"xxQо, E$jANe[IpO jOe8(`Uk3Z.ĔPsin-5F |Erԅ_0 %GK߳HJPa4æ)p" W]Dఁ2) }q4}%2Vtv#ֹer1[(#޹cJry&tޅ ew+3sB.MU6ziks'Z-䵹~m(r^|'\Yo<.pEjB\1i+:sW[)!VڊX:JT.+>OY>% -u"IENDB`./plugins/keyboard/gsd-keyboard-manager.h0000644000004100000410000000452212735467744020706 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #ifndef __GSD_KEYBOARD_MANAGER_H #define __GSD_KEYBOARD_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_KEYBOARD_MANAGER (gsd_keyboard_manager_get_type ()) #define GSD_KEYBOARD_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_KEYBOARD_MANAGER, GsdKeyboardManager)) #define GSD_KEYBOARD_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_KEYBOARD_MANAGER, GsdKeyboardManagerClass)) #define GSD_IS_KEYBOARD_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_KEYBOARD_MANAGER)) #define GSD_IS_KEYBOARD_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_KEYBOARD_MANAGER)) #define GSD_KEYBOARD_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_KEYBOARD_MANAGER, GsdKeyboardManagerClass)) typedef struct GsdKeyboardManagerPrivate GsdKeyboardManagerPrivate; typedef struct { GObject parent; GsdKeyboardManagerPrivate *priv; } GsdKeyboardManager; typedef struct { GObjectClass parent_class; } GsdKeyboardManagerClass; GType gsd_keyboard_manager_get_type (void); GsdKeyboardManager * gsd_keyboard_manager_new (void); gboolean gsd_keyboard_manager_start (GsdKeyboardManager *manager, GError **error); void gsd_keyboard_manager_stop (GsdKeyboardManager *manager); G_END_DECLS #endif /* __GSD_KEYBOARD_MANAGER_H */ ./plugins/keyboard/kbd-capslock-off.png0000644000004100000410000000316212735467744020364 0ustar www-datawww-dataPNG  IHDR@@iqsBIT|d pHYsnNtEXtSoftwarewww.inkscape.org<IDATxME5|.DM#H(W<9p OzGA=iL< D$ Q? +]I0ay蚥fv&;dW .iْf>m(6d x vUD`p t- ǁ;%P@еpor\NIPPX*NKz?N~3 SC xhvC fފ,Gqh?o~`A9IY00@"ۀ]uO+g嬅]@[,6`w v"*Y5|%bK'5YY`Nrc} !ۜ@2ۇ&n`jrj}1Hڧs>;یa!IN ȹLݾp 5,|&z:M|P~|GS{dJ*5)Z,m0 (D_ᑬx qP>>)Q듂B{!n_D=Bөwd^ 8Tkq[al[b])8_I:KRC6zf`s% ,I9cΔ/1IrE@gf?g 6@c$7p0 ޛ"ic~ ج$gzewdIUdD?zXH6S[=Ƙ}9{ 6#r+qPn69U2֑-d(Iz(FP 9sՓpD'8'x$-l6 6 K|џ>N6 >u^!H4@G޵0WңL-cNCյ}! IY4)IE3+po*e,t*+9$̃9m5/d; }Ǟ,ǵ1r7&I ȗ3hwo3">H "I LkqK޳|r;w:v.f)f maO,d^4͹,1U4fmg JbvWVJlt``#?< ຣ30.ӎ=|ػ j{ܙB7t4vXKaM@a.]D@iJDc'Ocz5ֺer΀rOh@-],mZ\#_hWfd|i*ksK%)in ͝1:qAZCx+]zIzgJOBYjۦI1V ! W[PrO$%|/P@ٞsh$JҼ>NajjLjLIENDB`./plugins/keyboard/gsd-keyboard-plugin.c0000644000004100000410000000202612735467744020562 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-keyboard-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdKeyboard, gsd_keyboard) ./plugins/keyboard/kbd-numlock-off.png0000644000004100000410000000331612735467744020236 0ustar www-datawww-dataPNG  IHDR@@iqsBIT|d pHYsnNtEXtSoftwarewww.inkscape.org<KIDATx[VU6b7g}g{8笵ko}Y{*4GdI㍟$&I{scte6lnt\E VAg 8 Fx(*b36H^%|ץcBM%QN~%S`0 ]щD=]lBj[;̎c1g)>fVf‘CEщYpfa#0$`sU` F *$W3|mon0|)$>YlLր9$?[SɁ|7FzY'`/rRB:XcžR c*p0k&!#Jw1KVZ8Gɐ|ׇ#iNr&3W38x0]o~8(a:6z%{ -F|(>*A!~,/RISTg@-ToԙJs+]?J9uGzm]́L4[ p9O0K{XҨ޴$1fUԳ{alUm- Bވ8.c8$OݺM[@q~ PM|"r9I`uoJPS3b**YBEp}̓Խv48-wNh|AE7bIUm+Z SoVU 2ދHd$e܋VH:4Jz_~ 3DR)2{@5Hkkpm % HIw/sIpOJ]wyGa4Is$z)ԭSι'Z G ss0]+m+>1{4G; 'D*tZ mi+e;%؞E0Ob- l>^-@:Ou/c'>XIq~b0[ ` ?l 0Ep lbhV Ֆ୉!r/b]~Qkr.PX$ IKLhs/pJ߈J:;v_Aұ/Wse: lr<@_y`xH,]]$er)0`^Q(2>xKe], rDd&@hj7n~67O] R 1?k9 oaof|Yo>V3Ktrщ/\ >Ǹ؂] Z*T)K]cn+Tg?|wU&IENDB`./plugins/keyboard/gsd-xkb-utils.h0000644000004100000410000000207012735467744017414 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 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 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, see . * */ #ifndef __GSD_XKB_UTILS_H #define __GSD_XKB_UTILS_H #include #include void gsd_xkb_get_var_defs (char **rules, XkbRF_VarDefsRec **var_defs); void gsd_xkb_free_var_defs (XkbRF_VarDefsRec *var_defs); #endif /* __GSD_XKB_UTILS_H */ ./plugins/keyboard/input-method-engines.gperf0000644000004100000410000001405612735467744021645 0ustar www-datawww-data%pic %struct-type %readonly-tables %define slot-name ibus_engine %define hash-function-name ibus_engine_hash %define lookup-function-name get_engine_for_ibus_engine struct Engine { int ibus_engine; const char *fcitx_engine; }; %% Unikey, "unikey" anthy, "anthy" array, "array30" array30, "array30" array30-big, "array30-big" bopomofo, "zhuyin-libpinyin" cangjie, "cangjie3" cangjie-big, "cangjie-big" cangjie3, "cangjie3" cangjie5, "cangjie5" cantonese, "cantonese" cantonhk, "cantonhk" chewing, "chewing" cns11643, "cns11643" compose, "compose" easy-big, "easy-big" emoji-table, "emoji" erbi, "erbi" erbi-qs, "erbi" googlepinyin, "googlepinyin" hangul, "hangul" # input-pad, NULL ipa-x-sampa, "ipa-x-sampa" jyutping, "jyutping" latex, "latex" libbopomofo, "zhuyin-libpinyin" libpinyin, "pinyin-libpinyin" libthai, "thai" m17n:am:sera, "m17n_am_sera" m17n:ar:kbd, "arabic" m17n:as:inscript, "m17n_as_inscript" m17n:as:itrans, "m17n_as_itrans" m17n:as:phonetic, "m17n_as_phonetic" m17n:ath:phonetic, "m17n_ath_phonetic" m17n:be:kbd, "fcitx-keyboard-by" m17n:bla:phonetic, "m17n_bla_phonetic" m17n:bn:disha, "m17n_bn_disha" m17n:bn:inscript, "m17n_bn_inscript" m17n:bn:itrans, "m17n_bn_itrans" m17n:bn:probhat, "m17n_bn_probhat" m17n:bn:unijoy, "m17n_bn_unijoy" m17n:bo:ewts, "m17n_bo_ewts" m17n:bo:tcrc, "m17n_bo_tcrc" m17n:bo:wylie, "m17n_bo_wylie" # m17n:cmc:kbd, NULL m17n:cr:western, "m17n_cr_western" m17n:cs:kbd, "fcitx-keyboard-cz-qwerty_bksl" m17n:da:post, "m17n_da_post" m17n:dv:phonetic, "m17n_dv_phonetic" m17n:el:kbd, "fcitx-keyboard-gr-simple" m17n:eo:h-fundamente, "m17n_eo_h-fundamente" m17n:eo:h-sistemo, "m17n_eo_h-sistemo" m17n:eo:plena, "m17n_eo_plena" m17n:eo:q-sistemo, "m17n_eo_q-sistemo" m17n:eo:vi-sistemo, "m17n_eo_vi-sistemo" m17n:eo:x-sistemo, "m17n_eo_x-sistemo" m17n:fa:isiri, "m17n_fa_isiri" m17n:fr:azerty, "m17n_fr_azerty" m17n:grc:mizuochi, "m17n_grc_mizuochi" m17n:gu:inscript, "m17n_gu_inscript" m17n:gu:itrans, "m17n_gu_itrans" m17n:gu:phonetic, "m17n_gu_phonetic" m17n:he:kbd, "fcitx-keyboard-il" m17n:hi:inscript, "m17n_hi_inscript" m17n:hi:itrans, "m17n_hi_itrans" m17n:hi:phonetic, "m17n_hi_phonetic" m17n:hi:remington, "m17n_hi_remington" m17n:hi:typewriter, "m17n_hi_typewriter" m17n:hi:vedmata, "m17n_hi_vedmata" m17n:hr:kbd, "fcitx-keyboard-hr" m17n:hy:kbd, "fcitx-keyboard-am-eastern-alt" m17n:ii:phonetic, "m17n_ii_phonetic" m17n:iu:phonetic, "m17n_iu_phonetic" m17n:ja:tcode, "m17n_ja_tcode" m17n:ja:trycode, "m17n_ja_trycode" m17n:ka:kbd, "fcitx-keyboard-ge" m17n:kk:arabic, "m17n_kk_arabic" m17n:kk:kbd, "fcitx-keyboard-kz" m17n:km:yannis, "m17n_km_yannis" m17n:kn:inscript, "m17n_kn_inscript" m17n:kn:itrans, "m17n_kn_itrans" m17n:kn:kgp, "m17n_kn_kgp" m17n:kn:typewriter, "m17n_kn_typewriter" # m17n:ko:han2, NULL # m17n:ko:romaja, NULL m17n:ks:inscript, "m17n_ks_inscript" m17n:ks:kbd, "m17n_ks_kbd" m17n:lo:kbd, "fcitx-keyboard-la-stea" m17n:lo:lrt, "m17n_lo_lrt" m17n:mai:inscript, "m17n_mai_inscript" m17n:ml:inscript, "m17n_ml_inscript" m17n:ml:itrans, "m17n_ml_itrans" m17n:ml:mozhi, "m17n_ml_mozhi" m17n:ml:remington, "m17n_ml_remington" m17n:ml:swanalekha, "m17n_ml_swanalekha" m17n:mr:inscript, "m17n_mr_inscript" m17n:mr:itrans, "m17n_mr_itrans" m17n:mr:phonetic, "m17n_mr_phonetic" m17n:my:kbd, "fcitx-keyboard-mm" m17n:ne:rom, "m17n_ne_rom" m17n:ne:trad, "m17n_ne_trad" m17n:nsk:phonetic, "m17n_nsk_phonetic" m17n:oj:phonetic, "m17n_oj_phonetic" m17n:or:inscript, "m17n_or_inscript" m17n:or:itrans, "m17n_or_itrans" m17n:or:phonetic, "m17n_or_phonetic" m17n:pa:anmollipi, "m17n_pa_anmollipi" m17n:pa:inscript, "m17n_pa_inscript" m17n:pa:itrans, "m17n_pa_itrans" m17n:pa:jhelum, "m17n_pa_jhelum" m17n:pa:phonetic, "m17n_pa_phonetic" m17n:ps:phonetic, "m17n_ps_phonetic" m17n:ru:kbd, "fcitx-keyboard-ru" m17n:ru:phonetic, "m17n_ru_phonetic" m17n:ru:translit, "m17n_ru_translit" m17n:ru:yawerty, "m17n_ru_yawerty" m17n:sa:IAST, "m17n_sa_IAST" m17n:sa:harvard-kyoto, "m17n_sa_harvard-kyoto" m17n:sa:itrans, "m17n_sa_itrans" m17n:sd:inscript, "m17n_sd_inscript" m17n:si:phonetic-dynamic, "m17n_si_phonetic-dynamic" m17n:si:samanala, "m17n_si_samanala" m17n:si:singlish, "m17n_si_singlish" m17n:si:sumihiri, "m17n_si_sumihiri" m17n:si:transliteration, "m17n_si_transliteration" m17n:si:wijesekera, "m17n_si_wijesekera" m17n:sk:kbd, "fcitx-keyboard-sk" m17n:sr:kbd, "fcitx-keyboard-rs" m17n:sv:post, "m17n_sv_post" m17n:t:latn-post, "m17n_t_latn-post" m17n:t:latn-pre, "m17n_t_latn-pre" m17n:t:math-latex, "m17n_t_math-latex" m17n:t:rfc1345, "m17n_t_rfc1345" m17n:t:syrc-phonetic, "m17n_t_syrc-phonetic" m17n:t:unicode, "m17n_t_unicode" m17n:ta:inscript, "m17n_ta_inscript" m17n:ta:itrans, "m17n_ta_itrans" m17n:ta:lk-renganathan, "m17n_ta_lk-renganathan" m17n:ta:phonetic, "m17n_ta_phonetic" m17n:ta:tamil99, "m17n_ta_tamil99" m17n:ta:typewriter, "m17n_ta_typewriter" m17n:ta:vutam, "m17n_ta_vutam" m17n:tai:sonla-kbd, "m17n_tai_sonla-kbd" m17n:te:apple, "m17n_te_apple" m17n:te:inscript, "m17n_te_inscript" m17n:te:itrans, "m17n_te_itrans" m17n:te:pothana, "m17n_te_pothana" m17n:te:rts, "m17n_te_rts" m17n:te:sarala, "m17n_te_sarala" m17n:th:kesmanee, "m17n_th_kesmanee" m17n:th:pattachote, "m17n_th_pattachote" m17n:th:tis820, "m17n_th_tis820" m17n:ua:kbd, "fcitx-keyboard-ua" m17n:ug:kbd, "fcitx-keyboard-cn-ug" m17n:ur:phonetic, "m17n_ur_phonetic" m17n:uz:kbd, "fcitx-keyboard-uz" m17n:vi:han, "m17n_vi_han" m17n:vi:nomtelex, "m17n_vi_nomtelex" m17n:vi:nomvni, "m17n_vi_nomvni" m17n:vi:tcvn, "m17n_vi_tcvn" m17n:vi:telex, "m17n_vi_telex" m17n:vi:viqr, "m17n_vi_viqr" m17n:vi:vni, "m17n_vi_vni" m17n:yi:yivo, "m17n_yi_yivo" m17n:zh:bopomofo, "m17n_zh_bopomofo" m17n:zh:cangjie, "cangjie3" m17n:zh:pinyin, "m17n_zh_pinyin" m17n:zh:pinyin-vi, "m17n_zh_pinyin-vi" # m17n:zh:py, NULL m17n:zh:quick, "quick3" # m17n:zh:tonepy, NULL mozc-jp, "mozc" pinyin, "pinyin" quick, "quick3" quick-classic, "quick-classic" quick3, "quick3" quick5, "quick5" rime, "rime" rustrad, "rustrad" scj6, "scj6" skk, "skk" stroke5, "stroke5" sunpinyin, "sunpinyin" # tegaki, NULL thai, "thai" translit, "translit" translit-ua, "translit-ua" viqr, "viqr" wu, "wu" wubi-haifeng86, "wubi" wubi-jidian86, "wubi" # xkbc, NULL yawerty, "yawerty" # yong, NULL %% ./plugins/keyboard/.indent.pro0000644000004100000410000000003012735467744016621 0ustar www-datawww-data-kr -i8 -pcs -lps -psl ./plugins/keyboard/keyboard.gnome-settings-plugin.in0000644000004100000410000000022412735467744023133 0ustar www-datawww-data[GNOME Settings Plugin] Module=keyboard IAge=0 Priority=6 _Name=Keyboard _Description=Keyboard plugin Authors= Copyright=Copyright © 2007 Website= ./plugins/keyboard/kbd-numlock-on.png0000644000004100000410000000306712735467744020103 0ustar www-datawww-dataPNG  IHDR@@iqsRGBbKGD pHYsnNtIMEPLIDATxmhUu9 m±ޖl}^VNiїJCQ"(KA&RӈF\&Wr,Zlmm>ܳ|8?><׻<ߞso<"n%@(W:@\#qYlM`@!IɰO .PL]A}VN\b<&*p)e-f`Pƴqkc鄒4`^'R7ğ%^4R3v;1l1_/p%A=meX=)p^ZkɬFk M@K`D^e._n8CmhhDI50Íu%aE MPxV'Ц)tU\dpLPo&ǛN8ھEҌbԬaLˏEw+|Mj prb+$A`uD~,m P(0LhqDPKG.PHܩ p*d?(C!hNmw:4 L(\ P6Zi=8c .3*^ƍ H=+)Rj x0`Jϭ:@vJ&́;oI}Gs7O%U~xc m` cNXr $Fb5K8﷞`6ļAaGhU#<r0{qt.)up<r5km(4S&qrfwJi<U9޽ʻ[ϦXIyr KoxMl 0{~B;xxW"H.T\3&FGHT4oG*vGk'sʆbھpɽS< t,'.o%:EqDԑ/>`FQ%}poX BxpD"Vn+[ǀ ʇfn\k~Gp5F" 3\j WApd |Ϣ#ZBPQ#S%B<7%tSbv*yR7 <#P&*S)W]k:QZ[Ui紫4Q(HG:`KhLy~ (JkGcė [O-8C@mȄfREg<8E[c}zi`Լ@.h `)$ -BI-5X',WtB]PC~^\4՛i ksM;;k. =8z#͐'% L>df"`zsvJ'5j끀Jа}dE bhUw}GI8n. !IENDB`./plugins/keyboard/kbd-scrolllock-off.png0000644000004100000410000000267312735467744020742 0ustar www-datawww-dataPNG  IHDR@@iqsBIT|d pHYsnNtEXtSoftwarewww.inkscape.org<8IDATxkUGǿ"F4"RЊJK6BnSM?@hiu!Db? آ.]"j(i\(qc]SlJtq癛s_˛y/ 9sΝ;wZ$O^IIssm pX`X_ۯq` <.{[t^}@Z]wIN ]֏ -蚐snFxR+0н߷+ewM Zz]ÛpBXʘ^J@GDϑ7qp8ߞnϙ@S(&`(h h {9Ƕ A(gTk_ *Qh CڼM6&ր8؇e@_{/ 3T2OڰDŽ*^&€{:|`xb40w{>gDIؓxCW=Yh 3<`/k EznßWXx4QZF6 jzmuw$?X*I˙]Js'#Ӓv4YTF'Ӵa?g"BdFAOK m{śͱx^"Kb|T42< @KAɦEv&S`fIӒJA#_J()tz+S|+ȜgAkSiL4Fnbd)a+sι#l\7&)hԀB=AQoz޾`CZ(cJ<ާlI:gkcp70i45v=m[cF $)7)=Gh~ wC(8I%(к~k{8g/ . +fxuȌT-K)5{P2qKe=AcK{-҈&2F4Q[<4Usab!=t9K2aA:)zmnI>Ifn[)VH9/m_lLWyROI%K%iv@?rIENDB`./plugins/keyboard/test-keyboard-ibus-utils.c0000644000004100000410000000566212735467744021577 0ustar www-datawww-data#include "gsd-xkb-utils.c" #include "gsd-keyboard-manager.c" static void test_layout_from_ibus_layout (void) { gint i; const gchar *test_strings[][2] = { /* input output */ { "", "" }, { "a", "a" }, { "a(", "a" }, { "a[", "a" }, }; for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) g_assert_cmpstr (layout_from_ibus_layout (test_strings[i][0]), ==, test_strings[i][1]); } static void test_variant_from_ibus_layout (void) { gint i; const gchar *test_strings[][2] = { /* input output */ { "", NULL }, { "a", NULL }, { "(", NULL }, { "()", "" }, { "(b)", "b" }, { "a(", NULL }, { "a()", "" }, { "a(b)", "b" }, }; for (i = 0; i < G_N_ELEMENTS (test_strings); ++i) g_assert_cmpstr (variant_from_ibus_layout (test_strings[i][0]), ==, test_strings[i][1]); } static void test_options_from_ibus_layout (void) { gint i, j; gchar *output_0[] = { NULL }; gchar *output_1[] = { "", NULL }; gchar *output_2[] = { "b", NULL }; gchar *output_3[] = { "b", "", NULL }; gchar *output_4[] = { "b", "c", NULL }; const gpointer tests[][2] = { /* input output */ { "", NULL }, { "a", NULL }, { "a[", output_0 }, { "a[]", output_1 }, { "a[b]", output_2 }, { "a[b,]", output_3 }, { "a[b,c]", output_4 }, }; for (i = 0; i < G_N_ELEMENTS (tests); ++i) { if (tests[i][1] == NULL) { g_assert (options_from_ibus_layout (tests[i][0]) == NULL); } else { gchar **strv_a = options_from_ibus_layout (tests[i][0]); gchar **strv_b = tests[i][1]; g_assert (g_strv_length (strv_a) == g_strv_length (strv_b)); for (j = 0; j < g_strv_length (strv_a); ++j) g_assert_cmpstr (strv_a[j], ==, strv_b[j]); } } } int main (void) { test_layout_from_ibus_layout (); test_variant_from_ibus_layout (); test_options_from_ibus_layout (); return 0; } ./plugins/common/0000755000004100000410000000000012735467763014240 5ustar www-datawww-data./plugins/common/Makefile.am0000644000004100000410000000200612735467744016271 0ustar www-datawww-dataplugin_name = common noinst_LTLIBRARIES = libcommon.la libcommon_la_SOURCES = \ gsd-keygrab.c \ gsd-keygrab.h \ gsd-input-helper.c \ gsd-input-helper.h \ gsd-settings-migrate.c \ gsd-settings-migrate.h libcommon_la_CPPFLAGS = \ $(AM_CPPFLAGS) libcommon_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(COMMON_CFLAGS) \ $(AM_CFLAGS) libcommon_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libcommon_la_LIBADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(COMMON_LIBS) libexec_PROGRAMS = usd-test-input-helper usd_test_input_helper_SOURCES = test-input-helper.c usd_test_input_helper_LDADD = libcommon.la usd_test_input_helper_CFLAGS = $(libcommon_la_CFLAGS) noinst_PROGRAMS = test-egg-key-parsing test_egg_key_parsing_SOURCES = test-egg-key-parsing.c test_egg_key_parsing_LDADD = libcommon.la $(COMMON_LIBS) test_egg_key_parsing_CFLAGS = $(libcommon_la_CFLAGS) scriptsdir = $(datadir)/unity-settings-daemon-@GSD_API_VERSION@ scripts_DATA = input-device-example.sh EXTRA_DIST = $(scripts_DATA) test-plugin.h ./plugins/common/test-egg-key-parsing.c0000644000004100000410000000110512735467744020346 0ustar www-datawww-data#include #define KEY "XF86AudioMute" int main (int argc, char **argv) { guint gdk_accel_key; guint *gdk_accel_codes; GdkModifierType gdk_mods; gtk_init (&argc, &argv); g_message ("gdk_keyval_from_name ('%s') == %d", KEY, gdk_keyval_from_name(KEY)); gtk_accelerator_parse_with_keycode (KEY, &gdk_accel_key, &gdk_accel_codes, &gdk_mods); g_message ("gtk_accelerator_parse_full ('%s') returned keyval '%d' keycode[0]: '%d' mods: 0x%x", KEY, gdk_accel_key, gdk_accel_codes ? gdk_accel_codes[0] : 0, gdk_mods); g_free (gdk_accel_codes); return 0; } ./plugins/common/gsd-input-helper.h0000644000004100000410000000702412735467744017602 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 Bastien Nocera * * 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. */ #ifndef __GSD_INPUT_HELPER_H #define __GSD_INPUT_HELPER_H G_BEGIN_DECLS #include #include #include #define WACOM_SERIAL_IDS_PROP "Wacom Serial IDs" typedef enum { COMMAND_DEVICE_ADDED, COMMAND_DEVICE_REMOVED, COMMAND_DEVICE_PRESENT } CustomCommand; /* Generic property setting code. Fill up the struct property with the property * data and pass it into device_set_property together with the device to be * changed. Note: doesn't cater for non-zero offsets yet, but we don't have * any settings that require that. */ typedef struct { const char *name; /* property name */ gint nitems; /* number of items in data */ gint format; /* CARD8 or CARD32 sized-items */ gint type; /* Atom representing data type */ union { const gchar *c; /* 8 bit data */ const gint *i; /* 32 bit data */ } data; } PropertyHelper; gboolean supports_xinput_devices (void); gboolean supports_xinput2_devices (int *opcode); gboolean supports_xtest (void); gboolean set_device_enabled (int device_id, gboolean enabled); gboolean device_is_touchpad (XDevice *xdevice); gboolean device_info_is_touchpad (XDeviceInfo *device_info); gboolean device_info_is_touchscreen (XDeviceInfo *device_info); gboolean device_info_is_tablet (XDeviceInfo *device_info); gboolean device_info_is_mouse (XDeviceInfo *device_info); gboolean device_info_is_trackball (XDeviceInfo *device_info); gboolean touchpad_is_present (void); gboolean touchscreen_is_present (void); gboolean mouse_is_present (void); gboolean trackball_is_present (void); gboolean device_set_property (XDevice *xdevice, const char *device_name, PropertyHelper *property); gboolean run_custom_command (GdkDevice *device, CustomCommand command); GList * get_disabled_devices (GdkDeviceManager *manager); char * xdevice_get_device_node (int deviceid); int xdevice_get_last_tool_id (int deviceid); gboolean xdevice_get_dimensions (int deviceid, guint *width, guint *height); void xdevice_close (XDevice *xdevice); G_END_DECLS #endif /* __GSD_INPUT_HELPER_H */ ./plugins/common/gsd-keygrab.c0000644000004100000410000005532012735467744016607 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2001-2003 Bastien Nocera * Copyright (C) 2006-2007 William Jon McCann * Copyright (C) 2008 Jens Granseuer * * 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 "config.h" #include #include #include #include #include #include #include #include #include "gsd-keygrab.h" /* these are the mods whose combinations are ignored by the keygrabbing code */ static GdkModifierType gsd_ignored_mods = 0; /* these are the ones we actually use for global keys, we always only check * for these set */ static GdkModifierType gsd_used_mods = 0; /* Taken from a comment in XF86keysym.h */ #define XF86KEYS_RANGE_MIN 0x10080001 #define XF86KEYS_RANGE_MAX 0x1008FFFF #define FKEYS_RANGE_MIN GDK_KEY_F1 #define FKEYS_RANGE_MAX GDK_KEY_R15 #define IN_RANGE(x, min, max) (x >= min && x <= max) static void setup_modifiers (void) { if (gsd_used_mods == 0 || gsd_ignored_mods == 0) { GdkModifierType dynmods; /* default modifiers */ gsd_ignored_mods = \ 0x2000 /*Xkb modifier*/ | GDK_LOCK_MASK | GDK_HYPER_MASK; gsd_used_mods = \ GDK_SHIFT_MASK | GDK_CONTROL_MASK |\ GDK_MOD1_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK | GDK_MOD4_MASK |\ GDK_MOD5_MASK | GDK_SUPER_MASK | GDK_META_MASK; /* NumLock can be assigned to varying keys so we need to * resolve and ignore it specially */ dynmods = XkbKeysymToModifiers (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_KEY_Num_Lock); gsd_ignored_mods |= dynmods; gsd_used_mods &= ~dynmods; } } static void grab_key_real (guint keycode, GdkWindow *root, gboolean grab, gboolean synchronous, XIGrabModifiers *mods, int num_mods) { XIEventMask evmask; unsigned char mask[(XI_LASTEVENT + 7)/8]; memset (mask, 0, sizeof (mask)); XISetMask (mask, XI_KeyPress); XISetMask (mask, XI_KeyRelease); evmask.deviceid = XIAllMasterDevices; evmask.mask_len = sizeof (mask); evmask.mask = mask; if (grab) { XIGrabKeycode (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XIAllMasterDevices, keycode, GDK_WINDOW_XID (root), GrabModeAsync, synchronous ? GrabModeSync : GrabModeAsync, False, &evmask, num_mods, mods); } else { XIUngrabKeycode (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XIAllMasterDevices, keycode, GDK_WINDOW_XID (root), num_mods, mods); } } /* Grab the key. In order to ignore GSD_IGNORED_MODS we need to grab * all combinations of the ignored modifiers and those actually used * for the binding (if any). * * inspired by all_combinations from gnome-panel/gnome-panel/global-keys.c * * This may generate X errors. The correct way to use this is like: * * gdk_error_trap_push (); * * grab_key_unsafe (key, grab, screens); * * gdk_flush (); * if (gdk_error_trap_pop ()) * g_warning ("Grab failed, another application may already have access to key '%u'", * key->keycode); * * This is not done in the function itself, to allow doing multiple grab_key * operations with one flush only. */ #define N_BITS 32 static void grab_key_internal (Key *key, gboolean grab, GsdKeygrabFlags flags, GSList *screens) { int indexes[N_BITS]; /* indexes of bits we need to flip */ int i; int bit; int bits_set_cnt; int uppervalue; guint mask, modifiers; GArray *all_mods; GSList *l; setup_modifiers (); mask = gsd_ignored_mods & ~key->state & GDK_MODIFIER_MASK; /* XGrabKey requires real modifiers, not virtual ones */ modifiers = key->state; gdk_keymap_map_virtual_modifiers (gdk_keymap_get_default (), &modifiers); modifiers &= ~(GDK_META_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK); /* If key doesn't have a usable modifier, we don't want * to grab it, since the user might lose a useful key. * * The exception is the XFree86 keys and the Function keys * (which are useful to grab without a modifier). */ if (!(flags & GSD_KEYGRAB_ALLOW_UNMODIFIED) && (modifiers & gsd_used_mods) == 0 && !IN_RANGE(key->keysym, XF86KEYS_RANGE_MIN, XF86KEYS_RANGE_MAX) && !IN_RANGE(key->keysym, FKEYS_RANGE_MIN, FKEYS_RANGE_MAX) && key->keysym != GDK_KEY_Pause && key->keysym != GDK_KEY_Print && key->keysym != GDK_KEY_Scroll_Lock && key->keysym != GDK_KEY_Caps_Lock && key->keysym != GDK_KEY_Pause && key->keysym != GDK_KEY_Break && key->keysym != GDK_KEY_Menu) { GString *keycodes; keycodes = g_string_new (""); if (key->keycodes != NULL) { guint *c; for (c = key->keycodes; *c; ++c) { g_string_printf (keycodes, " %u", *c); } } g_warning ("Key 0x%x (keycodes: %s) with state 0x%x (resolved to 0x%x) " " has no usable modifiers (usable modifiers are 0x%x)", key->keysym, keycodes->str, key->state, modifiers, gsd_used_mods); g_string_free (keycodes, TRUE); return; } bit = 0; /* store the indexes of all set bits in mask in the array */ for (i = 0; mask; ++i, mask >>= 1) { if (mask & 0x1) { indexes[bit++] = i; } } bits_set_cnt = bit; all_mods = g_array_new (FALSE, TRUE, sizeof(XIGrabModifiers)); uppervalue = 1 << bits_set_cnt; /* store all possible modifier combinations for our mask into all_mods */ for (i = 0; i < uppervalue; ++i) { int j; int result = 0; XIGrabModifiers *mod; /* map bits in the counter to those in the mask */ for (j = 0; j < bits_set_cnt; ++j) { if (i & (1 << j)) { result |= (1 << indexes[j]); } } /* Grow the array by one, to fit our new XIGrabModifiers item */ g_array_set_size (all_mods, all_mods->len + 1); mod = &g_array_index (all_mods, XIGrabModifiers, all_mods->len - 1); mod->modifiers = result | modifiers; } /* Capture the actual keycodes with the modifier array */ for (l = screens; l; l = l->next) { GdkScreen *screen = l->data; guint *code; for (code = key->keycodes; *code; ++code) { grab_key_real (*code, gdk_screen_get_root_window (screen), grab, flags & GSD_KEYGRAB_SYNCHRONOUS, (XIGrabModifiers *) all_mods->data, all_mods->len); } } g_array_free (all_mods, TRUE); } static void get_keys_for_bit (guint bit, guint *left, guint *right) { guint left_dummy; guint right_dummy; if (left == NULL) left = &left_dummy; if (right == NULL) right = &right_dummy; *left = 0; *right = 0; switch (1 << bit) { case GDK_SHIFT_MASK: *left = GDK_KEY_Shift_L; *right = GDK_KEY_Shift_R; break; case GDK_CONTROL_MASK: *left = GDK_KEY_Control_L; *right = GDK_KEY_Control_R; break; case GDK_LOCK_MASK: *left = GDK_KEY_Caps_Lock; *right = GDK_KEY_Shift_Lock; break; case GDK_META_MASK: case GDK_MOD1_MASK: *left = GDK_KEY_Alt_L; *right = GDK_KEY_Alt_R; break; case GDK_SUPER_MASK: *left = GDK_KEY_Super_L; *right = GDK_KEY_Super_R; break; } } static guint get_mask_for_key (guint key) { switch (key) { case GDK_KEY_Shift_L: case GDK_KEY_Shift_R: return GDK_SHIFT_MASK; case GDK_KEY_Control_L: case GDK_KEY_Control_R: return GDK_CONTROL_MASK; case GDK_KEY_Caps_Lock: case GDK_KEY_Shift_Lock: return GDK_LOCK_MASK; case GDK_KEY_Meta_L: case GDK_KEY_Meta_R: case GDK_KEY_Alt_L: case GDK_KEY_Alt_R: return GDK_MOD1_MASK; case GDK_KEY_Super_L: case GDK_KEY_Super_R: return GDK_SUPER_MASK; } return 0; } static guint get_mirrored_key (guint key) { switch (key) { case GDK_KEY_Shift_L: return GDK_KEY_Shift_R; case GDK_KEY_Shift_R: return GDK_KEY_Shift_L; case GDK_KEY_Control_L: return GDK_KEY_Control_R; case GDK_KEY_Control_R: return GDK_KEY_Control_L; case GDK_KEY_Meta_L: return GDK_KEY_Meta_R; case GDK_KEY_Meta_R: return GDK_KEY_Meta_L; case GDK_KEY_Alt_L: return GDK_KEY_Alt_R; case GDK_KEY_Alt_R: return GDK_KEY_Alt_L; case GDK_KEY_Super_L: return GDK_KEY_Super_R; case GDK_KEY_Super_R: return GDK_KEY_Super_L; case GDK_KEY_Hyper_L: return GDK_KEY_Hyper_R; case GDK_KEY_Hyper_R: return GDK_KEY_Hyper_L; } return 0; } void grab_key_unsafe (Key *key, GsdKeygrabFlags flags, GSList *screens) { guint key_mask = get_mask_for_key (key->keysym); grab_key_internal (key, TRUE, flags, screens); if (key_mask != 0) { Key copy; guint i, j; if ((key->state & key_mask) != 0) { guint mirror = get_mirrored_key (key->keysym); if (mirror != 0) { gint mirror_keys_len; GdkKeymapKey *mirror_keys; gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), mirror, &mirror_keys, &mirror_keys_len); copy.keysym = mirror; copy.state = key->state; copy.keycodes = g_new0 (guint, mirror_keys_len + 1); for (j = 0; j < mirror_keys_len; j++) copy.keycodes[j] = mirror_keys[j].keycode; grab_key_internal (©, TRUE, flags, screens); g_free (copy.keycodes); g_free (mirror_keys); } } for (i = 0; i < 8 * sizeof (guint); i++) { guint left, right; gint left_keys_len, right_keys_len; GdkKeymapKey *left_keys, *right_keys; if (1 << i == key_mask || (key->state & 1 << i) == 0) continue; get_keys_for_bit (i, &left, &right); if (left == 0 && right == 0) continue; left_keys_len = 0; right_keys_len = 0; left_keys = NULL; right_keys = NULL; if (left != 0) gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), left, &left_keys, &left_keys_len); if (right != 0) gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), right, &right_keys, &right_keys_len); copy.keysym = left != 0 ? left : right; copy.state = (key->state | key_mask) & ~(1 << i); copy.keycodes = g_new0 (guint, left_keys_len + right_keys_len + 1); for (j = 0; j < left_keys_len; j++) copy.keycodes[j] = left_keys[j].keycode; for (j = 0; j < right_keys_len; j++) copy.keycodes[left_keys_len + j] = right_keys[j].keycode; grab_key_internal (©, TRUE, flags, screens); g_free (copy.keycodes); g_free (right_keys); g_free (left_keys); } } } void ungrab_key_unsafe (Key *key, GSList *screens) { guint key_mask = get_mask_for_key (key->keysym); grab_key_internal (key, FALSE, 0, screens); if (key_mask != 0) { Key copy; guint i, j; if ((key->state & key_mask) != 0) { guint mirror = get_mirrored_key (key->keysym); if (mirror != 0) { gint mirror_keys_len; GdkKeymapKey *mirror_keys; gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), mirror, &mirror_keys, &mirror_keys_len); copy.keysym = mirror; copy.state = key->state; copy.keycodes = g_new0 (guint, mirror_keys_len + 1); for (j = 0; j < mirror_keys_len; j++) copy.keycodes[j] = mirror_keys[j].keycode; grab_key_internal (©, FALSE, 0, screens); g_free (copy.keycodes); g_free (mirror_keys); } } for (i = 0; i < 8 * sizeof (guint); i++) { guint left, right; gint left_keys_len, right_keys_len; GdkKeymapKey *left_keys, *right_keys; if (1 << i == key_mask || (key->state & 1 << i) == 0) continue; get_keys_for_bit (i, &left, &right); if (left == 0 && right == 0) continue; left_keys_len = 0; right_keys_len = 0; left_keys = NULL; right_keys = NULL; if (left != 0) gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), left, &left_keys, &left_keys_len); if (right != 0) gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), right, &right_keys, &right_keys_len); copy.keysym = left != 0 ? left : right; copy.state = (key->state | key_mask) & ~(1 << i); copy.keycodes = g_new0 (guint, left_keys_len + right_keys_len + 1); for (j = 0; j < left_keys_len; j++) copy.keycodes[j] = left_keys[j].keycode; for (j = 0; j < right_keys_len; j++) copy.keycodes[left_keys_len + j] = right_keys[j].keycode; grab_key_internal (©, FALSE, 0, screens); g_free (copy.keycodes); g_free (right_keys); g_free (left_keys); } } } static gboolean have_xkb (Display *dpy) { static int have_xkb = -1; if (have_xkb == -1) { int opcode, error_base, major, minor, xkb_event_base; have_xkb = XkbQueryExtension (dpy, &opcode, &xkb_event_base, &error_base, &major, &minor) && XkbUseExtension (dpy, &major, &minor); } return have_xkb; } gboolean key_uses_keycode (const Key *key, guint keycode) { if (key->keycodes != NULL) { guint *c; for (c = key->keycodes; *c; ++c) { if (*c == keycode) return TRUE; } } return FALSE; } /* Adapted from _gdk_x11_device_xi2_translate_state() * in gtk+/gdk/x11/gdkdevice-xi2.c */ static guint device_xi2_translate_state (XIModifierState *mods_state, XIGroupState *group_state) { guint state; gint group; state = (guint) mods_state->base | mods_state->latched | mods_state->locked; group = group_state->base | group_state->latched | group_state->locked; /* FIXME: do we need the XKB complications for this ? */ group = CLAMP(group, 0, 3); state |= group << 13; return state; } gboolean match_xi2_key (Key *key, XIDeviceEvent *event) { guint keyval; GdkModifierType consumed; gint group; guint keycode, state; if (key == NULL) return FALSE; setup_modifiers (); state = device_xi2_translate_state (&event->mods, &event->group); if (have_xkb (event->display)) group = XkbGroupForCoreState (state); else group = (state & GDK_KEY_Mode_switch) ? 1 : 0; keycode = event->detail; /* Check if we find a keysym that matches our current state */ if (gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), keycode, state, group, &keyval, NULL, NULL, &consumed)) { guint key_bit, event_bit; guint lower, upper; guint mask, full_mask; /* HACK: we don't want to use SysRq as a keybinding, so we avoid * its translation from Alt+Print. */ if (keyval == GDK_KEY_Sys_Req && (state & GDK_MOD1_MASK) != 0) { consumed = 0; keyval = GDK_KEY_Print; } /* The Key structure contains virtual modifiers, whereas * the XEvent will be using the real modifier, so translate those */ key_bit = get_mask_for_key (key->keysym); event_bit = get_mask_for_key (keyval); mask = key->state; full_mask = mask | key_bit; gdk_keymap_map_virtual_modifiers (gdk_keymap_get_default (), &mask); gdk_keymap_map_virtual_modifiers (gdk_keymap_get_default (), &full_mask); mask &= ~(GDK_META_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK); full_mask &= ~(GDK_META_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK); gdk_keyval_convert_case (keyval, &lower, &upper); /* If we are checking against the lower version of the * keysym, we might need the Shift state for matching, * so remove it from the consumed modifiers */ if (lower == key->keysym || event_bit != 0) consumed &= ~GDK_SHIFT_MASK; state &= ~consumed & gsd_used_mods; if (key_bit != 0 && event_bit != 0) { state |= event_bit; gdk_keymap_map_virtual_modifiers (gdk_keymap_get_default (), &state); state &= ~(GDK_META_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK); return state == full_mask; } return (lower == key->keysym || upper == key->keysym) && state == mask; } /* The key we passed doesn't have a keysym, so try with just the keycode */ return (key != NULL && key->state == (state & gsd_used_mods) && key_uses_keycode (key, keycode)); } Key * parse_key (const char *str) { Key *key; if (str == NULL || *str == '\0' || g_str_equal (str, "disabled")) { return NULL; } key = g_new0 (Key, 1); gtk_accelerator_parse_with_keycode (str, &key->keysym, &key->keycodes, &key->state); if (key->keysym == 0 && key->keycodes == NULL && key->state == 0) { g_free (key); return NULL; } return key; } void free_key (Key *key) { if (key == NULL) return; g_free (key->keycodes); g_free (key); } static void grab_button_real (int deviceid, gboolean grab, GdkWindow *root) { XIGrabModifiers mods; mods.modifiers = XIAnyModifier; if (grab) { XIEventMask evmask; unsigned char mask[(XI_LASTEVENT + 7)/8]; memset (mask, 0, sizeof (mask)); XISetMask (mask, XI_ButtonRelease); XISetMask (mask, XI_ButtonPress); evmask.deviceid = deviceid; evmask.mask_len = sizeof (mask); evmask.mask = mask; XIGrabButton (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), deviceid, XIAnyButton, GDK_WINDOW_XID (root), None, GrabModeAsync, GrabModeAsync, False, &evmask, 1, &mods); } else { XIUngrabButton (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), deviceid, XIAnyButton, GDK_WINDOW_XID (root), 1, &mods); } } void grab_button (int deviceid, gboolean grab, GSList *screens) { GSList *l; for (l = screens; l; l = l->next) { GdkScreen *screen = l->data; grab_button_real (deviceid, grab, gdk_screen_get_root_window (screen)); } } ./plugins/common/gsd-input-helper.c0000644000004100000410000004366512735467763017611 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 Bastien Nocera * * 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 "config.h" #include #include #include #include #include #include #include "gsd-input-helper.h" #define INPUT_DEVICES_SCHEMA "org.gnome.settings-daemon.peripherals.input-devices" #define KEY_HOTPLUG_COMMAND "hotplug-command" #define ABS_MT_X "Abs MT Position X" #define ABS_MT_Y "Abs MT Position Y" #define ABS_X "Abs X" #define ABS_Y "Abs Y" typedef gboolean (* InfoIdentifyFunc) (XDeviceInfo *device_info); typedef gboolean (* DeviceIdentifyFunc) (XDevice *xdevice); gboolean device_set_property (XDevice *xdevice, const char *device_name, PropertyHelper *property) { int rc, i; Atom prop; Atom realtype; int realformat; unsigned long nitems, bytes_after; unsigned char *data; prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), property->name, False); if (!prop) return FALSE; gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, 0, property->nitems, False, AnyPropertyType, &realtype, &realformat, &nitems, &bytes_after, &data); if (rc != Success || realtype != property->type || realformat != property->format || nitems < property->nitems) { gdk_error_trap_pop_ignored (); g_warning ("Error reading property \"%s\" for \"%s\"", property->name, device_name); return FALSE; } for (i = 0; i < nitems; i++) { switch (property->format) { case 8: data[i] = property->data.c[i]; break; case 32: ((long*)data)[i] = property->data.i[i]; break; } } XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, realtype, realformat, PropModeReplace, data, nitems); XFree (data); if (gdk_error_trap_pop ()) { g_warning ("Error in setting \"%s\" for \"%s\"", property->name, device_name); return FALSE; } return TRUE; } static gboolean supports_xinput_devices_with_opcode (int *opcode) { gint op_code, event, error; gboolean retval; retval = XQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "XInputExtension", &op_code, &event, &error); if (opcode) *opcode = op_code; return retval; } gboolean supports_xinput_devices (void) { return supports_xinput_devices_with_opcode (NULL); } gboolean supports_xtest (void) { gint op_code, event, error; gboolean retval; retval = XQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "XTEST", &op_code, &event, &error); return retval; } gboolean supports_xinput2_devices (int *opcode) { int major, minor; if (supports_xinput_devices_with_opcode (opcode) == FALSE) return FALSE; gdk_error_trap_push (); major = 2; minor = 3; if (XIQueryVersion (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &major, &minor) != Success) { gdk_error_trap_pop_ignored (); return FALSE; } gdk_error_trap_pop_ignored (); if ((major * 1000 + minor) < (2000)) return FALSE; return TRUE; } gboolean device_is_touchpad (XDevice *xdevice) { Atom realtype, prop; int realformat; unsigned long nitems, bytes_after; unsigned char *data; /* we don't check on the type being XI_TOUCHPAD here, * but having a "Synaptics Off" property should be enough */ prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Off", False); if (!prop) return FALSE; gdk_error_trap_push (); if ((XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, 0, 1, False, XA_INTEGER, &realtype, &realformat, &nitems, &bytes_after, &data) == Success) && (realtype != None)) { gdk_error_trap_pop_ignored (); XFree (data); return TRUE; } gdk_error_trap_pop_ignored (); return FALSE; } gboolean device_info_is_touchpad (XDeviceInfo *device_info) { return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TOUCHPAD, False)); } gboolean device_info_is_touchscreen (XDeviceInfo *device_info) { return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TOUCHSCREEN, False)); } gboolean device_info_is_tablet (XDeviceInfo *device_info) { /* Note that this doesn't match Wacom tablets */ return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TABLET, False)); } gboolean device_info_is_mouse (XDeviceInfo *device_info) { return (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_MOUSE, False)); } gboolean device_info_is_trackball (XDeviceInfo *device_info) { gboolean retval; retval = (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TRACKBALL, False)); if (retval == FALSE && device_info->name != NULL) { char *lowercase; lowercase = g_ascii_strdown (device_info->name, -1); retval = strstr (lowercase, "trackball") != NULL; g_free (lowercase); } return retval; } static gboolean device_type_is_present (InfoIdentifyFunc info_func, DeviceIdentifyFunc device_func) { XDeviceInfo *device_info; gint n_devices; guint i; gboolean retval; if (supports_xinput_devices () == FALSE) return TRUE; retval = FALSE; device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) return FALSE; for (i = 0; i < n_devices; i++) { XDevice *device; /* Check with the device info first */ retval = (info_func) (&device_info[i]); if (retval == FALSE) continue; /* If we only have an info func, we're done checking */ if (device_func == NULL) break; gdk_error_trap_push (); device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_info[i].id); if (gdk_error_trap_pop () || (device == NULL)) continue; retval = (device_func) (device); xdevice_close (device); if (retval) break; } XFreeDeviceList (device_info); return retval; } gboolean touchscreen_is_present (void) { return device_type_is_present (device_info_is_touchscreen, NULL); } gboolean touchpad_is_present (void) { return device_type_is_present (device_info_is_touchpad, device_is_touchpad); } gboolean mouse_is_present (void) { return device_type_is_present (device_info_is_mouse, NULL); } gboolean trackball_is_present (void) { return device_type_is_present (device_info_is_trackball, NULL); } char * xdevice_get_device_node (int deviceid) { Atom prop; Atom act_type; int act_format; unsigned long nitems, bytes_after; unsigned char *data; char *ret; gdk_display_sync (gdk_display_get_default ()); prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Device Node", False); if (!prop) return NULL; gdk_error_trap_push (); if (!XIGetProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), deviceid, prop, 0, 1000, False, AnyPropertyType, &act_type, &act_format, &nitems, &bytes_after, &data) == Success) { gdk_error_trap_pop_ignored (); return NULL; } if (gdk_error_trap_pop ()) goto out; if (nitems == 0) goto out; if (act_type != XA_STRING) goto out; /* Unknown string format */ if (act_format != 8) goto out; ret = g_strdup ((char *) data); XFree (data); return ret; out: XFree (data); return NULL; } #define TOOL_ID_FORMAT_SIZE 32 static int get_id_for_index (guchar *data, guint idx) { guchar *ptr; int id; ptr = data; ptr += TOOL_ID_FORMAT_SIZE / 8 * idx; id = *((int32_t*)ptr); id = id & 0xfffff; return id; } #define STYLUS_DEVICE_ID 0x02 #define ERASER_DEVICE_ID 0x0A int xdevice_get_last_tool_id (int deviceid) { Atom prop; Atom act_type; int act_format; unsigned long nitems, bytes_after; unsigned char *data; int id; id = -1; gdk_display_sync (gdk_display_get_default ()); prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), WACOM_SERIAL_IDS_PROP, False); if (!prop) return -1; data = NULL; gdk_error_trap_push (); if (XIGetProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), deviceid, prop, 0, 1000, False, AnyPropertyType, &act_type, &act_format, &nitems, &bytes_after, &data) != Success) { gdk_error_trap_pop_ignored (); goto out; } if (gdk_error_trap_pop ()) goto out; if (nitems != 4 && nitems != 5) goto out; if (act_type != XA_INTEGER) goto out; if (act_format != TOOL_ID_FORMAT_SIZE) goto out; /* item 0 = tablet ID * item 1 = old device serial number (== last tool in proximity) * item 2 = old hardware serial number (including tool ID) * item 3 = current serial number (0 if no tool in proximity) * item 4 = current tool ID (since Feb 2012) * * Get the current tool ID first, if available, then the old one */ id = 0x0; if (nitems == 5) id = get_id_for_index (data, 4); if (id == 0x0) id = get_id_for_index (data, 2); /* That means that no tool was set down yet */ if (id == STYLUS_DEVICE_ID || id == ERASER_DEVICE_ID) id = 0x0; out: if (data != NULL) XFree (data); return id; } gboolean set_device_enabled (int device_id, gboolean enabled) { Atom prop; guchar value; prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Device Enabled", False); if (!prop) return FALSE; gdk_error_trap_push (); value = enabled ? 1 : 0; XIChangeProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_id, prop, XA_INTEGER, 8, PropModeReplace, &value, 1); if (gdk_error_trap_pop ()) return FALSE; return TRUE; } static const char * custom_command_to_string (CustomCommand command) { switch (command) { case COMMAND_DEVICE_ADDED: return "added"; case COMMAND_DEVICE_REMOVED: return "removed"; case COMMAND_DEVICE_PRESENT: return "present"; default: g_assert_not_reached (); } } /* Run a custom command on device presence events. Parameters passed into * the custom command are: * command -t [added|removed|present] -i * Type 'added' and 'removed' signal 'device added' and 'device removed', * respectively. Type 'present' signals 'device present at * gnome-settings-daemon init'. * * The script is expected to run synchronously, and an exit value * of "1" means that no other settings will be applied to this * particular device. * * More options may be added in the future. * * This function returns TRUE if we should not apply any more settings * to the device. */ gboolean run_custom_command (GdkDevice *device, CustomCommand command) { GSettings *settings; GError *error = NULL; char *cmd; char *argv[7]; int exit_status; gboolean rc; int id; settings = g_settings_new (INPUT_DEVICES_SCHEMA); cmd = g_settings_get_string (settings, KEY_HOTPLUG_COMMAND); g_object_unref (settings); if (!cmd || cmd[0] == '\0') { g_free (cmd); return FALSE; } /* Easter egg! */ g_object_get (device, "device-id", &id, NULL); argv[0] = cmd; argv[1] = "-t"; argv[2] = (char *) custom_command_to_string (command); argv[3] = "-i"; argv[4] = g_strdup_printf ("%d", id); argv[5] = (char*) gdk_device_get_name (device); argv[6] = NULL; rc = g_spawn_sync (g_get_home_dir (), argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &exit_status, &error); if (rc == FALSE) { g_warning ("Couldn't execute command '%s', verify that this is a valid command: %s", cmd, error->message); g_clear_error (&error); } g_free (argv[0]); g_free (argv[4]); if (!g_spawn_check_exit_status (exit_status, &error)) { if (g_error_matches (error, G_SPAWN_EXIT_ERROR, 1)) { g_clear_error (&error); return TRUE; } g_clear_error (&error); } return FALSE; } GList * get_disabled_devices (GdkDeviceManager *manager) { XDeviceInfo *device_info; gint n_devices; guint i; GList *ret; ret = NULL; device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) return ret; for (i = 0; i < n_devices; i++) { GdkDevice *device; /* Ignore core devices */ if (device_info[i].use == IsXKeyboard || device_info[i].use == IsXPointer) continue; /* Check whether the device is actually available */ device = gdk_x11_device_manager_lookup (manager, device_info[i].id); if (device != NULL) continue; ret = g_list_prepend (ret, GINT_TO_POINTER (device_info[i].id)); } XFreeDeviceList (device_info); return ret; } gboolean xdevice_get_dimensions (int deviceid, guint *width, guint *height) { GdkDisplay *display = gdk_display_get_default (); XIDeviceInfo *info; guint *value, w, h; int i, n_info; /* ignore errors, device might be removed before config is done as in #1503758 */ gdk_error_trap_push(); info = XIQueryDevice (GDK_DISPLAY_XDISPLAY (display), deviceid, &n_info); gdk_error_trap_pop_ignored (); *width = *height = w = h = 0; if (!info) return FALSE; for (i = 0; i < info->num_classes; i++) { XIValuatorClassInfo *valuator_info; if (info->classes[i]->type != XIValuatorClass) continue; valuator_info = (XIValuatorClassInfo *) info->classes[i]; if (valuator_info->label == gdk_x11_get_xatom_by_name_for_display (display, ABS_X) || valuator_info->label == gdk_x11_get_xatom_by_name_for_display (display, ABS_MT_X)) value = &w; else if (valuator_info->label == gdk_x11_get_xatom_by_name_for_display (display, ABS_Y) || valuator_info->label == gdk_x11_get_xatom_by_name_for_display (display, ABS_MT_Y)) value = &h; else continue; *value = (valuator_info->max - valuator_info->min) * 1000 / valuator_info->resolution; } *width = w; *height = h; XIFreeDeviceInfo (info); return (w != 0 && h != 0); } void xdevice_close (XDevice *xdevice) { gdk_error_trap_push (); XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice); gdk_error_trap_pop_ignored(); } ./plugins/common/test-plugin.h0000644000004100000410000000462012735467744016665 0ustar www-datawww-data/** * Create a test app for your plugin quickly. * * #define NEW gsd_media_keys_manager_new * #define START gsd_media_keys_manager_start * #define MANAGER GsdMediaKeysManager * #include "gsd-media-keys-manager.h" * * #include "test-plugin.h" */ #include "config.h" #include #include #include #include #ifndef SCHEMA_NAME #define SCHEMA_NAME PLUGIN_NAME #endif #ifndef PLUGIN_NAME #error Include PLUGIN_CFLAGS in the test application s CFLAGS #endif /* !PLUGIN_NAME */ static MANAGER *manager = NULL; static gboolean has_settings (void) { const gchar * const * list; guint i; list = g_settings_list_schemas (); for (i = 0; list[i] != NULL; i++) { if (g_str_equal (list[i], "org.gnome.settings-daemon.plugins." SCHEMA_NAME)) return TRUE; } return FALSE; } static void print_enable_disable_help (void) { fprintf (stderr, "To deactivate:\n"); fprintf (stderr, "\tgsettings set org.gnome.settings-daemon.plugins." SCHEMA_NAME " active false\n"); fprintf (stderr, "To reactivate:\n"); fprintf (stderr, "\tgsettings set org.gnome.settings-daemon.plugins." SCHEMA_NAME " active true\n"); } int main (int argc, char **argv) { GError *error; GSettings *settings; bindtextdomain (GETTEXT_PACKAGE, GNOME_SETTINGS_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); notify_init ("gnome-settings-daemon"); g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); error = NULL; if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error)) { fprintf (stderr, "%s\n", error->message); g_error_free (error); exit (1); } if (has_settings () == FALSE) { fprintf (stderr, "The schemas for plugin '%s' isn't available, check your installation.\n", SCHEMA_NAME); } else { settings = g_settings_new ("org.gnome.settings-daemon.plugins." SCHEMA_NAME); if (g_settings_get_boolean (settings, "active") != FALSE) { fprintf (stderr, "Plugin '%s' is not disabled. You need to disable it before launching the test application.\n", SCHEMA_NAME); print_enable_disable_help (); exit (1); } print_enable_disable_help(); } manager = NEW (); error = NULL; START (manager, &error); gtk_main (); STOP (manager); g_object_unref (manager); return 0; } ./plugins/common/input-device-example.sh0000644000004100000410000000300612735467744020617 0ustar www-datawww-data#!/bin/sh # # This script is an example hotplug script for use with the various # input devices plugins. # # The script is called with the arguments: # -t [added|present|removed] # added ... device was just plugged in # present.. device was present at gnome-settings-daemon startup # removed.. device was just removed # -i # device ID being the XInput device ID # The name of the device # # The script should return 0 if the device is to be # ignored from future configuration. # # Set the script to be used with: # gsettings set org.gnome.settings-daemon.peripherals.input-devices hotplug-command /path/to/script/input-devices.sh # args=`getopt "t:i:" $*` set -- $args while [ $# -gt 0 ] do case $1 in -t) shift; type="$1" ;; -i) shift; id="$1" ;; --) shift; device="$@" break; ;; *) echo "Unknown option $1"; exit 1 ;; esac shift done retval=0 case $type in added) echo "Device '$device' (ID=$id) was added" ;; present) echo "Device '$device' (ID=$id) was already present at startup" ;; removed) echo "Device '$device' (ID=$id) was removed" ;; *) echo "Unknown operation" retval=1 ;; esac # All further processing will be disabled if $retval == 1 exit $retval ./plugins/common/gsd-keygrab.h0000644000004100000410000000373112735467744016613 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Jens Granseuer * * 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. */ #ifndef __GSD_COMMON_KEYGRAB_H #define __GSD_COMMON_KEYGRAB_H G_BEGIN_DECLS #include #include #include typedef struct { guint keysym; guint state; guint *keycodes; } Key; typedef enum { GSD_KEYGRAB_NORMAL = 0, GSD_KEYGRAB_ALLOW_UNMODIFIED = 1 << 0, GSD_KEYGRAB_SYNCHRONOUS = 1 << 1 } GsdKeygrabFlags; void grab_key_unsafe (Key *key, GsdKeygrabFlags flags, GSList *screens); void ungrab_key_unsafe (Key *key, GSList *screens); gboolean match_xi2_key (Key *key, XIDeviceEvent *event); gboolean key_uses_keycode (const Key *key, guint keycode); Key * parse_key (const char *str); void free_key (Key *key); void grab_button (int deviceid, gboolean grab, GSList *screens); G_END_DECLS #endif /* __GSD_COMMON_KEYGRAB_H */ ./plugins/common/gsd-settings-migrate.c0000644000004100000410000000471512735467744020453 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2015 Red Hat * * 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. * * Author: Carlos Garnacho */ #include "config.h" #include #include "gsd-settings-migrate.h" void gsd_settings_migrate_check (const gchar *origin_schema, const gchar *origin_path, const gchar *dest_schema, const gchar *dest_path, GsdSettingsMigrateEntry entries[], guint n_entries) { GSettings *origin_settings, *dest_settings; GVariant *variant; guint i; origin_settings = g_settings_new_with_path (origin_schema, origin_path); dest_settings = g_settings_new_with_path (dest_schema, dest_path); for (i = 0; i < n_entries; i++) { variant = g_settings_get_user_value (origin_settings, entries[i].origin_key); if (!variant) continue; if (entries[i].dest_key) { if (entries[i].func) { GVariant *modified; modified = entries[i].func (variant); g_variant_unref (variant); variant = g_variant_ref_sink (modified); } g_settings_set_value (dest_settings, entries[i].dest_key, variant); } g_settings_reset (origin_settings, entries[i].origin_key); g_variant_unref (variant); } g_object_unref (origin_settings); g_object_unref (dest_settings); } ./plugins/common/gsd-settings-migrate.h0000644000004100000410000000323012735467744020447 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2015 Red Hat * * 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. * * Author: Carlos Garnacho */ #ifndef __GSD_SETTINGS_MIGRATE_H__ #define __GSD_SETTINGS_MIGRATE_H__ typedef struct _GsdSettingsMigrateEntry GsdSettingsMigrateEntry; typedef GVariant * (* GsdSettingsMigrateFunc) (GVariant *variant); struct _GsdSettingsMigrateEntry { const gchar *origin_key; const gchar *dest_key; GsdSettingsMigrateFunc func; }; void gsd_settings_migrate_check (const gchar *origin_schema, const gchar *origin_path, const gchar *dest_schema, const gchar *dest_path, GsdSettingsMigrateEntry entries[], guint n_entries); #endif /* __GSD_SETTINGS_MIGRATE_H__ */ ./plugins/common/test-input-helper.c0000644000004100000410000000772212735467744020004 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2011 Bastien Nocera * * 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 "config.h" #include #include #include #include #include #include "gsd-input-helper.h" static void print_disabled_devices (void) { GList *devices, *l; GdkDeviceManager *manager; manager = gdk_display_get_device_manager (gdk_display_get_default ()); devices = get_disabled_devices (manager); g_print ("Disabled devices:\t\t\t"); if (devices == NULL) { g_print ("no\n"); return; } for (l = devices; l != NULL; l = l->next) { g_print ("%d ", GPOINTER_TO_INT (l->data)); } g_list_free (devices); g_print ("\n"); } int main (int argc, char **argv) { gboolean supports_xinput; gboolean has_touchpad, has_touchscreen, has_trackball; XDeviceInfo *device_info; gint n_devices, opcode; guint i; gtk_init (&argc, &argv); supports_xinput = supports_xinput_devices (); if (supports_xinput) { g_print ("Supports XInput:\t\t\tyes\n"); } else { g_print ("Supports XInput:\t\t\tno\n"); return 0; } supports_xinput = supports_xinput2_devices (&opcode); if (supports_xinput) { g_print ("Supports XInput2:\t\t\tyes (opcode: %d)\n", opcode); } else { g_print ("Supports XInput2:\t\t\tno\n"); return 0; } has_touchpad = touchpad_is_present (); g_print ("Has touchpad:\t\t\t\t%s\n", has_touchpad ? "yes" : "no"); has_touchscreen = touchscreen_is_present (); g_print ("Has touchscreen:\t\t\t%s\n", has_touchscreen ? "yes" : "no"); has_trackball = trackball_is_present (); g_print ("Has trackball:\t\t\t\t%s\n", has_trackball ? "yes" : "no"); device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) { g_warning ("Has no input devices"); return 1; } print_disabled_devices (); for (i = 0; i < n_devices; i++) { XDevice *device; if (device_info_is_touchscreen (&device_info[i])) { g_print ("Device %d is touchscreen:\t\t%s\n", (int) device_info[i].id, "yes"); continue; } if (device_info_is_trackball (&device_info[i])) { g_print ("Device %d is trackball:\t\t\t%s\n", (int) device_info[i].id, "yes"); continue; } if (device_info_is_tablet (&device_info[i])) { g_print ("Device %d is tablet:\t\t\t%s\n", (int) device_info[i].id, "yes"); continue; } gdk_error_trap_push (); device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_info[i].id); if (gdk_error_trap_pop () || (device == NULL)) continue; if (device_is_touchpad (device)) g_print ("Device %d is touchpad:\t\t%s\n", (int) device_info[i].id, "yes"); else { int tool_id; tool_id = xdevice_get_last_tool_id (device_info[i].id); if (tool_id >= 0x0) g_print ("Device %d is touchpad/touchscreen:\t%s (tool ID: 0x%x)\n", (int) device_info[i].id, "no", tool_id); else g_print ("Device %d is touchpad/touchscreen:\t%s\n", (int) device_info[i].id, "no"); } XCloseDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device); } XFreeDeviceList (device_info); return 0; } ./plugins/automount/0000755000004100000410000000000012735467763015003 5ustar www-datawww-data./plugins/automount/gsd-automount-manager.c0000644000004100000410000005214412735467763021373 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 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 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 * * Author: Tomas Bzatek */ #include "config.h" #include #include #include #include #include #include "gnome-settings-profile.h" #include "gnome-settings-bus.h" #include "gsd-automount-manager.h" #include "gsd-autorun.h" #define GSD_AUTOMOUNT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManagerPrivate)) struct GsdAutomountManagerPrivate { GSettings *settings; GVolumeMonitor *volume_monitor; unsigned int automount_idle_id; GDBusProxy *session; gboolean session_is_active; gboolean screensaver_active; gboolean lockscreen_active; guint ss_watch_id; guint us_watch_id; GDBusProxy *ss_proxy; GDBusProxy *us_proxy; GList *volume_queue; }; static void gsd_automount_manager_class_init (GsdAutomountManagerClass *klass); static void gsd_automount_manager_init (GsdAutomountManager *gsd_automount_manager); G_DEFINE_TYPE (GsdAutomountManager, gsd_automount_manager, G_TYPE_OBJECT) static GtkDialog * show_error_dialog (const char *primary_text, const char *secondary_text) { GtkWidget *dialog; dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", ""); g_object_set (dialog, "text", primary_text, "secondary-text", secondary_text, NULL); gtk_widget_show (GTK_WIDGET (dialog)); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); return GTK_DIALOG (dialog); } static void startup_volume_mount_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { g_volume_mount_finish (G_VOLUME (source_object), res, NULL); } static void automount_all_volumes (GsdAutomountManager *manager) { GList *volumes, *l; GMount *mount; GVolume *volume; if (g_settings_get_boolean (manager->priv->settings, "automount")) { /* automount all mountable volumes at start-up */ volumes = g_volume_monitor_get_volumes (manager->priv->volume_monitor); for (l = volumes; l != NULL; l = l->next) { volume = l->data; if (!g_volume_should_automount (volume) || !g_volume_can_mount (volume)) { continue; } mount = g_volume_get_mount (volume); if (mount != NULL) { g_object_unref (mount); continue; } /* pass NULL as GMountOperation to avoid user interaction */ g_volume_mount (volume, 0, NULL, NULL, startup_volume_mount_cb, NULL); } g_list_free_full (volumes, g_object_unref); } } static gboolean automount_all_volumes_idle_cb (gpointer data) { GsdAutomountManager *manager = GSD_AUTOMOUNT_MANAGER (data); automount_all_volumes (manager); manager->priv->automount_idle_id = 0; return FALSE; } static void volume_mount_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GMountOperation *mount_op = user_data; GError *error; char *primary; char *name; error = NULL; gsd_allow_autorun_for_volume_finish (G_VOLUME (source_object)); if (!g_volume_mount_finish (G_VOLUME (source_object), res, &error)) { if (error->code != G_IO_ERROR_FAILED_HANDLED) { name = g_volume_get_name (G_VOLUME (source_object)); primary = g_strdup_printf (_("Unable to mount %s"), name); g_free (name); show_error_dialog (primary, error->message); g_free (primary); } g_error_free (error); } g_object_unref (mount_op); } static void do_mount_volume (GVolume *volume) { GMountOperation *mount_op; mount_op = gtk_mount_operation_new (NULL); g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION); gsd_allow_autorun_for_volume (volume); g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, mount_op); } static void check_volume_queue (GsdAutomountManager *manager) { GList *l; GVolume *volume; if (manager->priv->screensaver_active || manager->priv->lockscreen_active) return; l = manager->priv->volume_queue; while (l != NULL) { volume = l->data; do_mount_volume (volume); manager->priv->volume_queue = g_list_remove (manager->priv->volume_queue, volume); g_object_unref (volume); l = l->next; } manager->priv->volume_queue = NULL; } static void clear_volume_queue (GsdAutomountManager *manager) { if (!manager->priv->volume_queue) return; g_list_free_full (manager->priv->volume_queue, g_object_unref); manager->priv->volume_queue = NULL; } static void check_screen_lock_and_mount (GsdAutomountManager *manager, GVolume *volume) { if (!manager->priv->session_is_active) return; if (manager->priv->screensaver_active || manager->priv->lockscreen_active) { /* queue the volume, to mount it after the screensaver state changed */ g_debug ("Queuing volume %p", volume); manager->priv->volume_queue = g_list_prepend (manager->priv->volume_queue, g_object_ref (volume)); } else { /* mount it immediately */ do_mount_volume (volume); } } static void volume_removed_callback (GVolumeMonitor *monitor, GVolume *volume, GsdAutomountManager *manager) { g_debug ("Volume %p removed, removing from the queue", volume); /* clear it from the queue, if present */ manager->priv->volume_queue = g_list_remove (manager->priv->volume_queue, volume); } static void volume_added_callback (GVolumeMonitor *monitor, GVolume *volume, GsdAutomountManager *manager) { if (g_settings_get_boolean (manager->priv->settings, "automount") && g_volume_should_automount (volume) && g_volume_can_mount (volume)) { check_screen_lock_and_mount (manager, volume); } else { /* Allow gsd_autorun() to run. When the mount is later * added programmatically (i.e. for a blank CD), * gsd_autorun() will be called by mount_added_callback(). */ gsd_allow_autorun_for_volume (volume); gsd_allow_autorun_for_volume_finish (volume); } } static void autorun_show_window (GMount *mount, gpointer user_data) { GFile *location; char *uri; GError *error; char *primary; char *name; location = g_mount_get_root (mount); uri = g_file_get_uri (location); error = NULL; /* use default folder handler */ if (! gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error)) { name = g_mount_get_name (mount); primary = g_strdup_printf (_("Unable to open a folder for %s"), name); g_free (name); show_error_dialog (primary, error->message); g_free (primary); g_error_free (error); } g_free (uri); g_object_unref (location); } static void mount_added_callback (GVolumeMonitor *monitor, GMount *mount, GsdAutomountManager *manager) { /* don't autorun if the session is not active */ if (!manager->priv->session_is_active) { return; } gsd_autorun (mount, manager->priv->settings, autorun_show_window, manager); } static void session_state_changed (GDBusProxy *session, GVariant *changed, char **invalidated, GsdAutomountManager *manager) { GsdAutomountManagerPrivate *p = manager->priv; GVariant *v; v = g_variant_lookup_value (changed, "SessionIsActive", G_VARIANT_TYPE_BOOLEAN); if (v) { gboolean active; active = g_variant_get_boolean (v); g_debug ("Received session is active change: now %s", active ? "active" : "inactive"); /* when doing the fast-user-switch into a new account, * ensure the new account is undimmed and with the backlight on */ if (active) p->session_is_active = TRUE; else p->session_is_active = FALSE; g_variant_unref (v); } if (!p->session_is_active) { if (p->volume_queue != NULL) { g_list_free_full (p->volume_queue, g_object_unref); p->volume_queue = NULL; } } } static gboolean is_session_active (GsdAutomountManager *manager) { GVariant *variant; gboolean is_session_active = FALSE; variant = g_dbus_proxy_get_cached_property (manager->priv->session, "SessionIsActive"); if (variant) { is_session_active = g_variant_get_boolean (variant); g_variant_unref (variant); } return is_session_active; } static void do_initialize_session (GsdAutomountManager *manager) { manager->priv->session = G_DBUS_PROXY (gnome_settings_bus_get_session_proxy ()); g_signal_connect (manager->priv->session, "g-properties-changed", G_CALLBACK (session_state_changed), manager); manager->priv->session_is_active = is_session_active (manager); } #define SCREENSAVER_NAME "org.gnome.ScreenSaver" static void screensaver_signal_callback (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { GsdAutomountManager *manager = user_data; if (g_strcmp0 (signal_name, "ActiveChanged") == 0) { g_variant_get (parameters, "(b)", &manager->priv->screensaver_active); g_debug ("Screensaver active changed to %d", manager->priv->screensaver_active); check_volume_queue (manager); } } static void screensaver_get_active_ready_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GsdAutomountManager *manager = user_data; GDBusProxy *proxy = manager->priv->ss_proxy; GVariant *result; GError *error = NULL; result = g_dbus_proxy_call_finish (proxy, res, &error); if (error != NULL) { g_warning ("Can't call GetActive() on the ScreenSaver object: %s", error->message); g_error_free (error); return; } g_variant_get (result, "(b)", &manager->priv->screensaver_active); g_variant_unref (result); g_debug ("Screensaver GetActive() returned %d", manager->priv->screensaver_active); } static void screensaver_appeared_callback (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GsdAutomountManager *manager = user_data; g_debug ("ScreenSaver name appeared"); manager->priv->ss_proxy = G_DBUS_PROXY (gnome_settings_bus_get_screen_saver_proxy ()); if (manager->priv->ss_proxy == NULL) { g_warning ("Can't get proxy for the ScreenSaver object"); return; } g_debug ("ScreenSaver proxy ready"); g_signal_connect (manager->priv->ss_proxy, "g-signal", G_CALLBACK (screensaver_signal_callback), manager); g_dbus_proxy_call (manager->priv->ss_proxy, "GetActive", NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, screensaver_get_active_ready_cb, manager); } static void screensaver_vanished_callback (GDBusConnection *connection, const gchar *name, gpointer user_data) { GsdAutomountManager *manager = user_data; g_debug ("ScreenSaver name vanished"); manager->priv->screensaver_active = FALSE; g_clear_object (&manager->priv->ss_proxy); if (!manager->priv->ss_proxy && !manager->priv->us_proxy) { /* in this case force a clear of the volume queue, without * mounting them. */ clear_volume_queue (manager); } } static void do_initialize_screensaver (GsdAutomountManager *manager) { GsdAutomountManagerPrivate *p = manager->priv; p->ss_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, SCREENSAVER_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE, screensaver_appeared_callback, screensaver_vanished_callback, manager, NULL); } #define UNITY_NAME "com.canonical.Unity" #define UNITY_SESSION_PATH "/com/canonical/Unity/Session" #define UNITY_SESSION_INTERFACE "com.canonical.Unity.Session" static void unity_session_signal_callback (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { GsdAutomountManager *manager = user_data; if (g_strcmp0 (signal_name, "Locked") == 0) { manager->priv->lockscreen_active = TRUE; check_volume_queue (manager); g_debug ("Unity.Session Locked"); } else if (g_strcmp0 (signal_name, "Unlocked") == 0) { manager->priv->lockscreen_active = FALSE; check_volume_queue (manager); g_debug ("Unity.Session Unlocked"); } } static void unity_session_is_locked_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GsdAutomountManager *manager = user_data; GDBusProxy *proxy = manager->priv->us_proxy; GVariant *result; GError *error = NULL; result = g_dbus_proxy_call_finish (proxy, res, &error); if (error != NULL) { g_warning ("Can't call IsLocked() on the Unity.Session object: %s", error->message); g_error_free (error); return; } g_variant_get (result, "(b)", &manager->priv->lockscreen_active); g_variant_unref (result); g_debug ("Unity Session IsLocked() returned %d", manager->priv->lockscreen_active); } static void unity_session_proxy_ready_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GsdAutomountManager *manager = user_data; GError *error = NULL; GDBusProxy *us_proxy; us_proxy = g_dbus_proxy_new_finish (res, &error); if (error != NULL) { g_warning ("Can't get proxy for the Unity Session object: %s", error->message); g_error_free (error); return; } g_debug ("Unity Session proxy ready"); manager->priv->us_proxy = us_proxy; g_signal_connect (us_proxy, "g-signal", G_CALLBACK (unity_session_signal_callback), manager); g_dbus_proxy_call (us_proxy, "IsLocked", NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, unity_session_is_locked_cb, manager); } static void unity_appeared_callback (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { GsdAutomountManager *manager = user_data; g_debug ("Unity name appeared"); manager->priv->lockscreen_active = FALSE; g_dbus_proxy_new (connection, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, name, UNITY_SESSION_PATH, UNITY_SESSION_INTERFACE, NULL, unity_session_proxy_ready_cb, manager); } static void unity_vanished_callback (GDBusConnection *connection, const gchar *name, gpointer user_data) { GsdAutomountManager *manager = user_data; g_debug ("Unity name vanished"); manager->priv->lockscreen_active = FALSE; g_clear_object (&manager->priv->us_proxy); if (!manager->priv->ss_proxy && !manager->priv->us_proxy) { /* in this case force a clear of the volume queue, without * mounting them. */ clear_volume_queue (manager); } } static void do_initialize_lockscreen (GsdAutomountManager *manager) { GsdAutomountManagerPrivate *p = manager->priv; p->us_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, UNITY_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE, unity_appeared_callback, unity_vanished_callback, manager, NULL); } static void setup_automounter (GsdAutomountManager *manager) { do_initialize_session (manager); do_initialize_screensaver (manager); do_initialize_lockscreen (manager); manager->priv->volume_monitor = g_volume_monitor_get (); g_signal_connect_object (manager->priv->volume_monitor, "mount-added", G_CALLBACK (mount_added_callback), manager, 0); g_signal_connect_object (manager->priv->volume_monitor, "volume-added", G_CALLBACK (volume_added_callback), manager, 0); g_signal_connect_object (manager->priv->volume_monitor, "volume-removed", G_CALLBACK (volume_removed_callback), manager, 0); manager->priv->automount_idle_id = g_idle_add_full (G_PRIORITY_LOW, automount_all_volumes_idle_cb, manager, NULL); } gboolean gsd_automount_manager_start (GsdAutomountManager *manager, GError **error) { g_debug ("Starting automounting manager"); gnome_settings_profile_start (NULL); manager->priv->settings = g_settings_new ("org.gnome.desktop.media-handling"); setup_automounter (manager); gnome_settings_profile_end (NULL); return TRUE; } void gsd_automount_manager_stop (GsdAutomountManager *manager) { GsdAutomountManagerPrivate *p = manager->priv; g_debug ("Stopping automounting manager"); if (p->ss_proxy) { g_signal_handlers_disconnect_by_data (p->ss_proxy, manager); } g_clear_object (&p->session); g_clear_object (&p->volume_monitor); g_clear_object (&p->settings); g_clear_object (&p->ss_proxy); g_clear_object (&p->us_proxy); g_bus_unwatch_name (p->ss_watch_id); g_bus_unwatch_name (p->us_watch_id); if (p->volume_queue != NULL) { g_list_free_full (p->volume_queue, g_object_unref); p->volume_queue = NULL; } if (p->automount_idle_id != 0) { g_source_remove (p->automount_idle_id); p->automount_idle_id = 0; } } static void gsd_automount_manager_class_init (GsdAutomountManagerClass *klass) { g_type_class_add_private (klass, sizeof (GsdAutomountManagerPrivate)); } static void gsd_automount_manager_init (GsdAutomountManager *manager) { manager->priv = GSD_AUTOMOUNT_MANAGER_GET_PRIVATE (manager); } GsdAutomountManager * gsd_automount_manager_new (void) { return GSD_AUTOMOUNT_MANAGER (g_object_new (GSD_TYPE_AUTOMOUNT_MANAGER, NULL)); } ./plugins/automount/gsd-automount-manager.h0000644000004100000410000000461312735467744021375 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 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 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 * * Author: Tomas Bzatek */ #ifndef __GSD_AUTOMOUNT_MANAGER_H #define __GSD_AUTOMOUNT_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_AUTOMOUNT_MANAGER (gsd_automount_manager_get_type ()) #define GSD_AUTOMOUNT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManager)) #define GSD_AUTOMOUNT_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManagerClass)) #define GSD_IS_AUTOMOUNT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_AUTOMOUNT_MANAGER)) #define GSD_IS_AUTOMOUNT_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_AUTOMOUNT_MANAGER)) #define GSD_AUTOMOUNT_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManagerClass)) typedef struct GsdAutomountManagerPrivate GsdAutomountManagerPrivate; typedef struct { GObject parent; GsdAutomountManagerPrivate *priv; } GsdAutomountManager; typedef struct { GObjectClass parent_class; } GsdAutomountManagerClass; GType gsd_automount_manager_get_type (void); GsdAutomountManager * gsd_automount_manager_new (void); gboolean gsd_automount_manager_start (GsdAutomountManager *manager, GError **error); void gsd_automount_manager_stop (GsdAutomountManager *manager); G_END_DECLS #endif /* __GSD_AUTOMOUNT_MANAGER_H */ ./plugins/automount/Makefile.am0000644000004100000410000000201512735467744017034 0ustar www-datawww-datalibexec_PROGRAMS = unity-fallback-mount-helper unity_fallback_mount_helper_SOURCES = \ gnome-fallback-mount-helper.c \ gsd-automount-manager.c \ gsd-automount-manager.h \ gsd-autorun.c \ gsd-autorun.h unity_fallback_mount_helper_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ $(AM_CPPFLAGS) unity_fallback_mount_helper_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(AUTOMOUNT_CFLAGS) unity_fallback_mount_helper_LDADD = \ $(SETTINGS_PLUGIN_LIBS) \ $(AUTOMOUNT_LIBS) \ $(top_builddir)/gnome-settings-daemon/libgsd.la autostartdir = $(sysconfdir)/xdg/autostart autostart_in_files = unity-fallback-mount-helper.desktop.in autostart_in_in_files = unity-fallback-mount-helper.desktop.in.in autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) $(autostart_in_files): $(autostart_in_in_files) @sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@ @INTLTOOL_DESKTOP_RULE@ EXTRA_DIST = $(autostart_in_in_files) CLEANFILES = $(autostart_DATA) $(autostart_in_files) ./plugins/automount/gnome-fallback-mount-helper.c0000644000004100000410000000351112735467744022425 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2010 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 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 * * Author: Tomas Bzatek */ #include "config.h" #include #include #include #include #include "gsd-automount-manager.h" int main (int argc, char **argv) { GMainLoop *loop; GsdAutomountManager *manager; GError *error = NULL; gtk_init (&argc, &argv); bindtextdomain (GETTEXT_PACKAGE, GNOME_SETTINGS_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); loop = g_main_loop_new (NULL, FALSE); manager = gsd_automount_manager_new (); gsd_automount_manager_start (manager, &error); if (error != NULL) { g_printerr ("Unable to start the mount manager: %s", error->message); g_error_free (error); _exit (1); } g_main_loop_run (loop); gsd_automount_manager_stop (manager); g_main_loop_unref (loop); return 0; } ./plugins/automount/gsd-autorun.c0000644000004100000410000007057612735467744017435 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* * gsd-automount.c: helpers for automounting hotplugged volumes * * Copyright (C) 2008, 2010 Red Hat, Inc. * * Nautilus 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 * * Authors: David Zeuthen * Cosimo Cecchi */ #include #include #include #include #include #include #include #include #include "gsd-autorun.h" static gboolean should_autorun_mount (GMount *mount); #define CUSTOM_ITEM_ASK "gsd-item-ask" #define CUSTOM_ITEM_DO_NOTHING "gsd-item-do-nothing" #define CUSTOM_ITEM_OPEN_FOLDER "gsd-item-open-folder" typedef struct { GtkWidget *dialog; GMount *mount; gboolean should_eject; gboolean selected_ignore; gboolean selected_open_folder; GAppInfo *selected_app; gboolean remember; char *x_content_type; GsdAutorunOpenWindow open_window_func; gpointer user_data; } AutorunDialogData; static int gsd_autorun_g_strv_find (char **strv, const char *find_me) { guint index; g_return_val_if_fail (find_me != NULL, -1); for (index = 0; strv[index] != NULL; ++index) { if (strcmp (strv[index], find_me) == 0) { return index; } } return -1; } #define ICON_SIZE_STANDARD 48 static gint get_icon_size_for_stock_size (GtkIconSize size) { gint w, h; if (gtk_icon_size_lookup (size, &w, &h)) { return MAX (w, h); } return ICON_SIZE_STANDARD; } static GdkPixbuf * render_icon (GIcon *icon, gint icon_size) { GdkPixbuf *pixbuf; GtkIconInfo *info; pixbuf = NULL; if (G_IS_THEMED_ICON (icon)) { gchar const * const *names; info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), icon, icon_size, 0); if (info) { pixbuf = gtk_icon_info_load_icon (info, NULL); gtk_icon_info_free (info); } if (pixbuf == NULL) { names = g_themed_icon_get_names (G_THEMED_ICON (icon)); pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), *names, icon_size, 0, NULL); } } else if (G_IS_FILE_ICON (icon)) { GFile *icon_file; gchar *path; icon_file = g_file_icon_get_file (G_FILE_ICON (icon)); path = g_file_get_path (icon_file); pixbuf = gdk_pixbuf_new_from_file_at_size (path, icon_size, icon_size, NULL); g_free (path); g_object_unref (G_OBJECT (icon_file)); } return pixbuf; } static void gsd_autorun_get_preferences (const char *x_content_type, gboolean *pref_start_app, gboolean *pref_ignore, gboolean *pref_open_folder) { GSettings *settings; char **x_content_start_app; char **x_content_ignore; char **x_content_open_folder; g_return_if_fail (pref_start_app != NULL); g_return_if_fail (pref_ignore != NULL); g_return_if_fail (pref_open_folder != NULL); settings = g_settings_new ("org.gnome.desktop.media-handling"); *pref_start_app = FALSE; *pref_ignore = FALSE; *pref_open_folder = FALSE; x_content_start_app = g_settings_get_strv (settings, "autorun-x-content-start-app"); x_content_ignore = g_settings_get_strv (settings, "autorun-x-content-ignore"); x_content_open_folder = g_settings_get_strv (settings, "autorun-x-content-open-folder"); if (x_content_start_app != NULL) { *pref_start_app = gsd_autorun_g_strv_find (x_content_start_app, x_content_type) != -1; } if (x_content_ignore != NULL) { *pref_ignore = gsd_autorun_g_strv_find (x_content_ignore, x_content_type) != -1; } if (x_content_open_folder != NULL) { *pref_open_folder = gsd_autorun_g_strv_find (x_content_open_folder, x_content_type) != -1; } g_strfreev (x_content_ignore); g_strfreev (x_content_start_app); g_strfreev (x_content_open_folder); g_object_unref (settings); } static char ** remove_elem_from_str_array (char **v, const char *s) { GPtrArray *array; guint idx; array = g_ptr_array_new (); for (idx = 0; v[idx] != NULL; idx++) { if (g_strcmp0 (v[idx], s) == 0) { continue; } g_ptr_array_add (array, v[idx]); } g_ptr_array_add (array, NULL); g_free (v); return (char **) g_ptr_array_free (array, FALSE); } static char ** add_elem_to_str_array (char **v, const char *s) { GPtrArray *array; guint idx; array = g_ptr_array_new (); for (idx = 0; v[idx] != NULL; idx++) { g_ptr_array_add (array, v[idx]); } g_ptr_array_add (array, g_strdup (s)); g_ptr_array_add (array, NULL); g_free (v); return (char **) g_ptr_array_free (array, FALSE); } static void gsd_autorun_set_preferences (const char *x_content_type, gboolean pref_start_app, gboolean pref_ignore, gboolean pref_open_folder) { GSettings *settings; char **x_content_start_app; char **x_content_ignore; char **x_content_open_folder; g_assert (x_content_type != NULL); settings = g_settings_new ("org.gnome.desktop.media-handling"); x_content_start_app = g_settings_get_strv (settings, "autorun-x-content-start-app"); x_content_ignore = g_settings_get_strv (settings, "autorun-x-content-ignore"); x_content_open_folder = g_settings_get_strv (settings, "autorun-x-content-open-folder"); x_content_start_app = remove_elem_from_str_array (x_content_start_app, x_content_type); if (pref_start_app) { x_content_start_app = add_elem_to_str_array (x_content_start_app, x_content_type); } g_settings_set_strv (settings, "autorun-x-content-start-app", (const gchar * const*) x_content_start_app); x_content_ignore = remove_elem_from_str_array (x_content_ignore, x_content_type); if (pref_ignore) { x_content_ignore = add_elem_to_str_array (x_content_ignore, x_content_type); } g_settings_set_strv (settings, "autorun-x-content-ignore", (const gchar * const*) x_content_ignore); x_content_open_folder = remove_elem_from_str_array (x_content_open_folder, x_content_type); if (pref_open_folder) { x_content_open_folder = add_elem_to_str_array (x_content_open_folder, x_content_type); } g_settings_set_strv (settings, "autorun-x-content-open-folder", (const gchar * const*) x_content_open_folder); g_strfreev (x_content_open_folder); g_strfreev (x_content_ignore); g_strfreev (x_content_start_app); g_object_unref (settings); } static void custom_item_activated_cb (GtkAppChooserButton *button, const gchar *item, gpointer user_data) { gchar *content_type; AutorunDialogData *data = user_data; content_type = gtk_app_chooser_get_content_type (GTK_APP_CHOOSER (button)); if (g_strcmp0 (item, CUSTOM_ITEM_ASK) == 0) { gsd_autorun_set_preferences (content_type, FALSE, FALSE, FALSE); data->selected_open_folder = FALSE; data->selected_ignore = FALSE; } else if (g_strcmp0 (item, CUSTOM_ITEM_OPEN_FOLDER) == 0) { gsd_autorun_set_preferences (content_type, FALSE, FALSE, TRUE); data->selected_open_folder = TRUE; data->selected_ignore = FALSE; } else if (g_strcmp0 (item, CUSTOM_ITEM_DO_NOTHING) == 0) { gsd_autorun_set_preferences (content_type, FALSE, TRUE, FALSE); data->selected_open_folder = FALSE; data->selected_ignore = TRUE; } g_free (content_type); } static void combo_box_changed_cb (GtkComboBox *combo_box, gpointer user_data) { GAppInfo *info; AutorunDialogData *data = user_data; info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (combo_box)); if (info == NULL) return; g_clear_object (&data->selected_app); data->selected_app = info; } static void prepare_combo_box (GtkWidget *combo_box, AutorunDialogData *data) { GtkAppChooserButton *app_chooser = GTK_APP_CHOOSER_BUTTON (combo_box); GIcon *icon; gboolean pref_ask; gboolean pref_start_app; gboolean pref_ignore; gboolean pref_open_folder; GAppInfo *info; gchar *content_type; content_type = gtk_app_chooser_get_content_type (GTK_APP_CHOOSER (app_chooser)); /* fetch preferences for this content type */ gsd_autorun_get_preferences (content_type, &pref_start_app, &pref_ignore, &pref_open_folder); pref_ask = !pref_start_app && !pref_ignore && !pref_open_folder; info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (combo_box)); /* append the separator only if we have >= 1 apps in the chooser */ if (info != NULL) { gtk_app_chooser_button_append_separator (app_chooser); g_object_unref (info); } icon = g_themed_icon_new (GTK_STOCK_DIALOG_QUESTION); gtk_app_chooser_button_append_custom_item (app_chooser, CUSTOM_ITEM_ASK, _("Ask what to do"), icon); g_object_unref (icon); icon = g_themed_icon_new (GTK_STOCK_CLOSE); gtk_app_chooser_button_append_custom_item (app_chooser, CUSTOM_ITEM_DO_NOTHING, _("Do Nothing"), icon); g_object_unref (icon); icon = g_themed_icon_new ("folder-open"); gtk_app_chooser_button_append_custom_item (app_chooser, CUSTOM_ITEM_OPEN_FOLDER, _("Open Folder"), icon); g_object_unref (icon); gtk_app_chooser_button_set_show_dialog_item (app_chooser, TRUE); if (pref_ask) { gtk_app_chooser_button_set_active_custom_item (app_chooser, CUSTOM_ITEM_ASK); } else if (pref_ignore) { gtk_app_chooser_button_set_active_custom_item (app_chooser, CUSTOM_ITEM_DO_NOTHING); } else if (pref_open_folder) { gtk_app_chooser_button_set_active_custom_item (app_chooser, CUSTOM_ITEM_OPEN_FOLDER); } g_signal_connect (app_chooser, "changed", G_CALLBACK (combo_box_changed_cb), data); g_signal_connect (app_chooser, "custom-item-activated", G_CALLBACK (custom_item_activated_cb), data); g_free (content_type); } static gboolean is_shift_pressed (void) { gboolean ret; XkbStateRec state; Bool status; ret = FALSE; gdk_error_trap_push (); status = XkbGetState (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XkbUseCoreKbd, &state); gdk_error_trap_pop_ignored (); if (status == Success) { ret = state.mods & ShiftMask; } return ret; } enum { AUTORUN_DIALOG_RESPONSE_EJECT = 0 }; static void gsd_autorun_launch_for_mount (GMount *mount, GAppInfo *app_info) { GFile *root; GdkAppLaunchContext *launch_context; GError *error; gboolean result; GList *list; gchar *uri_scheme; gchar *uri; root = g_mount_get_root (mount); list = g_list_append (NULL, root); launch_context = gdk_app_launch_context_new (); error = NULL; result = g_app_info_launch (app_info, list, G_APP_LAUNCH_CONTEXT (launch_context), &error); g_object_unref (launch_context); if (!result) { if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_SUPPORTED) { uri = g_file_get_uri (root); uri_scheme = g_uri_parse_scheme (uri); /* FIXME: Present user a dialog to choose another app when the last one failed to handle a file */ g_warning ("Cannot open location: %s\n", error->message); g_free (uri_scheme); g_free (uri); } else { g_warning ("Cannot open app: %s\n", error->message); } g_error_free (error); } g_list_free (list); g_object_unref (root); } static void autorun_dialog_mount_unmounted (GMount *mount, AutorunDialogData *data); static void autorun_dialog_destroy (AutorunDialogData *data) { g_signal_handlers_disconnect_by_func (G_OBJECT (data->mount), G_CALLBACK (autorun_dialog_mount_unmounted), data); gtk_widget_destroy (GTK_WIDGET (data->dialog)); if (data->selected_app != NULL) { g_object_unref (data->selected_app); } g_object_unref (data->mount); g_free (data->x_content_type); g_free (data); } static void autorun_dialog_mount_unmounted (GMount *mount, AutorunDialogData *data) { /* remove the dialog if the media is unmounted */ autorun_dialog_destroy (data); } static void unmount_mount_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error; char *primary; gboolean unmounted; gboolean should_eject; GtkWidget *dialog; should_eject = user_data != NULL; error = NULL; if (should_eject) { unmounted = g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error); } else { unmounted = g_mount_unmount_with_operation_finish (G_MOUNT (source_object), res, &error); } if (! unmounted) { if (error->code != G_IO_ERROR_FAILED_HANDLED) { if (should_eject) { primary = g_strdup_printf (_("Unable to eject %p"), source_object); } else { primary = g_strdup_printf (_("Unable to unmount %p"), source_object); } dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s", primary); gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), "%s", error->message); gtk_widget_show (GTK_WIDGET (dialog)); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); g_free (primary); } } if (error != NULL) { g_error_free (error); } } static void do_unmount (GMount *mount, gboolean should_eject, GtkWindow *window) { GMountOperation *mount_op; mount_op = gtk_mount_operation_new (window); if (should_eject) { g_mount_eject_with_operation (mount, 0, mount_op, NULL, unmount_mount_callback, (gpointer) 1); } else { g_mount_unmount_with_operation (mount, 0, mount_op, NULL, unmount_mount_callback, (gpointer) 0); } g_object_unref (mount_op); } static void autorun_dialog_response (GtkDialog *dialog, gint response, AutorunDialogData *data) { switch (response) { case AUTORUN_DIALOG_RESPONSE_EJECT: do_unmount (data->mount, data->should_eject, GTK_WINDOW (dialog)); break; case GTK_RESPONSE_NONE: /* window was closed */ break; case GTK_RESPONSE_CANCEL: break; case GTK_RESPONSE_OK: /* do the selected action */ if (data->remember) { /* make sure we don't ask again */ gsd_autorun_set_preferences (data->x_content_type, TRUE, data->selected_ignore, data->selected_open_folder); if (!data->selected_ignore && !data->selected_open_folder && data->selected_app != NULL) { g_app_info_set_as_default_for_type (data->selected_app, data->x_content_type, NULL); } } else { /* make sure we do ask again */ gsd_autorun_set_preferences (data->x_content_type, FALSE, FALSE, FALSE); } if (!data->selected_ignore && !data->selected_open_folder && data->selected_app != NULL) { gsd_autorun_launch_for_mount (data->mount, data->selected_app); } else if (!data->selected_ignore && data->selected_open_folder) { if (data->open_window_func != NULL) data->open_window_func (data->mount, data->user_data); } break; } autorun_dialog_destroy (data); } static void autorun_always_toggled (GtkToggleButton *togglebutton, AutorunDialogData *data) { data->remember = gtk_toggle_button_get_active (togglebutton); } static gboolean combo_box_enter_ok (GtkWidget *togglebutton, GdkEventKey *event, GtkDialog *dialog) { if (event->keyval == GDK_KEY_KP_Enter || event->keyval == GDK_KEY_Return) { gtk_dialog_response (dialog, GTK_RESPONSE_OK); return TRUE; } return FALSE; } /* returns TRUE if a folder window should be opened */ static gboolean do_autorun_for_content_type (GMount *mount, const char *x_content_type, GsdAutorunOpenWindow open_window_func, gpointer user_data) { AutorunDialogData *data; GtkWidget *dialog; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *label; GtkWidget *combo_box; GtkWidget *always_check_button; GtkWidget *eject_button; GtkWidget *image; char *markup; char *content_description; char *mount_name; GIcon *icon; GdkPixbuf *pixbuf; int icon_size; gboolean user_forced_dialog; gboolean pref_ask; gboolean pref_start_app; gboolean pref_ignore; gboolean pref_open_folder; char *media_greeting; gboolean ret; ret = FALSE; mount_name = NULL; if (g_content_type_is_a (x_content_type, "x-content/win32-software")) { /* don't pop up the dialog anyway if the content type says * windows software. */ goto out; } user_forced_dialog = is_shift_pressed (); gsd_autorun_get_preferences (x_content_type, &pref_start_app, &pref_ignore, &pref_open_folder); pref_ask = !pref_start_app && !pref_ignore && !pref_open_folder; if (user_forced_dialog) { goto show_dialog; } if (!pref_ask && !pref_ignore && !pref_open_folder) { GAppInfo *app_info; app_info = g_app_info_get_default_for_type (x_content_type, FALSE); if (app_info != NULL) { gsd_autorun_launch_for_mount (mount, app_info); } goto out; } if (pref_open_folder) { ret = TRUE; goto out; } if (pref_ignore) { goto out; } show_dialog: mount_name = g_mount_get_name (mount); dialog = gtk_dialog_new (); hbox = gtk_hbox_new (FALSE, 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox), 12); icon = g_mount_get_icon (mount); icon_size = get_icon_size_for_stock_size (GTK_ICON_SIZE_DIALOG); image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG); pixbuf = render_icon (icon, icon_size); gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0); /* also use the icon on the dialog */ gtk_window_set_title (GTK_WINDOW (dialog), mount_name); gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); g_object_unref (icon); if (pixbuf) { g_object_unref (pixbuf); } vbox = gtk_vbox_new (FALSE, 12); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); label = gtk_label_new (NULL); /* Customize greeting for well-known x-content types */ if (strcmp (x_content_type, "x-content/audio-cdda") == 0) { media_greeting = _("You have just inserted an Audio CD."); } else if (strcmp (x_content_type, "x-content/audio-dvd") == 0) { media_greeting = _("You have just inserted an Audio DVD."); } else if (strcmp (x_content_type, "x-content/video-dvd") == 0) { media_greeting = _("You have just inserted a Video DVD."); } else if (strcmp (x_content_type, "x-content/video-vcd") == 0) { media_greeting = _("You have just inserted a Video CD."); } else if (strcmp (x_content_type, "x-content/video-svcd") == 0) { media_greeting = _("You have just inserted a Super Video CD."); } else if (strcmp (x_content_type, "x-content/blank-cd") == 0) { media_greeting = _("You have just inserted a blank CD."); } else if (strcmp (x_content_type, "x-content/blank-dvd") == 0) { media_greeting = _("You have just inserted a blank DVD."); } else if (strcmp (x_content_type, "x-content/blank-cd") == 0) { media_greeting = _("You have just inserted a blank Blu-Ray disc."); } else if (strcmp (x_content_type, "x-content/blank-cd") == 0) { media_greeting = _("You have just inserted a blank HD DVD."); } else if (strcmp (x_content_type, "x-content/image-photocd") == 0) { media_greeting = _("You have just inserted a Photo CD."); } else if (strcmp (x_content_type, "x-content/image-picturecd") == 0) { media_greeting = _("You have just inserted a Picture CD."); } else if (strcmp (x_content_type, "x-content/image-dcf") == 0) { media_greeting = _("You have just inserted a medium with digital photos."); } else if (strcmp (x_content_type, "x-content/audio-player") == 0) { media_greeting = _("You have just inserted a digital audio player."); } else if (g_content_type_is_a (x_content_type, "x-content/software")) { media_greeting = _("You have just inserted a medium with software intended to be automatically started."); } else { /* fallback to generic greeting */ media_greeting = _("You have just inserted a medium."); } markup = g_strdup_printf ("%s %s", media_greeting, _("Choose what application to launch.")); gtk_label_set_markup (GTK_LABEL (label), markup); g_free (markup); gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); gtk_label_set_max_width_chars (GTK_LABEL (label), 72); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); label = gtk_label_new (NULL); content_description = g_content_type_get_description (x_content_type); markup = g_strdup_printf (_("Select how to open \"%s\" and whether to perform this action in the future for other media of type \"%s\"."), mount_name, content_description); g_free (content_description); gtk_label_set_markup (GTK_LABEL (label), markup); g_free (markup); gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); gtk_label_set_max_width_chars (GTK_LABEL (label), 72); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); data = g_new0 (AutorunDialogData, 1); data->dialog = dialog; data->mount = g_object_ref (mount); data->remember = !pref_ask; data->selected_ignore = pref_ignore; data->x_content_type = g_strdup (x_content_type); data->selected_app = g_app_info_get_default_for_type (x_content_type, FALSE); data->open_window_func = open_window_func; data->user_data = user_data; combo_box = gtk_app_chooser_button_new (x_content_type); prepare_combo_box (combo_box, data); g_signal_connect (G_OBJECT (combo_box), "key-press-event", G_CALLBACK (combo_box_enter_ok), dialog); gtk_box_pack_start (GTK_BOX (vbox), combo_box, TRUE, TRUE, 0); always_check_button = gtk_check_button_new_with_mnemonic (_("_Always perform this action")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (always_check_button), data->remember); g_signal_connect (G_OBJECT (always_check_button), "toggled", G_CALLBACK (autorun_always_toggled), data); gtk_box_pack_start (GTK_BOX (vbox), always_check_button, TRUE, TRUE, 0); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); if (g_mount_can_eject (mount)) { GtkWidget *eject_image; eject_button = gtk_button_new_with_mnemonic (_("_Eject")); eject_image = gtk_image_new_from_icon_name ("media-eject", GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (eject_button), eject_image); data->should_eject = TRUE; } else { eject_button = gtk_button_new_with_mnemonic (_("_Unmount")); data->should_eject = FALSE; } gtk_dialog_add_action_widget (GTK_DIALOG (dialog), eject_button, AUTORUN_DIALOG_RESPONSE_EJECT); gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dialog))), eject_button, TRUE); /* show the dialog */ gtk_widget_show_all (dialog); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (autorun_dialog_response), data); g_signal_connect (G_OBJECT (data->mount), "unmounted", G_CALLBACK (autorun_dialog_mount_unmounted), data); out: g_free (mount_name); return ret; } typedef struct { GMount *mount; GsdAutorunOpenWindow open_window_func; gpointer user_data; GSettings *settings; } AutorunData; static void autorun_guessed_content_type_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error; char **guessed_content_type; AutorunData *data = user_data; gboolean open_folder; open_folder = FALSE; error = NULL; guessed_content_type = g_mount_guess_content_type_finish (G_MOUNT (source_object), res, &error); g_object_set_data_full (source_object, "gsd-content-type-cache", g_strdupv (guessed_content_type), (GDestroyNotify)g_strfreev); if (error != NULL) { g_warning ("Unable to guess content type for mount: %s", error->message); g_error_free (error); } else { if (guessed_content_type != NULL && g_strv_length (guessed_content_type) > 0) { int n; for (n = 0; guessed_content_type[n] != NULL; n++) { if (do_autorun_for_content_type (data->mount, guessed_content_type[n], data->open_window_func, data->user_data)) { open_folder = TRUE; } } g_strfreev (guessed_content_type); } else { if (g_settings_get_boolean (data->settings, "automount-open")) { open_folder = TRUE; } } } /* only open the folder once.. */ if (open_folder && data->open_window_func != NULL) { data->open_window_func (data->mount, data->user_data); } g_object_unref (data->mount); g_object_unref (data->settings); g_free (data); } void gsd_autorun (GMount *mount, GSettings *settings, GsdAutorunOpenWindow open_window_func, gpointer user_data) { AutorunData *data; if (!should_autorun_mount (mount) || g_settings_get_boolean (settings, "autorun-never")) { return; } data = g_new0 (AutorunData, 1); data->mount = g_object_ref (mount); data->open_window_func = open_window_func; data->user_data = user_data; data->settings = g_object_ref (settings); g_mount_guess_content_type (mount, FALSE, NULL, autorun_guessed_content_type_callback, data); } static gboolean remove_allow_volume (gpointer data) { GVolume *volume = data; g_object_set_data (G_OBJECT (volume), "gsd-allow-autorun", NULL); return FALSE; } void gsd_allow_autorun_for_volume (GVolume *volume) { g_object_set_data (G_OBJECT (volume), "gsd-allow-autorun", GINT_TO_POINTER (1)); } #define INHIBIT_AUTORUN_SECONDS 10 void gsd_allow_autorun_for_volume_finish (GVolume *volume) { if (g_object_get_data (G_OBJECT (volume), "gsd-allow-autorun") != NULL) { g_timeout_add_seconds_full (0, INHIBIT_AUTORUN_SECONDS, remove_allow_volume, g_object_ref (volume), g_object_unref); } } static gboolean should_skip_native_mount_root (GFile *root) { char *path; gboolean should_skip; /* skip any mounts in hidden directory hierarchies */ path = g_file_get_path (root); should_skip = strstr (path, "/.") != NULL; g_free (path); return should_skip; } static gboolean should_autorun_mount (GMount *mount) { GFile *root; GVolume *enclosing_volume; gboolean ignore_autorun; ignore_autorun = TRUE; enclosing_volume = g_mount_get_volume (mount); if (enclosing_volume != NULL) { if (g_object_get_data (G_OBJECT (enclosing_volume), "gsd-allow-autorun") != NULL) { ignore_autorun = FALSE; g_object_set_data (G_OBJECT (enclosing_volume), "gsd-allow-autorun", NULL); } } if (ignore_autorun) { if (enclosing_volume != NULL) { g_object_unref (enclosing_volume); } return FALSE; } root = g_mount_get_root (mount); /* only do autorun on local files or files where g_volume_should_automount() returns TRUE */ ignore_autorun = TRUE; if ((g_file_is_native (root) && !should_skip_native_mount_root (root)) || (enclosing_volume != NULL && g_volume_should_automount (enclosing_volume))) { ignore_autorun = FALSE; } if (enclosing_volume != NULL) { g_object_unref (enclosing_volume); } g_object_unref (root); return !ignore_autorun; } ./plugins/automount/unity-fallback-mount-helper.desktop.in.in0000644000004100000410000000045512735467744024735 0ustar www-datawww-data[Desktop Entry] _Name=Mount Helper _Comment=Automount and autorun plugged devices Exec=@LIBEXECDIR@/unity-fallback-mount-helper Icon=drive-optical Terminal=false Type=Application Categories= NoDisplay=true OnlyShowIn=Unity; X-GNOME-Autostart-Notify=true AutostartCondition=GNOME3 unless-session gnome ./plugins/automount/gsd-autorun.h0000644000004100000410000000316112735467744017424 0ustar www-datawww-data/* * gsd-automount.h:helpers for automounting hotplugged volumes * * Copyright (C) 2008 Red Hat, Inc. * * Nautilus 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 * * Authors: David Zeuthen * Cosimo Cecchi */ /* TODO: * * - unmount all the media we've automounted on shutdown * - finish x-content / * types * - finalize the semi-spec * - add probing/sniffing code * - implement missing features * - "Open Folder when mounted" * - Autorun spec (e.g. $ROOT/.autostart) * */ #ifndef __GSD_AUTORUN_H__ #define __GSD_AUTORUN_H__ #include #include typedef void (*GsdAutorunOpenWindow) (GMount *mount, gpointer user_data); void gsd_autorun (GMount *mount, GSettings *settings, GsdAutorunOpenWindow open_window_func, gpointer user_data); void gsd_allow_autorun_for_volume (GVolume *volume); void gsd_allow_autorun_for_volume_finish (GVolume *volume); #endif /* __GSD_AUTORUN_H__ */ ./plugins/color/0000755000004100000410000000000012735467763014066 5ustar www-datawww-data./plugins/color/gcm-edid.h0000644000004100000410000000725312735467744015716 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2009-2010 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GCM_EDID_H #define __GCM_EDID_H #include #include G_BEGIN_DECLS #define GCM_TYPE_EDID (gcm_edid_get_type ()) #define GCM_EDID(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_EDID, GcmEdid)) #define GCM_EDID_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_EDID, GcmEdidClass)) #define GCM_IS_EDID(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_EDID)) #define GCM_IS_EDID_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_EDID)) #define GCM_EDID_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_EDID, GcmEdidClass)) #define GCM_EDID_ERROR (gcm_edid_error_quark ()) typedef struct _GcmEdidPrivate GcmEdidPrivate; typedef struct _GcmEdid GcmEdid; typedef struct _GcmEdidClass GcmEdidClass; struct _GcmEdid { GObject parent; GcmEdidPrivate *priv; }; struct _GcmEdidClass { GObjectClass parent_class; }; enum { GCM_EDID_ERROR_FAILED_TO_PARSE }; GType gcm_edid_get_type (void); GQuark gcm_edid_error_quark (void); GcmEdid *gcm_edid_new (void); void gcm_edid_reset (GcmEdid *edid); gboolean gcm_edid_parse (GcmEdid *edid, const guint8 *data, gsize length, GError **error); const gchar *gcm_edid_get_monitor_name (GcmEdid *edid); const gchar *gcm_edid_get_vendor_name (GcmEdid *edid); const gchar *gcm_edid_get_serial_number (GcmEdid *edid); const gchar *gcm_edid_get_eisa_id (GcmEdid *edid); const gchar *gcm_edid_get_checksum (GcmEdid *edid); const gchar *gcm_edid_get_pnp_id (GcmEdid *edid); guint gcm_edid_get_width (GcmEdid *edid); guint gcm_edid_get_height (GcmEdid *edid); gfloat gcm_edid_get_gamma (GcmEdid *edid); const CdColorYxy *gcm_edid_get_red (GcmEdid *edid); const CdColorYxy *gcm_edid_get_green (GcmEdid *edid); const CdColorYxy *gcm_edid_get_blue (GcmEdid *edid); const CdColorYxy *gcm_edid_get_white (GcmEdid *edid); G_END_DECLS #endif /* __GCM_EDID_H */ ./plugins/color/gcm-edid.c0000644000004100000410000003427312735467744015713 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2008 Soren Sandmann * Copyright (C) 2009-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include "gcm-edid.h" #include "gsd-pnp-ids.h" static void gcm_edid_finalize (GObject *object); #define GCM_EDID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_EDID, GcmEdidPrivate)) struct _GcmEdidPrivate { gchar *monitor_name; gchar *vendor_name; gchar *serial_number; gchar *eisa_id; gchar *checksum; gchar *pnp_id; guint width; guint height; gfloat gamma; CdColorYxy *red; CdColorYxy *green; CdColorYxy *blue; CdColorYxy *white; GsdPnpIds *pnp_ids; }; G_DEFINE_TYPE (GcmEdid, gcm_edid, G_TYPE_OBJECT) #define GCM_EDID_OFFSET_PNPID 0x08 #define GCM_EDID_OFFSET_SERIAL 0x0c #define GCM_EDID_OFFSET_SIZE 0x15 #define GCM_EDID_OFFSET_GAMMA 0x17 #define GCM_EDID_OFFSET_DATA_BLOCKS 0x36 #define GCM_EDID_OFFSET_LAST_BLOCK 0x6c #define GCM_EDID_OFFSET_EXTENSION_BLOCK_COUNT 0x7e #define GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc #define GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff #define GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA 0xf9 #define GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe #define GCM_DESCRIPTOR_COLOR_POINT 0xfb GQuark gcm_edid_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gcm_edid_error"); return quark; } const gchar * gcm_edid_get_monitor_name (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->monitor_name; } const gchar * gcm_edid_get_vendor_name (GcmEdid *edid) { GcmEdidPrivate *priv = edid->priv; g_return_val_if_fail (GCM_IS_EDID (edid), NULL); if (priv->vendor_name == NULL) priv->vendor_name = gsd_pnp_ids_get_pnp_id (priv->pnp_ids, priv->pnp_id); return priv->vendor_name; } const gchar * gcm_edid_get_serial_number (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->serial_number; } const gchar * gcm_edid_get_eisa_id (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->eisa_id; } const gchar * gcm_edid_get_checksum (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->checksum; } const gchar * gcm_edid_get_pnp_id (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->pnp_id; } guint gcm_edid_get_width (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), 0); return edid->priv->width; } guint gcm_edid_get_height (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), 0); return edid->priv->height; } gfloat gcm_edid_get_gamma (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), 0.0f); return edid->priv->gamma; } const CdColorYxy * gcm_edid_get_red (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->red; } const CdColorYxy * gcm_edid_get_green (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->green; } const CdColorYxy * gcm_edid_get_blue (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->blue; } const CdColorYxy * gcm_edid_get_white (GcmEdid *edid) { g_return_val_if_fail (GCM_IS_EDID (edid), NULL); return edid->priv->white; } void gcm_edid_reset (GcmEdid *edid) { GcmEdidPrivate *priv = edid->priv; g_return_if_fail (GCM_IS_EDID (edid)); /* free old data */ g_free (priv->monitor_name); g_free (priv->vendor_name); g_free (priv->serial_number); g_free (priv->eisa_id); g_free (priv->checksum); /* do not deallocate, just blank */ priv->pnp_id[0] = '\0'; /* set to default values */ priv->monitor_name = NULL; priv->vendor_name = NULL; priv->serial_number = NULL; priv->eisa_id = NULL; priv->checksum = NULL; priv->width = 0; priv->height = 0; priv->gamma = 0.0f; } static gint gcm_edid_get_bit (gint in, gint bit) { return (in & (1 << bit)) >> bit; } /** * gcm_edid_get_bits: **/ static gint gcm_edid_get_bits (gint in, gint begin, gint end) { gint mask = (1 << (end - begin + 1)) - 1; return (in >> begin) & mask; } /** * gcm_edid_decode_fraction: **/ static gdouble gcm_edid_decode_fraction (gint high, gint low) { gdouble result = 0.0; gint i; high = (high << 2) | low; for (i = 0; i < 10; ++i) result += gcm_edid_get_bit (high, i) * pow (2, i - 10); return result; } static gchar * gcm_edid_parse_string (const guint8 *data) { gchar *text; guint i; guint replaced = 0; /* this is always 12 bytes, but we can't guarantee it's null * terminated or not junk. */ text = g_strndup ((const gchar *) data, 12); /* remove insane newline chars */ g_strdelimit (text, "\n\r", '\0'); /* remove spaces */ g_strchomp (text); /* nothing left? */ if (text[0] == '\0') { g_free (text); text = NULL; goto out; } /* ensure string is printable */ for (i = 0; text[i] != '\0'; i++) { if (!g_ascii_isprint (text[i])) { text[i] = '-'; replaced++; } } /* if the string is junk, ignore the string */ if (replaced > 4) { g_free (text); text = NULL; goto out; } out: return text; } gboolean gcm_edid_parse (GcmEdid *edid, const guint8 *data, gsize length, GError **error) { gboolean ret = TRUE; guint i; GcmEdidPrivate *priv = edid->priv; guint32 serial; gchar *tmp; /* check header */ if (length < 128) { g_set_error_literal (error, GCM_EDID_ERROR, GCM_EDID_ERROR_FAILED_TO_PARSE, "EDID length is too small"); ret = FALSE; goto out; } if (data[0] != 0x00 || data[1] != 0xff) { g_set_error_literal (error, GCM_EDID_ERROR, GCM_EDID_ERROR_FAILED_TO_PARSE, "Failed to parse EDID header"); ret = FALSE; goto out; } /* free old data */ gcm_edid_reset (edid); /* decode the PNP ID from three 5 bit words packed into 2 bytes * /--08--\/--09--\ * 7654321076543210 * |\---/\---/\---/ * R C1 C2 C3 */ priv->pnp_id[0] = 'A' + ((data[GCM_EDID_OFFSET_PNPID+0] & 0x7c) / 4) - 1; priv->pnp_id[1] = 'A' + ((data[GCM_EDID_OFFSET_PNPID+0] & 0x3) * 8) + ((data[GCM_EDID_OFFSET_PNPID+1] & 0xe0) / 32) - 1; priv->pnp_id[2] = 'A' + (data[GCM_EDID_OFFSET_PNPID+1] & 0x1f) - 1; /* maybe there isn't a ASCII serial number descriptor, so use this instead */ serial = (guint32) data[GCM_EDID_OFFSET_SERIAL+0]; serial += (guint32) data[GCM_EDID_OFFSET_SERIAL+1] * 0x100; serial += (guint32) data[GCM_EDID_OFFSET_SERIAL+2] * 0x10000; serial += (guint32) data[GCM_EDID_OFFSET_SERIAL+3] * 0x1000000; if (serial > 0) priv->serial_number = g_strdup_printf ("%" G_GUINT32_FORMAT, serial); /* get the size */ priv->width = data[GCM_EDID_OFFSET_SIZE+0]; priv->height = data[GCM_EDID_OFFSET_SIZE+1]; /* we don't care about aspect */ if (priv->width == 0 || priv->height == 0) { priv->width = 0; priv->height = 0; } /* get gamma */ if (data[GCM_EDID_OFFSET_GAMMA] == 0xff) { priv->gamma = 1.0f; } else { priv->gamma = ((gfloat) data[GCM_EDID_OFFSET_GAMMA] / 100) + 1; } /* get color red */ priv->red->x = gcm_edid_decode_fraction (data[0x1b], gcm_edid_get_bits (data[0x19], 6, 7)); priv->red->y = gcm_edid_decode_fraction (data[0x1c], gcm_edid_get_bits (data[0x19], 5, 4)); /* get color green */ priv->green->x = gcm_edid_decode_fraction (data[0x1d], gcm_edid_get_bits (data[0x19], 2, 3)); priv->green->y = gcm_edid_decode_fraction (data[0x1e], gcm_edid_get_bits (data[0x19], 0, 1)); /* get color blue */ priv->blue->x = gcm_edid_decode_fraction (data[0x1f], gcm_edid_get_bits (data[0x1a], 6, 7)); priv->blue->y = gcm_edid_decode_fraction (data[0x20], gcm_edid_get_bits (data[0x1a], 4, 5)); /* get color white */ priv->white->x = gcm_edid_decode_fraction (data[0x21], gcm_edid_get_bits (data[0x1a], 2, 3)); priv->white->y = gcm_edid_decode_fraction (data[0x22], gcm_edid_get_bits (data[0x1a], 0, 1)); /* parse EDID data */ for (i = GCM_EDID_OFFSET_DATA_BLOCKS; i <= GCM_EDID_OFFSET_LAST_BLOCK; i += 18) { /* ignore pixel clock data */ if (data[i] != 0) continue; if (data[i+2] != 0) continue; /* any useful blocks? */ if (data[i+3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME) { tmp = gcm_edid_parse_string (&data[i+5]); if (tmp != NULL) { g_free (priv->monitor_name); priv->monitor_name = tmp; } } else if (data[i+3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) { tmp = gcm_edid_parse_string (&data[i+5]); if (tmp != NULL) { g_free (priv->serial_number); priv->serial_number = tmp; } } else if (data[i+3] == GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA) { g_warning ("failing to parse color management data"); } else if (data[i+3] == GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) { tmp = gcm_edid_parse_string (&data[i+5]); if (tmp != NULL) { g_free (priv->eisa_id); priv->eisa_id = tmp; } } else if (data[i+3] == GCM_DESCRIPTOR_COLOR_POINT) { if (data[i+3+9] != 0xff) { /* extended EDID block(1) which contains * a better gamma value */ priv->gamma = ((gfloat) data[i+3+9] / 100) + 1; } if (data[i+3+14] != 0xff) { /* extended EDID block(2) which contains * a better gamma value */ priv->gamma = ((gfloat) data[i+3+9] / 100) + 1; } } } /* calculate checksum */ priv->checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5, data, length); out: return ret; } static void gcm_edid_class_init (GcmEdidClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gcm_edid_finalize; g_type_class_add_private (klass, sizeof (GcmEdidPrivate)); } static void gcm_edid_init (GcmEdid *edid) { edid->priv = GCM_EDID_GET_PRIVATE (edid); edid->priv->pnp_ids = gsd_pnp_ids_new (); edid->priv->pnp_id = g_new0 (gchar, 4); edid->priv->red = cd_color_yxy_new (); edid->priv->green = cd_color_yxy_new (); edid->priv->blue = cd_color_yxy_new (); edid->priv->white = cd_color_yxy_new (); } static void gcm_edid_finalize (GObject *object) { GcmEdid *edid = GCM_EDID (object); GcmEdidPrivate *priv = edid->priv; g_free (priv->monitor_name); g_free (priv->vendor_name); g_free (priv->serial_number); g_free (priv->eisa_id); g_free (priv->checksum); g_free (priv->pnp_id); cd_color_yxy_free (priv->white); cd_color_yxy_free (priv->red); cd_color_yxy_free (priv->green); cd_color_yxy_free (priv->blue); g_object_unref (priv->pnp_ids); G_OBJECT_CLASS (gcm_edid_parent_class)->finalize (object); } GcmEdid * gcm_edid_new (void) { GcmEdid *edid; edid = g_object_new (GCM_TYPE_EDID, NULL); return GCM_EDID (edid); } ./plugins/color/gcm-dmi.h0000644000004100000410000000422612735467744015557 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2009-2010 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GCM_DMI_H #define __GCM_DMI_H #include G_BEGIN_DECLS #define GCM_TYPE_DMI (gcm_dmi_get_type ()) #define GCM_DMI(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_DMI, GcmDmi)) #define GCM_DMI_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_DMI, GcmDmiClass)) #define GCM_IS_DMI(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_DMI)) #define GCM_IS_DMI_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_DMI)) #define GCM_DMI_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_DMI, GcmDmiClass)) typedef struct _GcmDmiPrivate GcmDmiPrivate; typedef struct _GcmDmi GcmDmi; typedef struct _GcmDmiClass GcmDmiClass; struct _GcmDmi { GObject parent; GcmDmiPrivate *priv; }; struct _GcmDmiClass { GObjectClass parent_class; }; GType gcm_dmi_get_type (void); GcmDmi *gcm_dmi_new (void); const gchar *gcm_dmi_get_name (GcmDmi *dmi); const gchar *gcm_dmi_get_version (GcmDmi *dmi); const gchar *gcm_dmi_get_vendor (GcmDmi *dmi); G_END_DECLS #endif /* __GCM_DMI_H */ ./plugins/color/test-data/0000755000004100000410000000000012735467744015753 5ustar www-datawww-data./plugins/color/test-data/Lenovo-T61-Internal.bin0000644000004100000410000000020012735467744022021 0ustar www-datawww-data$M(!x uUO&!PT/`@ @K'`@ @K 2 (LX3LTN154P2-L05 ./plugins/color/test-data/LG-L225W-External.bin0000644000004100000410000000020012735467744021270 0ustar www-datawww-datamcV^ /x%UI'PTk@qO|.`@0 6(!90b'@h6(8KS L225W t./plugins/color/Makefile.am0000644000004100000410000000320612735467744016122 0ustar www-datawww-dataplugin_name = color plugin_LTLIBRARIES = \ libcolor.la libcolor_la_SOURCES = \ gcm-profile-store.c \ gcm-profile-store.h \ gcm-dmi.c \ gcm-dmi.h \ gcm-edid.c \ gcm-edid.h \ gsd-color-manager.c \ gsd-color-manager.h \ gsd-color-plugin.c libcolor_la_CPPFLAGS = \ -I$(top_srcdir)/gnome-settings-daemon \ -I$(top_srcdir)/plugins/common \ -DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" \ -DBINDIR=\"$(bindir)\" \ $(AM_CPPFLAGS) libcolor_la_CFLAGS = \ $(PLUGIN_CFLAGS) \ $(COLOR_CFLAGS) \ $(LCMS_CFLAGS) \ $(SETTINGS_PLUGIN_CFLAGS) \ $(LIBNOTIFY_CFLAGS) \ $(AM_CFLAGS) libcolor_la_LDFLAGS = \ $(GSD_PLUGIN_LDFLAGS) libcolor_la_LIBADD = \ $(top_builddir)/gnome-settings-daemon/libgsd.la \ $(COLOR_LIBS) \ $(LCMS_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ $(LIBNOTIFY_LIBS) check_PROGRAMS = \ gcm-self-test gcm_self_test_CPPFLAGS = \ -DTESTDATADIR=\""$(top_srcdir)/plugins/color/test-data"\" \ $(AM_CPPFLAGS) gcm_self_test_CFLAGS = \ $(SETTINGS_PLUGIN_CFLAGS) \ $(COLOR_CFLAGS) \ $(PLUGIN_CFLAGS) \ $(AM_CFLAGS) gcm_self_test_SOURCES = \ gcm-dmi.c \ gcm-dmi.h \ gcm-edid.c \ gcm-edid.h \ gcm-self-test.c gcm_self_test_LDADD = \ $(COLOR_LIBS) \ $(LCMS_LIBS) \ $(SETTINGS_PLUGIN_LIBS) \ -lm TESTS = gcm-self-test plugin_in_files = \ color.gnome-settings-plugin.in plugin_DATA = $(plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin) EXTRA_DIST = \ $(plugin_in_files) \ test-data/Lenovo-T61-Internal.bin \ test-data/LG-L225W-External.bin CLEANFILES = \ $(plugin_DATA) DISTCLEANFILES = \ $(plugin_DATA) @GSD_INTLTOOL_PLUGIN_RULE@ ./plugins/color/gsd-color-plugin.c0000644000004100000410000000211012735467744017410 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2011 Richard Hughes * * 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. * */ #include "config.h" #include #include #include "gnome-settings-plugin.h" #include "gsd-color-manager.h" GNOME_SETTINGS_PLUGIN_REGISTER (GsdColor, gsd_color) ./plugins/color/gsd-color-manager.h0000644000004100000410000000476512735467744017553 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2011 Richard Hughes * * 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. * */ #ifndef __GSD_COLOR_MANAGER_H #define __GSD_COLOR_MANAGER_H #include G_BEGIN_DECLS #define GSD_TYPE_COLOR_MANAGER (gsd_color_manager_get_type ()) #define GSD_COLOR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_COLOR_MANAGER, GsdColorManager)) #define GSD_COLOR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_COLOR_MANAGER, GsdColorManagerClass)) #define GSD_IS_COLOR_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_COLOR_MANAGER)) #define GSD_IS_COLOR_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_COLOR_MANAGER)) #define GSD_COLOR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_COLOR_MANAGER, GsdColorManagerClass)) #define GSD_COLOR_MANAGER_ERROR (gsd_color_manager_error_quark ()) typedef struct GsdColorManagerPrivate GsdColorManagerPrivate; typedef struct { GObject parent; GsdColorManagerPrivate *priv; } GsdColorManager; typedef struct { GObjectClass parent_class; } GsdColorManagerClass; enum { GSD_COLOR_MANAGER_ERROR_FAILED }; GType gsd_color_manager_get_type (void); GQuark gsd_color_manager_error_quark (void); GsdColorManager * gsd_color_manager_new (void); gboolean gsd_color_manager_start (GsdColorManager *manager, GError **error); void gsd_color_manager_stop (GsdColorManager *manager); G_END_DECLS #endif /* __GSD_COLOR_MANAGER_H */ ./plugins/color/color.gnome-settings-plugin.in0000644000004100000410000000027712735467744021777 0ustar www-datawww-data[GNOME Settings Plugin] Module=color IAge=0 Priority=10 _Name=Color _Description=Color plugin Authors=Richard Hughes Copyright=Copyright © 2011 Richard Hughes Website= ./plugins/color/gcm-profile-store.c0000644000004100000410000004570712735467744017604 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2009-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include "gcm-profile-store.h" static void gcm_profile_store_finalize (GObject *object); #define GCM_PROFILE_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_PROFILE_STORE, GcmProfileStorePrivate)) struct _GcmProfileStorePrivate { GPtrArray *filename_array; GPtrArray *directory_array; GCancellable *cancellable; }; enum { SIGNAL_ADDED, SIGNAL_REMOVED, SIGNAL_CHANGED, SIGNAL_LAST }; static guint signals[SIGNAL_LAST] = { 0 }; G_DEFINE_TYPE (GcmProfileStore, gcm_profile_store, G_TYPE_OBJECT) static void gcm_profile_store_search_path (GcmProfileStore *profile_store, const gchar *path, guint depth); static void gcm_profile_store_process_child (GcmProfileStore *profile_store, const gchar *path, GFileInfo *info); #define GCM_PROFILE_STORE_MAX_RECURSION_LEVELS 2 typedef struct { gchar *path; GFileMonitor *monitor; guint depth; } GcmProfileStoreDirHelper; static void gcm_profile_store_helper_free (GcmProfileStoreDirHelper *helper) { g_free (helper->path); if (helper->monitor != NULL) g_object_unref (helper->monitor); g_free (helper); } static const gchar * gcm_profile_store_find_filename (GcmProfileStore *profile_store, const gchar *filename) { const gchar *tmp; guint i; GPtrArray *array = profile_store->priv->filename_array; for (i=0; ilen; i++) { tmp = g_ptr_array_index (array, i); if (g_strcmp0 (filename, tmp) == 0) return tmp; } return NULL; } static GcmProfileStoreDirHelper * gcm_profile_store_find_directory (GcmProfileStore *profile_store, const gchar *path) { GcmProfileStoreDirHelper *tmp; guint i; GPtrArray *array = profile_store->priv->directory_array; for (i=0; ilen; i++) { tmp = g_ptr_array_index (array, i); if (g_strcmp0 (path, tmp->path) == 0) return tmp; } return NULL; } static gboolean gcm_profile_store_remove_profile (GcmProfileStore *profile_store, const gchar *filename) { gboolean ret = FALSE; const gchar *tmp; gchar *filename_dup = NULL; GcmProfileStorePrivate *priv = profile_store->priv; /* find exact pointer */ tmp = gcm_profile_store_find_filename (profile_store, filename); if (tmp == NULL) goto out; /* dup so we can emit the signal */ filename_dup = g_strdup (tmp); ret = g_ptr_array_remove (priv->filename_array, (gpointer)tmp); if (!ret) { g_warning ("failed to remove %s", filename); goto out; } /* emit a signal */ g_debug ("emit removed: %s", filename_dup); g_signal_emit (profile_store, signals[SIGNAL_REMOVED], 0, filename_dup); out: g_free (filename_dup); return ret; } static void gcm_profile_store_add_profile (GcmProfileStore *profile_store, const gchar *filename) { GcmProfileStorePrivate *priv = profile_store->priv; /* add to list */ g_ptr_array_add (priv->filename_array, g_strdup (filename)); /* emit a signal */ g_debug ("emit add: %s", filename); g_signal_emit (profile_store, signals[SIGNAL_ADDED], 0, filename); } static void gcm_profile_store_created_query_info_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GFileInfo *info; GError *error = NULL; gchar *path; GFile *file = G_FILE (source_object); GFile *parent; GcmProfileStore *profile_store = GCM_PROFILE_STORE (user_data); info = g_file_query_info_finish (file, res, &error); if (info == NULL) { g_warning ("failed to get info about deleted file: %s", error->message); g_error_free (error); return; } parent = g_file_get_parent (file); path = g_file_get_path (parent); gcm_profile_store_process_child (profile_store, path, info); g_free (path); g_object_unref (info); g_object_unref (parent); } static void gcm_profile_store_remove_from_prefix (GcmProfileStore *profile_store, const gchar *prefix) { guint i; const gchar *path; GcmProfileStorePrivate *priv = profile_store->priv; for (i = 0; i < priv->filename_array->len; i++) { path = g_ptr_array_index (priv->filename_array, i); if (g_str_has_prefix (path, prefix)) { g_debug ("auto-removed %s as path removed", path); gcm_profile_store_remove_profile (profile_store, path); } } } static void gcm_profile_store_file_monitor_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, GcmProfileStore *profile_store) { gchar *path = NULL; gchar *parent_path = NULL; const gchar *tmp; GcmProfileStoreDirHelper *helper; /* profile was deleted */ if (event_type == G_FILE_MONITOR_EVENT_DELETED) { /* we can either have two things here, a directory or a * file. We can't call g_file_query_info_async() as the * inode doesn't exist anymore */ path = g_file_get_path (file); tmp = gcm_profile_store_find_filename (profile_store, path); if (tmp != NULL) { /* is a file */ gcm_profile_store_remove_profile (profile_store, path); goto out; } /* is a directory, urgh. Remove all profiles there. */ gcm_profile_store_remove_from_prefix (profile_store, path); helper = gcm_profile_store_find_directory (profile_store, path); if (helper != NULL) { g_ptr_array_remove (profile_store->priv->directory_array, helper); } goto out; } /* ignore temp files */ path = g_file_get_path (file); if (g_strrstr (path, ".goutputstream") != NULL) { g_debug ("ignoring gvfs temporary file"); goto out; } /* only care about created objects */ if (event_type == G_FILE_MONITOR_EVENT_CREATED) { g_file_query_info_async (file, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, G_PRIORITY_LOW, NULL, gcm_profile_store_created_query_info_cb, profile_store); goto out; } out: g_free (path); g_free (parent_path); } static void gcm_profile_store_process_child (GcmProfileStore *profile_store, const gchar *path, GFileInfo *info) { gchar *full_path = NULL; const gchar *name; GcmProfileStoreDirHelper *helper; /* check we're not in a loop */ helper = gcm_profile_store_find_directory (profile_store, path); if (helper == NULL) goto out; if (helper->depth > GCM_PROFILE_STORE_MAX_RECURSION_LEVELS) { g_warning ("recursing more than %i levels deep is insane", GCM_PROFILE_STORE_MAX_RECURSION_LEVELS); goto out; } /* make the compete path */ name = g_file_info_get_name (info); full_path = g_build_filename (path, name, NULL); /* if a directory */ if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { gcm_profile_store_search_path (profile_store, full_path, helper->depth + 1); goto out; } /* ignore temp files */ if (g_strrstr (full_path, ".goutputstream") != NULL) { g_debug ("ignoring gvfs temporary file"); goto out; } /* is a file */ gcm_profile_store_add_profile (profile_store, full_path); out: g_free (full_path); } static void gcm_profile_store_next_files_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GList *files; GList *f; GError *error = NULL; GFileInfo *info; GFile *file; gchar *path; GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object); GcmProfileStore *profile_store = GCM_PROFILE_STORE (user_data); files = g_file_enumerator_next_files_finish (enumerator, res, &error); if (files == NULL) { /* special value, meaning "no more files to process" */ return; } if (error != NULL) { g_warning ("failed to get data about enumerated directory: %s", error->message); g_error_free (error); return; } /* get each file */ file = g_file_enumerator_get_container (enumerator); path = g_file_get_path (file); for (f = files; f != NULL; f = f->next) { info = G_FILE_INFO (f->data); gcm_profile_store_process_child (profile_store, path, info); } /* continue to get the rest of the data in chunks */ g_file_enumerator_next_files_async (enumerator, 5, G_PRIORITY_LOW, profile_store->priv->cancellable, gcm_profile_store_next_files_cb, user_data); g_free (path); g_list_foreach (files, (GFunc) g_object_unref, NULL); g_list_free (files); } static void gcm_profile_store_enumerate_children_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; GFileEnumerator *enumerator; GcmProfileStore *profile_store = GCM_PROFILE_STORE (user_data); enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, &error); if (enumerator == NULL) { GcmProfileStoreDirHelper *helper; gchar *path = NULL; path = g_file_get_path (G_FILE (source_object)); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) g_debug ("failed to enumerate directory %s: %s", path, error->message); else g_warning ("failed to enumerate directory %s: %s", path, error->message); helper = gcm_profile_store_find_directory (profile_store, path); if (helper) g_ptr_array_remove (profile_store->priv->directory_array, helper); g_error_free (error); g_free (path); return; } /* get the first chunk of data */ g_file_enumerator_next_files_async (enumerator, 5, G_PRIORITY_LOW, profile_store->priv->cancellable, gcm_profile_store_next_files_cb, user_data); g_object_unref (enumerator); } static void gcm_profile_store_search_path (GcmProfileStore *profile_store, const gchar *path, guint depth) { GFile *file = NULL; GError *error = NULL; GcmProfileStoreDirHelper *helper; file = g_file_new_for_path (path); /* add an inotify watch if not already added */ helper = gcm_profile_store_find_directory (profile_store, path); if (helper == NULL) { helper = g_new0 (GcmProfileStoreDirHelper, 1); helper->depth = depth; helper->path = g_strdup (path); helper->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error); if (helper->monitor == NULL) { g_debug ("failed to monitor path: %s", error->message); g_error_free (error); gcm_profile_store_helper_free (helper); goto out; } g_signal_connect (helper->monitor, "changed", G_CALLBACK(gcm_profile_store_file_monitor_changed_cb), profile_store); g_ptr_array_add (profile_store->priv->directory_array, helper); } /* get contents of directory */ g_file_enumerate_children_async (file, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, G_PRIORITY_LOW, profile_store->priv->cancellable, gcm_profile_store_enumerate_children_cb, profile_store); out: g_object_unref (file); } static gboolean gcm_profile_store_mkdir_with_parents (const gchar *filename, GCancellable *cancellable, GError **error) { gboolean ret; GFile *file; /* ensure destination exists */ file = g_file_new_for_path (filename); ret = g_file_make_directory_with_parents (file, cancellable, error); g_object_unref (file); return ret; } gboolean gcm_profile_store_search (GcmProfileStore *profile_store) { gchar *path; gboolean ret; GError *error = NULL; /* get Linux per-user profiles */ path = g_build_filename (g_get_user_data_dir (), "icc", NULL); ret = gcm_profile_store_mkdir_with_parents (path, profile_store->priv->cancellable, &error); if (!ret && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { g_warning ("failed to create directory on startup: %s", error->message); } else { gcm_profile_store_search_path (profile_store, path, 0); } g_free (path); g_clear_error (&error); /* get per-user profiles from obsolete location */ path = g_build_filename (g_get_home_dir (), ".color", "icc", NULL); gcm_profile_store_search_path (profile_store, path, 0); g_free (path); return TRUE; } static void gcm_profile_store_class_init (GcmProfileStoreClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gcm_profile_store_finalize; signals[SIGNAL_ADDED] = g_signal_new ("added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcmProfileStoreClass, added), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); signals[SIGNAL_REMOVED] = g_signal_new ("removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcmProfileStoreClass, removed), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); g_type_class_add_private (klass, sizeof (GcmProfileStorePrivate)); } static void gcm_profile_store_init (GcmProfileStore *profile_store) { profile_store->priv = GCM_PROFILE_STORE_GET_PRIVATE (profile_store); profile_store->priv->cancellable = g_cancellable_new (); profile_store->priv->filename_array = g_ptr_array_new_with_free_func (g_free); profile_store->priv->directory_array = g_ptr_array_new_with_free_func ((GDestroyNotify) gcm_profile_store_helper_free); } static void gcm_profile_store_finalize (GObject *object) { GcmProfileStore *profile_store = GCM_PROFILE_STORE (object); GcmProfileStorePrivate *priv = profile_store->priv; g_cancellable_cancel (profile_store->priv->cancellable); g_object_unref (profile_store->priv->cancellable); g_ptr_array_unref (priv->filename_array); g_ptr_array_unref (priv->directory_array); G_OBJECT_CLASS (gcm_profile_store_parent_class)->finalize (object); } GcmProfileStore * gcm_profile_store_new (void) { GcmProfileStore *profile_store; profile_store = g_object_new (GCM_TYPE_PROFILE_STORE, NULL); return GCM_PROFILE_STORE (profile_store); } ./plugins/color/gcm-profile-store.h0000644000004100000410000000467312735467744017606 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2009-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __GCM_PROFILE_STORE_H #define __GCM_PROFILE_STORE_H #include G_BEGIN_DECLS #define GCM_TYPE_PROFILE_STORE (gcm_profile_store_get_type ()) #define GCM_PROFILE_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_PROFILE_STORE, GcmProfileStore)) #define GCM_PROFILE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GCM_TYPE_PROFILE_STORE, GcmProfileStoreClass)) #define GCM_IS_PROFILE_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_PROFILE_STORE)) #define GCM_IS_PROFILE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GCM_TYPE_PROFILE_STORE)) #define GCM_PROFILE_STORE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GCM_TYPE_PROFILE_STORE, GcmProfileStoreClass)) typedef struct _GcmProfileStorePrivate GcmProfileStorePrivate; typedef struct _GcmProfileStore GcmProfileStore; typedef struct _GcmProfileStoreClass GcmProfileStoreClass; struct _GcmProfileStore { GObject parent; GcmProfileStorePrivate *priv; }; struct _GcmProfileStoreClass { GObjectClass parent_class; void (* added) (const gchar *filename); void (* removed) (const gchar *filename); }; GType gcm_profile_store_get_type (void); GcmProfileStore *gcm_profile_store_new (void); gboolean gcm_profile_store_search (GcmProfileStore *profile_store); G_END_DECLS #endif /* __GCM_PROFILE_STORE_H */ ./plugins/color/gcm-dmi.c0000644000004100000410000001206312735467744015550 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2009-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include "gcm-dmi.h" static void gcm_dmi_finalize (GObject *object); #define GCM_DMI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_DMI, GcmDmiPrivate)) struct _GcmDmiPrivate { gchar *name; gchar *version; gchar *vendor; }; static gpointer gcm_dmi_object = NULL; G_DEFINE_TYPE (GcmDmi, gcm_dmi, G_TYPE_OBJECT) static gchar * gcm_dmi_get_from_filename (const gchar *filename) { gboolean ret; GError *error = NULL; gchar *data = NULL; /* get the contents */ ret = g_file_get_contents (filename, &data, NULL, &error); if (!ret) { if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) g_warning ("failed to get contents of %s: %s", filename, error->message); g_error_free (error); } /* process the random chars and trailing spaces */ if (data != NULL) { g_strdelimit (data, "\t_", ' '); g_strdelimit (data, "\n\r", '\0'); g_strchomp (data); } /* don't return an empty string */ if (data != NULL && data[0] == '\0') { g_free (data); data = NULL; } return data; } static gchar * gcm_dmi_get_from_filenames (const gchar * const * filenames) { guint i; gchar *tmp = NULL; /* try each one in preference order */ for (i = 0; filenames[i] != NULL; i++) { tmp = gcm_dmi_get_from_filename (filenames[i]); if (tmp != NULL) break; } return tmp; } const gchar * gcm_dmi_get_name (GcmDmi *dmi) { g_return_val_if_fail (GCM_IS_DMI (dmi), NULL); return dmi->priv->name; } const gchar * gcm_dmi_get_version (GcmDmi *dmi) { g_return_val_if_fail (GCM_IS_DMI (dmi), NULL); return dmi->priv->version; } const gchar * gcm_dmi_get_vendor (GcmDmi *dmi) { g_return_val_if_fail (GCM_IS_DMI (dmi), NULL); return dmi->priv->vendor; } static void gcm_dmi_class_init (GcmDmiClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gcm_dmi_finalize; g_type_class_add_private (klass, sizeof (GcmDmiPrivate)); } static void gcm_dmi_init (GcmDmi *dmi) { #if defined(__linux__) const gchar *sysfs_name[] = { "/sys/class/dmi/id/product_name", "/sys/class/dmi/id/board_name", NULL}; const gchar *sysfs_version[] = { "/sys/class/dmi/id/product_version", "/sys/class/dmi/id/chassis_version", "/sys/class/dmi/id/board_version", NULL}; const gchar *sysfs_vendor[] = { "/sys/class/dmi/id/sys_vendor", "/sys/class/dmi/id/chassis_vendor", "/sys/class/dmi/id/board_vendor", NULL}; #else #warning Please add dmi support for your OS const gchar *sysfs_name[] = { NULL }; const gchar *sysfs_version[] = { NULL }; const gchar *sysfs_vendor[] = { NULL }; #endif dmi->priv = GCM_DMI_GET_PRIVATE (dmi); /* get all the possible data now */ dmi->priv->name = gcm_dmi_get_from_filenames (sysfs_name); dmi->priv->version = gcm_dmi_get_from_filenames (sysfs_version); dmi->priv->vendor = gcm_dmi_get_from_filenames (sysfs_vendor); } static void gcm_dmi_finalize (GObject *object) { GcmDmi *dmi = GCM_DMI (object); g_free (dmi->priv->name); g_free (dmi->priv->version); g_free (dmi->priv->vendor); G_OBJECT_CLASS (gcm_dmi_parent_class)->finalize (object); } GcmDmi * gcm_dmi_new (void) { if (gcm_dmi_object != NULL) { g_object_ref (gcm_dmi_object); } else { gcm_dmi_object = g_object_new (GCM_TYPE_DMI, NULL); g_object_add_weak_pointer (gcm_dmi_object, &gcm_dmi_object); } return GCM_DMI (gcm_dmi_object); } ./plugins/color/gsd-color-manager.c0000644000004100000410000024011712735467763017540 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * Copyright (C) 2011 Richard Hughes * * 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 "config.h" #include #include #include #include #include #include #include #include "gnome-settings-plugin.h" #include "gnome-settings-profile.h" #include "gnome-settings-bus.h" #include "gsd-color-manager.h" #include "gcm-profile-store.h" #include "gcm-dmi.h" #include "gcm-edid.h" #include "gsd-rr.h" #define GSD_COLOR_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_COLOR_MANAGER, GsdColorManagerPrivate)) #define GCM_SESSION_NOTIFY_TIMEOUT 30000 /* ms */ #define GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD "recalibrate-printer-threshold" #define GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD "recalibrate-display-threshold" struct GsdColorManagerPrivate { GsdSessionManager *session; CdClient *client; GSettings *settings; GcmProfileStore *profile_store; GcmDmi *dmi; GsdRRScreen *x11_screen; GHashTable *edid_cache; GdkWindow *gdk_window; gboolean session_is_active; GHashTable *device_assign_hash; }; enum { PROP_0, }; static void gsd_color_manager_class_init (GsdColorManagerClass *klass); static void gsd_color_manager_init (GsdColorManager *color_manager); static void gsd_color_manager_finalize (GObject *object); G_DEFINE_TYPE (GsdColorManager, gsd_color_manager, G_TYPE_OBJECT) static gpointer manager_object = NULL; /* see http://www.oyranos.org/wiki/index.php?title=ICC_Profiles_in_X_Specification_0.3 */ #define GCM_ICC_PROFILE_IN_X_VERSION_MAJOR 0 #define GCM_ICC_PROFILE_IN_X_VERSION_MINOR 3 typedef struct { guint32 red; guint32 green; guint32 blue; } GsdRROutputClutItem; GQuark gsd_color_manager_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("gsd_color_manager_error"); return quark; } static GcmEdid * gcm_session_get_output_edid (GsdColorManager *manager, GsdRROutput *output, GError **error) { const guint8 *data; gsize size; GcmEdid *edid = NULL; gboolean ret; /* can we find it in the cache */ edid = g_hash_table_lookup (manager->priv->edid_cache, gsd_rr_output_get_name (output)); if (edid != NULL) { g_object_ref (edid); goto out; } /* parse edid */ data = gsd_rr_output_get_edid_data (output, &size); if (data == NULL || size == 0) { g_set_error_literal (error, GSD_RR_ERROR, GSD_RR_ERROR_UNKNOWN, "unable to get EDID for output"); goto out; } edid = gcm_edid_new (); ret = gcm_edid_parse (edid, data, size, error); if (!ret) { g_object_unref (edid); edid = NULL; goto out; } /* add to cache */ g_hash_table_insert (manager->priv->edid_cache, g_strdup (gsd_rr_output_get_name (output)), g_object_ref (edid)); out: return edid; } static gboolean gcm_session_screen_set_icc_profile (GsdColorManager *manager, const gchar *filename, GError **error) { gboolean ret; gchar *data = NULL; gsize length; guint version_data; GsdColorManagerPrivate *priv = manager->priv; g_return_val_if_fail (filename != NULL, FALSE); g_debug ("setting root window ICC profile atom from %s", filename); /* get contents of file */ ret = g_file_get_contents (filename, &data, &length, error); if (!ret) goto out; /* set profile property */ gdk_property_change (priv->gdk_window, gdk_atom_intern_static_string ("_ICC_PROFILE"), gdk_atom_intern_static_string ("CARDINAL"), 8, GDK_PROP_MODE_REPLACE, (const guchar *) data, length); /* set version property */ version_data = GCM_ICC_PROFILE_IN_X_VERSION_MAJOR * 100 + GCM_ICC_PROFILE_IN_X_VERSION_MINOR * 1; gdk_property_change (priv->gdk_window, gdk_atom_intern_static_string ("_ICC_PROFILE_IN_X_VERSION"), gdk_atom_intern_static_string ("CARDINAL"), 8, GDK_PROP_MODE_REPLACE, (const guchar *) &version_data, 1); out: g_free (data); return ret; } static gchar * gcm_session_get_output_id (GsdColorManager *manager, GsdRROutput *output) { const gchar *name; const gchar *serial; const gchar *vendor; GcmEdid *edid = NULL; GString *device_id; GError *error = NULL; /* all output devices are prefixed with this */ device_id = g_string_new ("xrandr"); /* get the output EDID if possible */ edid = gcm_session_get_output_edid (manager, output, &error); if (edid == NULL) { g_debug ("no edid for %s [%s], falling back to connection name", gsd_rr_output_get_name (output), error->message); g_error_free (error); g_string_append_printf (device_id, "-%s", gsd_rr_output_get_name (output)); goto out; } /* check EDID data is okay to use */ vendor = gcm_edid_get_vendor_name (edid); name = gcm_edid_get_monitor_name (edid); serial = gcm_edid_get_serial_number (edid); if (vendor == NULL && name == NULL && serial == NULL) { g_debug ("edid invalid for %s, falling back to connection name", gsd_rr_output_get_name (output)); g_string_append_printf (device_id, "-%s", gsd_rr_output_get_name (output)); goto out; } /* use EDID data */ if (vendor != NULL) g_string_append_printf (device_id, "-%s", vendor); if (name != NULL) g_string_append_printf (device_id, "-%s", name); if (serial != NULL) g_string_append_printf (device_id, "-%s", serial); out: if (edid != NULL) g_object_unref (edid); return g_string_free (device_id, FALSE); } typedef struct { GsdColorManager *manager; CdProfile *profile; CdDevice *device; guint32 output_id; } GcmSessionAsyncHelper; static void gcm_session_async_helper_free (GcmSessionAsyncHelper *helper) { if (helper->manager != NULL) g_object_unref (helper->manager); if (helper->profile != NULL) g_object_unref (helper->profile); if (helper->device != NULL) g_object_unref (helper->device); g_free (helper); } static cmsBool _cmsWriteTagTextAscii (cmsHPROFILE lcms_profile, cmsTagSignature sig, const gchar *text) { cmsBool ret; cmsMLU *mlu = cmsMLUalloc (0, 1); cmsMLUsetASCII (mlu, "en", "US", text); ret = cmsWriteTag (lcms_profile, sig, mlu); cmsMLUfree (mlu); return ret; } static gboolean gcm_utils_mkdir_for_filename (const gchar *filename, GError **error) { gboolean ret = FALSE; GFile *file; GFile *parent_dir = NULL; /* get parent directory */ file = g_file_new_for_path (filename); parent_dir = g_file_get_parent (file); if (parent_dir == NULL) { g_set_error (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "could not get parent dir %s", filename); goto out; } /* ensure desination does not already exist */ ret = g_file_query_exists (parent_dir, NULL); if (ret) goto out; ret = g_file_make_directory_with_parents (parent_dir, NULL, error); if (!ret) goto out; out: if (file != NULL) g_object_unref (file); if (parent_dir != NULL) g_object_unref (parent_dir); return ret; } #ifdef HAVE_NEW_LCMS static wchar_t * utf8_to_wchar_t (const char *src) { size_t len; size_t converted; wchar_t *buf = NULL; len = mbstowcs (NULL, src, 0); if (len == (size_t) -1) { g_warning ("Invalid UTF-8 in string %s", src); goto out; } len += 1; buf = g_malloc (sizeof (wchar_t) * len); converted = mbstowcs (buf, src, len - 1); g_assert (converted != -1); buf[converted] = '\0'; out: return buf; } static cmsBool _cmsDictAddEntryAscii (cmsHANDLE dict, const gchar *key, const gchar *value) { cmsBool ret = FALSE; wchar_t *mb_key = NULL; wchar_t *mb_value = NULL; mb_key = utf8_to_wchar_t (key); if (mb_key == NULL) goto out; mb_value = utf8_to_wchar_t (value); if (mb_value == NULL) goto out; ret = cmsDictAddEntry (dict, mb_key, mb_value, NULL, NULL); out: g_free (mb_key); g_free (mb_value); return ret; } #endif /* HAVE_NEW_LCMS */ static gboolean gcm_apply_create_icc_profile_for_edid (GsdColorManager *manager, CdDevice *device, GcmEdid *edid, const gchar *filename, GError **error) { const CdColorYxy *tmp; cmsCIExyYTRIPLE chroma; cmsCIExyY white_point; cmsHPROFILE lcms_profile = NULL; cmsToneCurve *transfer_curve[3] = { NULL, NULL, NULL }; const gchar *data; gboolean ret = FALSE; gchar *str; gfloat edid_gamma; gfloat localgamma; #ifdef HAVE_NEW_LCMS cmsHANDLE dict = NULL; #endif GsdColorManagerPrivate *priv = manager->priv; /* ensure the per-user directory exists */ ret = gcm_utils_mkdir_for_filename (filename, error); if (!ret) goto out; /* copy color data from our structures */ tmp = gcm_edid_get_red (edid); chroma.Red.x = tmp->x; chroma.Red.y = tmp->y; tmp = gcm_edid_get_green (edid); chroma.Green.x = tmp->x; chroma.Green.y = tmp->y; tmp = gcm_edid_get_blue (edid); chroma.Blue.x = tmp->x; chroma.Blue.y = tmp->y; tmp = gcm_edid_get_white (edid); white_point.x = tmp->x; white_point.y = tmp->y; white_point.Y = 1.0; /* estimate the transfer function for the gamma */ localgamma = gcm_edid_get_gamma (edid); transfer_curve[0] = transfer_curve[1] = transfer_curve[2] = cmsBuildGamma (NULL, localgamma); /* create our generated profile */ lcms_profile = cmsCreateRGBProfile (&white_point, &chroma, transfer_curve); if (lcms_profile == NULL) { g_set_error (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to create profile"); goto out; } cmsSetColorSpace (lcms_profile, cmsSigRgbData); cmsSetPCS (lcms_profile, cmsSigXYZData); cmsSetHeaderRenderingIntent (lcms_profile, INTENT_PERCEPTUAL); cmsSetDeviceClass (lcms_profile, cmsSigDisplayClass); /* copyright */ ret = _cmsWriteTagTextAscii (lcms_profile, cmsSigCopyrightTag, /* deliberately not translated */ "This profile is free of known copyright restrictions."); if (!ret) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to write copyright"); goto out; } /* set model */ data = gcm_edid_get_monitor_name (edid); if (data == NULL) data = gcm_dmi_get_name (priv->dmi); if (data == NULL) data = "Unknown monitor"; ret = _cmsWriteTagTextAscii (lcms_profile, cmsSigDeviceModelDescTag, data); if (!ret) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to write model"); goto out; } /* write title */ ret = _cmsWriteTagTextAscii (lcms_profile, cmsSigProfileDescriptionTag, data); if (!ret) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to write description"); goto out; } /* get manufacturer */ data = gcm_edid_get_vendor_name (edid); if (data == NULL) data = gcm_dmi_get_vendor (priv->dmi); if (data == NULL) data = "Unknown vendor"; ret = _cmsWriteTagTextAscii (lcms_profile, cmsSigDeviceMfgDescTag, data); if (!ret) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to write manufacturer"); goto out; } #ifdef HAVE_NEW_LCMS /* just create a new dict */ dict = cmsDictAlloc (NULL); /* set the framework creator metadata */ _cmsDictAddEntryAscii (dict, CD_PROFILE_METADATA_CMF_PRODUCT, PACKAGE_NAME); _cmsDictAddEntryAscii (dict, CD_PROFILE_METADATA_CMF_BINARY, PACKAGE_NAME); _cmsDictAddEntryAscii (dict, CD_PROFILE_METADATA_CMF_VERSION, PACKAGE_VERSION); _cmsDictAddEntryAscii (dict, CD_PROFILE_METADATA_MAPPING_DEVICE_ID, cd_device_get_id (device)); /* set the data source so we don't ever prompt the user to * recalibrate (as the EDID data won't have changed) */ _cmsDictAddEntryAscii (dict, CD_PROFILE_METADATA_DATA_SOURCE, CD_PROFILE_METADATA_DATA_SOURCE_EDID); /* set 'ICC meta Tag for Monitor Profiles' data */ _cmsDictAddEntryAscii (dict, "EDID_md5", gcm_edid_get_checksum (edid)); data = gcm_edid_get_monitor_name (edid); if (data != NULL) _cmsDictAddEntryAscii (dict, "EDID_model", data); data = gcm_edid_get_serial_number (edid); if (data != NULL) _cmsDictAddEntryAscii (dict, "EDID_serial", data); data = gcm_edid_get_pnp_id (edid); if (data != NULL) _cmsDictAddEntryAscii (dict, "EDID_mnft", data); data = gcm_edid_get_vendor_name (edid); if (data != NULL) _cmsDictAddEntryAscii (dict, "EDID_manufacturer", data); edid_gamma = gcm_edid_get_gamma (edid); if (edid_gamma > 0.0 && edid_gamma < 10.0) { str = g_strdup_printf ("%f", edid_gamma); _cmsDictAddEntryAscii (dict, "EDID_gamma", str); g_free (str); } /* also add the primaries */ str = g_strdup_printf ("%f", chroma.Red.x); _cmsDictAddEntryAscii (dict, "EDID_red_x", str); g_free (str); str = g_strdup_printf ("%f", chroma.Red.y); _cmsDictAddEntryAscii (dict, "EDID_red_y", str); g_free (str); str = g_strdup_printf ("%f", chroma.Green.x); _cmsDictAddEntryAscii (dict, "EDID_green_x", str); g_free (str); str = g_strdup_printf ("%f", chroma.Green.y); _cmsDictAddEntryAscii (dict, "EDID_green_y", str); g_free (str); str = g_strdup_printf ("%f", chroma.Blue.x); _cmsDictAddEntryAscii (dict, "EDID_blue_x", str); g_free (str); str = g_strdup_printf ("%f", chroma.Blue.y); _cmsDictAddEntryAscii (dict, "EDID_blue_y", str); g_free (str); /* write new tag */ ret = cmsWriteTag (lcms_profile, cmsSigMetaTag, dict); if (!ret) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to write profile metadata"); goto out; } #endif /* HAVE_NEW_LCMS */ /* write profile id */ ret = cmsMD5computeID (lcms_profile); if (!ret) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to write profile id"); goto out; } /* save, TODO: get error */ cmsSaveProfileToFile (lcms_profile, filename); ret = TRUE; out: #ifdef HAVE_NEW_LCMS if (dict != NULL) cmsDictFree (dict); #endif if (*transfer_curve != NULL) cmsFreeToneCurve (*transfer_curve); return ret; } static GPtrArray * gcm_session_generate_vcgt (CdProfile *profile, guint size) { GsdRROutputClutItem *tmp; GPtrArray *array = NULL; const cmsToneCurve **vcgt; cmsFloat32Number in; guint i; const gchar *filename; cmsHPROFILE lcms_profile = NULL; /* invalid size */ if (size == 0) goto out; /* not an actual profile */ filename = cd_profile_get_filename (profile); if (filename == NULL) goto out; /* open file */ lcms_profile = cmsOpenProfileFromFile (filename, "r"); if (lcms_profile == NULL) goto out; /* get tone curves from profile */ vcgt = cmsReadTag (lcms_profile, cmsSigVcgtTag); if (vcgt == NULL || vcgt[0] == NULL) { g_debug ("profile does not have any VCGT data"); goto out; } /* create array */ array = g_ptr_array_new_with_free_func (g_free); for (i = 0; i < size; i++) { in = (gdouble) i / (gdouble) (size - 1); tmp = g_new0 (GsdRROutputClutItem, 1); tmp->red = cmsEvalToneCurveFloat(vcgt[0], in) * (gdouble) 0xffff; tmp->green = cmsEvalToneCurveFloat(vcgt[1], in) * (gdouble) 0xffff; tmp->blue = cmsEvalToneCurveFloat(vcgt[2], in) * (gdouble) 0xffff; g_ptr_array_add (array, tmp); } out: if (lcms_profile != NULL) cmsCloseProfile (lcms_profile); return array; } static guint gsd_rr_output_get_gamma_size (GsdRROutput *output) { GsdRRCrtc *crtc; gint len = 0; crtc = gsd_rr_output_get_crtc (output); if (crtc == NULL) return 0; gsd_rr_crtc_get_gamma (crtc, &len, NULL, NULL, NULL); return (guint) len; } static gboolean gcm_session_output_set_gamma (GsdRROutput *output, GPtrArray *array, GError **error) { gboolean ret = TRUE; guint16 *red = NULL; guint16 *green = NULL; guint16 *blue = NULL; guint i; GsdRROutputClutItem *data; GsdRRCrtc *crtc; /* no length? */ if (array->len == 0) { ret = FALSE; g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "no data in the CLUT array"); goto out; } /* convert to a type X understands */ red = g_new (guint16, array->len); green = g_new (guint16, array->len); blue = g_new (guint16, array->len); for (i = 0; i < array->len; i++) { data = g_ptr_array_index (array, i); red[i] = data->red; green[i] = data->green; blue[i] = data->blue; } /* send to LUT */ crtc = gsd_rr_output_get_crtc (output); if (crtc == NULL) { ret = FALSE; g_set_error (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to get ctrc for %s", gsd_rr_output_get_name (output)); goto out; } gsd_rr_crtc_set_gamma (crtc, array->len, red, green, blue); out: g_free (red); g_free (green); g_free (blue); return ret; } static gboolean gcm_session_device_set_gamma (GsdRROutput *output, CdProfile *profile, GError **error) { gboolean ret = FALSE; guint size; GPtrArray *clut = NULL; /* create a lookup table */ size = gsd_rr_output_get_gamma_size (output); if (size == 0) { ret = TRUE; goto out; } clut = gcm_session_generate_vcgt (profile, size); if (clut == NULL) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to generate vcgt"); goto out; } /* apply the vcgt to this output */ ret = gcm_session_output_set_gamma (output, clut, error); if (!ret) goto out; out: if (clut != NULL) g_ptr_array_unref (clut); return ret; } static gboolean gcm_session_device_reset_gamma (GsdRROutput *output, GError **error) { gboolean ret; guint i; guint size; guint32 value; GPtrArray *clut; GsdRROutputClutItem *data; /* create a linear ramp */ g_debug ("falling back to dummy ramp"); clut = g_ptr_array_new_with_free_func (g_free); size = gsd_rr_output_get_gamma_size (output); if (size == 0) { ret = TRUE; goto out; } for (i = 0; i < size; i++) { value = (i * 0xffff) / (size - 1); data = g_new0 (GsdRROutputClutItem, 1); data->red = value; data->green = value; data->blue = value; g_ptr_array_add (clut, data); } /* apply the vcgt to this output */ ret = gcm_session_output_set_gamma (output, clut, error); if (!ret) goto out; out: g_ptr_array_unref (clut); return ret; } static GsdRROutput * gcm_session_get_x11_output_by_id (GsdColorManager *manager, const gchar *device_id, GError **error) { gchar *output_id; GsdRROutput *output = NULL; GsdRROutput **outputs = NULL; guint i; GsdColorManagerPrivate *priv = manager->priv; /* search all X11 outputs for the device id */ outputs = gsd_rr_screen_list_outputs (priv->x11_screen); if (outputs == NULL) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "Failed to get outputs"); goto out; } for (i = 0; outputs[i] != NULL && output == NULL; i++) { if (!gsd_rr_output_is_connected (outputs[i])) continue; output_id = gcm_session_get_output_id (manager, outputs[i]); if (g_strcmp0 (output_id, device_id) == 0) output = outputs[i]; g_free (output_id); } if (output == NULL) { g_set_error (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "Failed to find output %s", device_id); } out: return output; } /* this function is more complicated than it should be, due to the * fact that XOrg sometimes assigns no primary devices when using * "xrandr --auto" or when the version of RANDR is < 1.3 */ static gboolean gcm_session_use_output_profile_for_screen (GsdColorManager *manager, GsdRROutput *output) { gboolean has_laptop = FALSE; gboolean has_primary = FALSE; GsdRROutput **outputs; GsdRROutput *connected = NULL; guint i; /* do we have any screens marked as primary */ outputs = gsd_rr_screen_list_outputs (manager->priv->x11_screen); if (outputs == NULL || outputs[0] == NULL) { g_warning ("failed to get outputs"); return FALSE; } for (i = 0; outputs[i] != NULL; i++) { if (!gsd_rr_output_is_connected (outputs[i])) continue; if (connected == NULL) connected = outputs[i]; if (gsd_rr_output_get_is_primary (outputs[i])) has_primary = TRUE; if (gsd_rr_output_is_laptop (outputs[i])) has_laptop = TRUE; } /* we have an assigned primary device, are we that? */ if (has_primary) return gsd_rr_output_get_is_primary (output); /* choosing the internal panel is probably sane */ if (has_laptop) return gsd_rr_output_is_laptop (output); /* we have to choose one, so go for the first connected device */ if (connected != NULL) return gsd_rr_output_get_id (connected) == gsd_rr_output_get_id (output); return FALSE; } /* TODO: remove when we can dep on a released version of colord */ #ifndef CD_PROFILE_METADATA_SCREEN_BRIGHTNESS #define CD_PROFILE_METADATA_SCREEN_BRIGHTNESS "SCREEN_brightness" #endif #define GSD_DBUS_NAME_POWER GSD_DBUS_NAME ".Power" #define GSD_DBUS_INTERFACE_POWER_SCREEN GSD_DBUS_BASE_INTERFACE ".Power.Screen" #define GSD_DBUS_PATH_POWER GSD_DBUS_PATH "/Power" static void gcm_session_set_output_percentage_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GDBusConnection *connection = G_DBUS_CONNECTION (source_object); GError *error = NULL; GVariant *retval; retval = g_dbus_connection_call_finish (connection, res, &error); if (retval == NULL) { g_warning ("failed to set output brightness: %s", error->message); g_error_free (error); return; } g_variant_unref (retval); } static void gcm_session_set_output_percentage (guint percentage) { GDBusConnection *connection; /* get a ref to the existing bus connection */ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); if (connection == NULL) return; g_dbus_connection_call (connection, GSD_DBUS_NAME_POWER, GSD_DBUS_PATH_POWER, GSD_DBUS_INTERFACE_POWER_SCREEN, "SetPercentage", g_variant_new ("(u)", percentage), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, gcm_session_set_output_percentage_cb, NULL); g_object_unref (connection); } static void gcm_session_device_assign_profile_connect_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CdProfile *profile = CD_PROFILE (object); const gchar *brightness_profile; const gchar *filename; gboolean ret; GError *error = NULL; GsdRROutput *output; guint brightness_percentage; GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data; GsdColorManager *manager = GSD_COLOR_MANAGER (helper->manager); /* get properties */ ret = cd_profile_connect_finish (profile, res, &error); if (!ret) { g_warning ("failed to connect to profile: %s", error->message); g_error_free (error); goto out; } /* get the filename */ filename = cd_profile_get_filename (profile); g_assert (filename != NULL); /* get the output (can't save in helper as GsdRROutput isn't * a GObject, just a pointer */ output = gsd_rr_screen_get_output_by_id (manager->priv->x11_screen, helper->output_id); if (output == NULL) goto out; /* if output is a laptop screen and the profile has a * calibration brightness then set this new brightness */ brightness_profile = cd_profile_get_metadata_item (profile, CD_PROFILE_METADATA_SCREEN_BRIGHTNESS); if (gsd_rr_output_is_laptop (output) && brightness_profile != NULL) { /* the percentage is stored in the profile metadata as * a string, not ideal, but it's all we have... */ brightness_percentage = atoi (brightness_profile); gcm_session_set_output_percentage (brightness_percentage); } /* set the _ICC_PROFILE atom */ ret = gcm_session_use_output_profile_for_screen (manager, output); if (ret) { ret = gcm_session_screen_set_icc_profile (manager, filename, &error); if (!ret) { g_warning ("failed to set screen _ICC_PROFILE: %s", error->message); g_clear_error (&error); } } /* create a vcgt for this icc file */ ret = cd_profile_get_has_vcgt (profile); if (ret) { ret = gcm_session_device_set_gamma (output, profile, &error); if (!ret) { g_warning ("failed to set %s gamma tables: %s", cd_device_get_id (helper->device), error->message); g_error_free (error); goto out; } } else { ret = gcm_session_device_reset_gamma (output, &error); if (!ret) { g_warning ("failed to reset %s gamma tables: %s", cd_device_get_id (helper->device), error->message); g_error_free (error); goto out; } } out: gcm_session_async_helper_free (helper); } /* * Check to see if the on-disk profile has the MAPPING_device_id * metadata, and if not, we should delete the profile and re-create it * so that it gets mapped by the daemon. */ static gboolean gcm_session_check_profile_device_md (const gchar *filename) { cmsHANDLE dict; cmsHPROFILE lcms_profile; const cmsDICTentry *entry; gboolean ret = FALSE; gchar ascii_name[1024]; gsize len; /* parse the ICC file */ lcms_profile = cmsOpenProfileFromFile (filename, "r"); if (lcms_profile == NULL) goto out; /* does profile have metadata? */ dict = cmsReadTag (lcms_profile, cmsSigMetaTag); if (dict == NULL) { g_debug ("auto-edid profile is old, and contains no metadata"); goto out; } for (entry = cmsDictGetEntryList (dict); entry != NULL; entry = cmsDictNextEntry (entry)) { if (entry->Name == NULL) continue; len = wcstombs (ascii_name, entry->Name, sizeof (ascii_name)); if (len == (gsize) -1) continue; if (g_strcmp0 (ascii_name, CD_PROFILE_METADATA_MAPPING_DEVICE_ID) == 0) { ret = TRUE; goto out; } } g_debug ("auto-edid profile is old, and contains no %s data", CD_PROFILE_METADATA_MAPPING_DEVICE_ID); out: if (lcms_profile != NULL) cmsCloseProfile (lcms_profile); return ret; } static void gcm_session_device_assign_connect_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CdDeviceKind kind; CdProfile *profile = NULL; gboolean ret; gchar *autogen_filename = NULL; gchar *autogen_path = NULL; GcmEdid *edid = NULL; GsdRROutput *output = NULL; GError *error = NULL; const gchar *xrandr_id; GcmSessionAsyncHelper *helper; CdDevice *device = CD_DEVICE (object); GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); GsdColorManagerPrivate *priv = manager->priv; /* remove from assign array */ g_hash_table_remove (manager->priv->device_assign_hash, cd_device_get_object_path (device)); /* get properties */ ret = cd_device_connect_finish (device, res, &error); if (!ret) { g_warning ("failed to connect to device: %s", error->message); g_error_free (error); goto out; } /* check we care */ kind = cd_device_get_kind (device); if (kind != CD_DEVICE_KIND_DISPLAY) goto out; g_debug ("need to assign display device %s", cd_device_get_id (device)); /* get the GsdRROutput for the device id */ xrandr_id = cd_device_get_id (device); output = gcm_session_get_x11_output_by_id (manager, xrandr_id, &error); if (output == NULL) { g_warning ("no %s device found: %s", cd_device_get_id (device), error->message); g_error_free (error); goto out; } /* create profile from device edid if it exists */ edid = gcm_session_get_output_edid (manager, output, &error); if (edid == NULL) { g_warning ("unable to get EDID for %s: %s", cd_device_get_id (device), error->message); g_clear_error (&error); } else { autogen_filename = g_strdup_printf ("edid-%s.icc", gcm_edid_get_checksum (edid)); autogen_path = g_build_filename (g_get_user_data_dir (), "icc", autogen_filename, NULL); /* check if auto-profile has up-to-date metadata */ if (gcm_session_check_profile_device_md (autogen_path)) { g_debug ("auto-profile edid %s exists with md", autogen_path); } else { g_debug ("auto-profile edid does not exist, creating as %s", autogen_path); ret = gcm_apply_create_icc_profile_for_edid (manager, device, edid, autogen_path, &error); if (!ret) { g_warning ("failed to create profile from EDID data: %s", error->message); g_clear_error (&error); } } } /* get the default profile for the device */ profile = cd_device_get_default_profile (device); if (profile == NULL) { g_debug ("%s has no default profile to set", cd_device_get_id (device)); /* the default output? */ if (gsd_rr_output_get_is_primary (output)) { gdk_property_delete (priv->gdk_window, gdk_atom_intern_static_string ("_ICC_PROFILE")); gdk_property_delete (priv->gdk_window, gdk_atom_intern_static_string ("_ICC_PROFILE_IN_X_VERSION")); } /* reset, as we want linear profiles for profiling */ ret = gcm_session_device_reset_gamma (output, &error); if (!ret) { g_warning ("failed to reset %s gamma tables: %s", cd_device_get_id (device), error->message); g_error_free (error); goto out; } goto out; } /* get properties */ helper = g_new0 (GcmSessionAsyncHelper, 1); helper->output_id = gsd_rr_output_get_id (output); helper->manager = g_object_ref (manager); helper->device = g_object_ref (device); cd_profile_connect (profile, NULL, gcm_session_device_assign_profile_connect_cb, helper); out: g_free (autogen_filename); g_free (autogen_path); if (edid != NULL) g_object_unref (edid); if (profile != NULL) g_object_unref (profile); } static void gcm_session_device_assign (GsdColorManager *manager, CdDevice *device) { const gchar *key; gpointer found; /* are we already assigning this device */ key = cd_device_get_object_path (device); found = g_hash_table_lookup (manager->priv->device_assign_hash, key); if (found != NULL) { g_debug ("assign for %s already in progress", key); return; } g_hash_table_insert (manager->priv->device_assign_hash, g_strdup (key), GINT_TO_POINTER (TRUE)); cd_device_connect (device, NULL, gcm_session_device_assign_connect_cb, manager); } static void gcm_session_device_added_assign_cb (CdClient *client, CdDevice *device, GsdColorManager *manager) { gcm_session_device_assign (manager, device); } static void gcm_session_device_changed_assign_cb (CdClient *client, CdDevice *device, GsdColorManager *manager) { g_debug ("%s changed", cd_device_get_object_path (device)); gcm_session_device_assign (manager, device); } static void gcm_session_create_device_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CdDevice *device; GError *error = NULL; device = cd_client_create_device_finish (CD_CLIENT (object), res, &error); if (device == NULL) { if (error->domain != CD_CLIENT_ERROR || error->code != CD_CLIENT_ERROR_ALREADY_EXISTS) { g_warning ("failed to create device: %s", error->message); } g_error_free (error); return; } g_object_unref (device); } static void gcm_session_add_x11_output (GsdColorManager *manager, GsdRROutput *output) { const gchar *edid_checksum = NULL; const gchar *model = NULL; const gchar *serial = NULL; const gchar *vendor = NULL; gboolean ret; gchar *device_id = NULL; GcmEdid *edid; GError *error = NULL; GHashTable *device_props = NULL; GsdColorManagerPrivate *priv = manager->priv; /* try to get edid */ edid = gcm_session_get_output_edid (manager, output, &error); if (edid == NULL) { g_warning ("failed to get edid: %s", error->message); g_clear_error (&error); } /* prefer DMI data for the internal output */ ret = gsd_rr_output_is_laptop (output); if (ret) { model = gcm_dmi_get_name (priv->dmi); vendor = gcm_dmi_get_vendor (priv->dmi); } /* use EDID data if we have it */ if (edid != NULL) { edid_checksum = gcm_edid_get_checksum (edid); if (model == NULL) model = gcm_edid_get_monitor_name (edid); if (vendor == NULL) vendor = gcm_edid_get_vendor_name (edid); if (serial == NULL) serial = gcm_edid_get_serial_number (edid); } /* ensure mandatory fields are set */ if (model == NULL) model = gsd_rr_output_get_name (output); if (vendor == NULL) vendor = "unknown"; if (serial == NULL) serial = "unknown"; device_id = gcm_session_get_output_id (manager, output); g_debug ("output %s added", device_id); device_props = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); g_hash_table_insert (device_props, (gpointer) CD_DEVICE_PROPERTY_KIND, (gpointer) cd_device_kind_to_string (CD_DEVICE_KIND_DISPLAY)); g_hash_table_insert (device_props, (gpointer) CD_DEVICE_PROPERTY_MODE, (gpointer) cd_device_mode_to_string (CD_DEVICE_MODE_PHYSICAL)); g_hash_table_insert (device_props, (gpointer) CD_DEVICE_PROPERTY_COLORSPACE, (gpointer) cd_colorspace_to_string (CD_COLORSPACE_RGB)); g_hash_table_insert (device_props, (gpointer) CD_DEVICE_PROPERTY_VENDOR, (gpointer) vendor); g_hash_table_insert (device_props, (gpointer) CD_DEVICE_PROPERTY_MODEL, (gpointer) model); g_hash_table_insert (device_props, (gpointer) CD_DEVICE_PROPERTY_SERIAL, (gpointer) serial); g_hash_table_insert (device_props, (gpointer) CD_DEVICE_METADATA_XRANDR_NAME, (gpointer) gsd_rr_output_get_name (output)); #if CD_CHECK_VERSION(0,1,25) g_hash_table_insert (device_props, (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY, gsd_rr_output_get_is_primary (output) ? (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY_PRIMARY : (gpointer) CD_DEVICE_METADATA_OUTPUT_PRIORITY_SECONDARY); #endif #if CD_CHECK_VERSION(0,1,34) if (edid_checksum != NULL) { g_hash_table_insert (device_props, (gpointer) CD_DEVICE_METADATA_OUTPUT_EDID_MD5, (gpointer) edid_checksum); } #endif #if CD_CHECK_VERSION(0,1,27) /* set this so we can call the device a 'Laptop Screen' in the * control center main panel */ if (gsd_rr_output_is_laptop (output)) { g_hash_table_insert (device_props, (gpointer) CD_DEVICE_PROPERTY_EMBEDDED, NULL); } #endif cd_client_create_device (priv->client, device_id, CD_OBJECT_SCOPE_TEMP, device_props, NULL, gcm_session_create_device_cb, manager); g_free (device_id); if (device_props != NULL) g_hash_table_unref (device_props); if (edid != NULL) g_object_unref (edid); } static void gsd_rr_screen_output_added_cb (GsdRRScreen *screen, GsdRROutput *output, GsdColorManager *manager) { gcm_session_add_x11_output (manager, output); } static void gcm_session_screen_removed_delete_device_cb (GObject *object, GAsyncResult *res, gpointer user_data) { gboolean ret; GError *error = NULL; /* deleted device */ ret = cd_client_delete_device_finish (CD_CLIENT (object), res, &error); if (!ret) { g_warning ("failed to delete device: %s", error->message); g_error_free (error); } } static void gcm_session_screen_removed_find_device_cb (GObject *object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; CdDevice *device; GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); device = cd_client_find_device_finish (manager->priv->client, res, &error); if (device == NULL) { g_warning ("failed to find device: %s", error->message); g_error_free (error); return; } g_debug ("output %s found, and will be removed", cd_device_get_object_path (device)); cd_client_delete_device (manager->priv->client, device, NULL, gcm_session_screen_removed_delete_device_cb, manager); g_object_unref (device); } static void gsd_rr_screen_output_removed_cb (GsdRRScreen *screen, GsdRROutput *output, GsdColorManager *manager) { g_debug ("output %s removed", gsd_rr_output_get_name (output)); g_hash_table_remove (manager->priv->edid_cache, gsd_rr_output_get_name (output)); cd_client_find_device_by_property (manager->priv->client, CD_DEVICE_METADATA_XRANDR_NAME, gsd_rr_output_get_name (output), NULL, gcm_session_screen_removed_find_device_cb, manager); } static void gcm_session_get_devices_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CdDevice *device; GError *error = NULL; GPtrArray *array; guint i; GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); array = cd_client_get_devices_finish (CD_CLIENT (object), res, &error); if (array == NULL) { g_warning ("failed to get devices: %s", error->message); g_error_free (error); goto out; } for (i = 0; i < array->len; i++) { device = g_ptr_array_index (array, i); gcm_session_device_assign (manager, device); } out: if (array != NULL) g_ptr_array_unref (array); } static void gcm_session_profile_gamma_find_device_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CdClient *client = CD_CLIENT (object); CdDevice *device = NULL; GError *error = NULL; GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); device = cd_client_find_device_by_property_finish (client, res, &error); if (device == NULL) { g_warning ("could not find device: %s", error->message); g_error_free (error); goto out; } /* get properties */ cd_device_connect (device, NULL, gcm_session_device_assign_connect_cb, manager); out: if (device != NULL) g_object_unref (device); } /* We have to reset the gamma tables each time as if the primary output * has changed then different crtcs are going to be used. * See https://bugzilla.gnome.org/show_bug.cgi?id=660164 for an example */ static void gsd_rr_screen_output_changed_cb (GsdRRScreen *screen, GsdColorManager *manager) { GsdRROutput **outputs; GsdColorManagerPrivate *priv = manager->priv; guint i; /* get X11 outputs */ outputs = gsd_rr_screen_list_outputs (priv->x11_screen); if (outputs == NULL) { g_warning ("failed to get outputs"); return; } for (i = 0; outputs[i] != NULL; i++) { if (!gsd_rr_output_is_connected (outputs[i])) continue; /* get CdDevice for this output */ cd_client_find_device_by_property (manager->priv->client, CD_DEVICE_METADATA_XRANDR_NAME, gsd_rr_output_get_name (outputs[i]), NULL, gcm_session_profile_gamma_find_device_cb, manager); } } static void gcm_session_client_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { gboolean ret; GError *error = NULL; GsdRROutput **outputs; guint i; GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); GsdColorManagerPrivate *priv = manager->priv; /* connected */ g_debug ("connected to colord"); ret = cd_client_connect_finish (manager->priv->client, res, &error); if (!ret) { g_warning ("failed to connect to colord: %s", error->message); g_error_free (error); return; } #if CD_CHECK_VERSION(0,1,12) /* is there an available colord instance? */ ret = cd_client_get_has_server (manager->priv->client); if (!ret) { g_warning ("There is no colord server available"); goto out; } #endif /* add profiles */ gcm_profile_store_search (priv->profile_store); /* add screens */ gsd_rr_screen_refresh (priv->x11_screen, &error); if (error != NULL) { g_warning ("failed to refresh: %s", error->message); g_error_free (error); goto out; } /* get X11 outputs */ outputs = gsd_rr_screen_list_outputs (priv->x11_screen); if (outputs == NULL) { g_warning ("failed to get outputs"); goto out; } for (i = 0; outputs[i] != NULL; i++) { if (gsd_rr_output_is_connected (outputs[i])) gcm_session_add_x11_output (manager, outputs[i]); } /* only connect when colord is awake */ g_signal_connect (priv->x11_screen, "output-connected", G_CALLBACK (gsd_rr_screen_output_added_cb), manager); g_signal_connect (priv->x11_screen, "output-disconnected", G_CALLBACK (gsd_rr_screen_output_removed_cb), manager); g_signal_connect (priv->x11_screen, "changed", G_CALLBACK (gsd_rr_screen_output_changed_cb), manager); g_signal_connect (priv->client, "device-added", G_CALLBACK (gcm_session_device_added_assign_cb), manager); g_signal_connect (priv->client, "device-changed", G_CALLBACK (gcm_session_device_changed_assign_cb), manager); /* set for each device that already exist */ cd_client_get_devices (priv->client, NULL, gcm_session_get_devices_cb, manager); out: return; } gboolean gsd_color_manager_start (GsdColorManager *manager, GError **error) { GsdColorManagerPrivate *priv = manager->priv; gboolean ret = FALSE; g_debug ("Starting color manager"); gnome_settings_profile_start (NULL); /* coldplug the list of screens */ priv->x11_screen = gsd_rr_screen_new (gdk_screen_get_default (), error); if (priv->x11_screen == NULL) goto out; cd_client_connect (priv->client, NULL, gcm_session_client_connect_cb, manager); /* success */ ret = TRUE; out: gnome_settings_profile_end (NULL); return ret; } void gsd_color_manager_stop (GsdColorManager *manager) { g_debug ("Stopping color manager"); g_clear_object (&manager->priv->settings); g_clear_object (&manager->priv->client); g_clear_object (&manager->priv->profile_store); g_clear_object (&manager->priv->dmi); g_clear_object (&manager->priv->session); g_clear_pointer (&manager->priv->edid_cache, g_hash_table_destroy); g_clear_pointer (&manager->priv->device_assign_hash, g_hash_table_destroy); g_clear_object (&manager->priv->x11_screen); } static void gcm_session_exec_control_center (GsdColorManager *manager) { gboolean ret; GError *error = NULL; GAppInfo *app_info; GdkAppLaunchContext *launch_context; /* setup the launch context so the startup notification is correct */ launch_context = gdk_display_get_app_launch_context (gdk_display_get_default ()); app_info = g_app_info_create_from_commandline (BINDIR "/gnome-control-center color", "gnome-control-center", G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION, &error); if (app_info == NULL) { g_warning ("failed to create application info: %s", error->message); g_error_free (error); goto out; } /* launch gnome-control-center */ ret = g_app_info_launch (app_info, NULL, G_APP_LAUNCH_CONTEXT (launch_context), &error); if (!ret) { g_warning ("failed to launch gnome-control-center: %s", error->message); g_error_free (error); goto out; } out: g_object_unref (launch_context); if (app_info != NULL) g_object_unref (app_info); } static void gcm_session_notify_cb (NotifyNotification *notification, gchar *action, gpointer user_data) { GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); if (g_strcmp0 (action, "recalibrate") == 0) { notify_notification_close (notification, NULL); gcm_session_exec_control_center (manager); } } static void closed_cb (NotifyNotification *notification, gpointer data) { g_object_unref (notification); } static gboolean gcm_session_notify_recalibrate (GsdColorManager *manager, const gchar *title, const gchar *message, CdDeviceKind kind) { gboolean ret; GError *error = NULL; NotifyNotification *notification; GsdColorManagerPrivate *priv = manager->priv; /* show a bubble */ notification = notify_notification_new (title, message, "preferences-color"); notify_notification_set_timeout (notification, GCM_SESSION_NOTIFY_TIMEOUT); notify_notification_set_urgency (notification, NOTIFY_URGENCY_LOW); notify_notification_set_app_name (notification, _("Color")); /* TRANSLATORS: button: this is to open GCM */ notify_notification_add_action (notification, "recalibrate", _("Recalibrate now"), gcm_session_notify_cb, priv, NULL); g_signal_connect (notification, "closed", G_CALLBACK (closed_cb), NULL); ret = notify_notification_show (notification, &error); if (!ret) { g_warning ("failed to show notification: %s", error->message); g_error_free (error); } return ret; } static gchar * gcm_session_device_get_title (CdDevice *device) { const gchar *vendor; const gchar *model; model = cd_device_get_model (device); vendor = cd_device_get_vendor (device); if (model != NULL && vendor != NULL) return g_strdup_printf ("%s - %s", vendor, model); if (vendor != NULL) return g_strdup (vendor); if (model != NULL) return g_strdup (model); return g_strdup (cd_device_get_id (device)); } static void gcm_session_notify_device (GsdColorManager *manager, CdDevice *device) { CdDeviceKind kind; const gchar *title; gchar *device_title = NULL; gchar *message; guint threshold; glong since; GsdColorManagerPrivate *priv = manager->priv; /* TRANSLATORS: this is when the device has not been recalibrated in a while */ title = _("Recalibration required"); device_title = gcm_session_device_get_title (device); /* check we care */ kind = cd_device_get_kind (device); if (kind == CD_DEVICE_KIND_DISPLAY) { /* get from GSettings */ threshold = g_settings_get_uint (priv->settings, GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD); /* TRANSLATORS: this is when the display has not been recalibrated in a while */ message = g_strdup_printf (_("The display '%s' should be recalibrated soon."), device_title); } else { /* get from GSettings */ threshold = g_settings_get_uint (priv->settings, GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD); /* TRANSLATORS: this is when the printer has not been recalibrated in a while */ message = g_strdup_printf (_("The printer '%s' should be recalibrated soon."), device_title); } /* check if we need to notify */ since = (g_get_real_time () - cd_device_get_modified (device)) / G_USEC_PER_SEC; if (threshold > since) gcm_session_notify_recalibrate (manager, title, message, kind); g_free (device_title); g_free (message); } static void gcm_session_profile_connect_cb (GObject *object, GAsyncResult *res, gpointer user_data) { const gchar *filename; gboolean ret; gchar *basename = NULL; const gchar *data_source; GError *error = NULL; CdProfile *profile = CD_PROFILE (object); GcmSessionAsyncHelper *helper = (GcmSessionAsyncHelper *) user_data; GsdColorManager *manager = GSD_COLOR_MANAGER (helper->manager); ret = cd_profile_connect_finish (profile, res, &error); if (!ret) { g_warning ("failed to connect to profile: %s", error->message); g_error_free (error); goto out; } /* ensure it's a profile generated by us */ data_source = cd_profile_get_metadata_item (profile, CD_PROFILE_METADATA_DATA_SOURCE); if (data_source == NULL) { /* existing profiles from gnome-color-manager < 3.1 * won't have the extra metadata values added */ filename = cd_profile_get_filename (profile); if (filename == NULL) goto out; basename = g_path_get_basename (filename); if (!g_str_has_prefix (basename, "GCM")) { g_debug ("not a GCM profile for %s: %s", cd_device_get_id (helper->device), filename); goto out; } /* ensure it's been created from a calibration, rather than from * auto-EDID */ } else if (g_strcmp0 (data_source, CD_PROFILE_METADATA_DATA_SOURCE_CALIB) != 0) { g_debug ("not a calib profile for %s", cd_device_get_id (helper->device)); goto out; } /* handle device */ gcm_session_notify_device (manager, helper->device); out: gcm_session_async_helper_free (helper); g_free (basename); } static void gcm_session_device_connect_cb (GObject *object, GAsyncResult *res, gpointer user_data) { gboolean ret; GError *error = NULL; CdDeviceKind kind; CdProfile *profile = NULL; CdDevice *device = CD_DEVICE (object); GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); GcmSessionAsyncHelper *helper; ret = cd_device_connect_finish (device, res, &error); if (!ret) { g_warning ("failed to connect to device: %s", error->message); g_error_free (error); goto out; } /* check we care */ kind = cd_device_get_kind (device); if (kind != CD_DEVICE_KIND_DISPLAY && kind != CD_DEVICE_KIND_PRINTER) goto out; /* ensure we have a profile */ profile = cd_device_get_default_profile (device); if (profile == NULL) { g_debug ("no profile set for %s", cd_device_get_id (device)); goto out; } /* connect to the profile */ helper = g_new0 (GcmSessionAsyncHelper, 1); helper->manager = g_object_ref (manager); helper->device = g_object_ref (device); cd_profile_connect (profile, NULL, gcm_session_profile_connect_cb, helper); out: if (profile != NULL) g_object_unref (profile); } static void gcm_session_device_added_notify_cb (CdClient *client, CdDevice *device, GsdColorManager *manager) { /* connect to the device to get properties */ cd_device_connect (device, NULL, gcm_session_device_connect_cb, manager); } static gchar * gcm_session_get_precooked_md5 (cmsHPROFILE lcms_profile) { cmsUInt8Number profile_id[16]; gboolean md5_precooked = FALSE; guint i; gchar *md5 = NULL; /* check to see if we have a pre-cooked MD5 */ cmsGetHeaderProfileID (lcms_profile, profile_id); for (i = 0; i < 16; i++) { if (profile_id[i] != 0) { md5_precooked = TRUE; break; } } if (!md5_precooked) goto out; /* convert to a hex string */ md5 = g_new0 (gchar, 32 + 1); for (i = 0; i < 16; i++) g_snprintf (md5 + i*2, 3, "%02x", profile_id[i]); out: return md5; } static gchar * gcm_session_get_md5_for_filename (const gchar *filename, GError **error) { gboolean ret; gchar *checksum = NULL; gchar *data = NULL; gsize length; cmsHPROFILE lcms_profile = NULL; /* get the internal profile id, if it exists */ lcms_profile = cmsOpenProfileFromFile (filename, "r"); if (lcms_profile == NULL) { g_set_error_literal (error, GSD_COLOR_MANAGER_ERROR, GSD_COLOR_MANAGER_ERROR_FAILED, "failed to load: not an ICC profile"); goto out; } checksum = gcm_session_get_precooked_md5 (lcms_profile); if (checksum != NULL) goto out; /* generate checksum */ ret = g_file_get_contents (filename, &data, &length, error); if (!ret) goto out; checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *) data, length); out: g_free (data); if (lcms_profile != NULL) cmsCloseProfile (lcms_profile); return checksum; } static void gcm_session_create_profile_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CdProfile *profile; GError *error = NULL; CdClient *client = CD_CLIENT (object); profile = cd_client_create_profile_finish (client, res, &error); if (profile == NULL) { if (error->domain != CD_CLIENT_ERROR || error->code != CD_CLIENT_ERROR_ALREADY_EXISTS) g_warning ("%s", error->message); g_error_free (error); return; } g_object_unref (profile); } static void gcm_session_profile_store_added_cb (GcmProfileStore *profile_store, const gchar *filename, GsdColorManager *manager) { gchar *checksum = NULL; gchar *profile_id = NULL; GError *error = NULL; GHashTable *profile_props = NULL; GsdColorManagerPrivate *priv = manager->priv; g_debug ("profile %s added", filename); /* generate ID */ checksum = gcm_session_get_md5_for_filename (filename, &error); if (checksum == NULL) { g_debug ("failed to get profile checksum for %s: %s", filename, error->message); g_error_free (error); goto out; } profile_id = g_strdup_printf ("icc-%s", checksum); profile_props = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); g_hash_table_insert (profile_props, CD_PROFILE_PROPERTY_FILENAME, (gpointer) filename); g_hash_table_insert (profile_props, CD_PROFILE_METADATA_FILE_CHECKSUM, (gpointer) checksum); cd_client_create_profile (priv->client, profile_id, CD_OBJECT_SCOPE_TEMP, profile_props, NULL, gcm_session_create_profile_cb, manager); out: g_free (checksum); g_free (profile_id); if (profile_props != NULL) g_hash_table_unref (profile_props); } static void gcm_session_delete_profile_cb (GObject *object, GAsyncResult *res, gpointer user_data) { gboolean ret; GError *error = NULL; CdClient *client = CD_CLIENT (object); ret = cd_client_delete_profile_finish (client, res, &error); if (!ret) { g_warning ("%s", error->message); g_error_free (error); } } static void gcm_session_find_profile_by_filename_cb (GObject *object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; CdProfile *profile; CdClient *client = CD_CLIENT (object); GsdColorManager *manager = GSD_COLOR_MANAGER (user_data); profile = cd_client_find_profile_by_filename_finish (client, res, &error); if (profile == NULL) { g_warning ("%s", error->message); g_error_free (error); goto out; } /* remove it from colord */ cd_client_delete_profile (manager->priv->client, profile, NULL, gcm_session_delete_profile_cb, manager); out: if (profile != NULL) g_object_unref (profile); } static void gcm_session_profile_store_removed_cb (GcmProfileStore *profile_store, const gchar *filename, GsdColorManager *manager) { /* find the ID for the filename */ g_debug ("filename %s removed", filename); cd_client_find_profile_by_filename (manager->priv->client, filename, NULL, gcm_session_find_profile_by_filename_cb, manager); } static void gcm_session_sensor_added_cb (CdClient *client, CdSensor *sensor, GsdColorManager *manager) { ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "device-added", /* TRANSLATORS: this is the application name */ CA_PROP_APPLICATION_NAME, _("GNOME Settings Daemon Color Plugin"), /* TRANSLATORS: this is a sound description */ CA_PROP_EVENT_DESCRIPTION, _("Color calibration device added"), NULL); /* open up the color prefs window */ gcm_session_exec_control_center (manager); } static void gcm_session_sensor_removed_cb (CdClient *client, CdSensor *sensor, GsdColorManager *manager) { ca_context_play (ca_gtk_context_get (), 0, CA_PROP_EVENT_ID, "device-removed", /* TRANSLATORS: this is the application name */ CA_PROP_APPLICATION_NAME, _("GNOME Settings Daemon Color Plugin"), /* TRANSLATORS: this is a sound description */ CA_PROP_EVENT_DESCRIPTION, _("Color calibration device removed"), NULL); } static gboolean has_changed (char **strv, const char *str) { guint i; for (i = 0; strv[i] != NULL; i++) { if (g_str_equal (str, strv[i])) return TRUE; } return FALSE; } static void gcm_session_active_changed_cb (GDBusProxy *session, GVariant *changed, char **invalidated, GsdColorManager *manager) { GsdColorManagerPrivate *priv = manager->priv; GVariant *active_v = NULL; gboolean is_active; if (has_changed (invalidated, "SessionIsActive")) return; /* not yet connected to the daemon */ if (!cd_client_get_connected (priv->client)) return; active_v = g_dbus_proxy_get_cached_property (session, "SessionIsActive"); g_return_if_fail (active_v != NULL); is_active = g_variant_get_boolean (active_v); g_variant_unref (active_v); /* When doing the fast-user-switch into a new account, load the * new users chosen profiles. * * If this is the first time the GnomeSettingsSession has been * loaded, then we'll get a change from unknown to active * and we want to avoid reprobing the devices for that. */ if (is_active && !priv->session_is_active) { g_debug ("Done switch to new account, reload devices"); cd_client_get_devices (manager->priv->client, NULL, gcm_session_get_devices_cb, manager); } priv->session_is_active = is_active; } static void gsd_color_manager_class_init (GsdColorManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_color_manager_finalize; g_type_class_add_private (klass, sizeof (GsdColorManagerPrivate)); } static void gsd_color_manager_init (GsdColorManager *manager) { GsdColorManagerPrivate *priv; priv = manager->priv = GSD_COLOR_MANAGER_GET_PRIVATE (manager); /* track the active session */ priv->session = gnome_settings_bus_get_session_proxy (); g_signal_connect (priv->session, "g-properties-changed", G_CALLBACK (gcm_session_active_changed_cb), manager); /* set the _ICC_PROFILE atoms on the root screen */ priv->gdk_window = gdk_screen_get_root_window (gdk_screen_get_default ()); /* parsing the EDID is expensive */ priv->edid_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); /* we don't want to assign devices multiple times at startup */ priv->device_assign_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); /* use DMI data for internal panels */ priv->dmi = gcm_dmi_new (); priv->settings = g_settings_new ("org.gnome.settings-daemon.plugins.color"); priv->client = cd_client_new (); g_signal_connect (priv->client, "device-added", G_CALLBACK (gcm_session_device_added_notify_cb), manager); g_signal_connect (priv->client, "sensor-added", G_CALLBACK (gcm_session_sensor_added_cb), manager); g_signal_connect (priv->client, "sensor-removed", G_CALLBACK (gcm_session_sensor_removed_cb), manager); /* have access to all user profiles */ priv->profile_store = gcm_profile_store_new (); g_signal_connect (priv->profile_store, "added", G_CALLBACK (gcm_session_profile_store_added_cb), manager); g_signal_connect (priv->profile_store, "removed", G_CALLBACK (gcm_session_profile_store_removed_cb), manager); } static void gsd_color_manager_finalize (GObject *object) { GsdColorManager *manager; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_COLOR_MANAGER (object)); manager = GSD_COLOR_MANAGER (object); g_signal_handlers_disconnect_by_data (manager->priv->session, manager); g_clear_object (&manager->priv->settings); g_clear_object (&manager->priv->client); g_clear_object (&manager->priv->profile_store); g_clear_object (&manager->priv->dmi); g_clear_object (&manager->priv->session); g_clear_pointer (&manager->priv->edid_cache, g_hash_table_destroy); g_clear_pointer (&manager->priv->device_assign_hash, g_hash_table_destroy); g_clear_object (&manager->priv->x11_screen); G_OBJECT_CLASS (gsd_color_manager_parent_class)->finalize (object); } GsdColorManager * gsd_color_manager_new (void) { if (manager_object != NULL) { g_object_ref (manager_object); } else { manager_object = g_object_new (GSD_TYPE_COLOR_MANAGER, NULL); g_object_add_weak_pointer (manager_object, (gpointer *) &manager_object); } return GSD_COLOR_MANAGER (manager_object); } ./plugins/color/gcm-self-test.c0000644000004100000410000000774612735467744016721 0ustar www-datawww-data/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2007-2011 Richard Hughes * * Licensed under the GNU General Public License Version 2 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include "gcm-edid.h" #include "gcm-dmi.h" static void gcm_test_dmi_func (void) { GcmDmi *dmi; dmi = gcm_dmi_new (); g_assert (dmi != NULL); g_assert (gcm_dmi_get_name (dmi) != NULL); g_assert (gcm_dmi_get_vendor (dmi) != NULL); g_object_unref (dmi); } static void gcm_test_edid_func (void) { GcmEdid *edid; gchar *data; gboolean ret; GError *error = NULL; gsize length = 0; edid = gcm_edid_new (); g_assert (edid != NULL); /* LG 21" LCD panel */ ret = g_file_get_contents (TESTDATADIR "/LG-L225W-External.bin", &data, &length, &error); g_assert_no_error (error); g_assert (ret); ret = gcm_edid_parse (edid, (const guint8 *) data, length, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpstr (gcm_edid_get_monitor_name (edid), ==, "L225W"); g_assert_cmpstr (gcm_edid_get_vendor_name (edid), ==, "Goldstar Company Ltd"); g_assert_cmpstr (gcm_edid_get_serial_number (edid), ==, "34398"); g_assert_cmpstr (gcm_edid_get_eisa_id (edid), ==, NULL); g_assert_cmpstr (gcm_edid_get_checksum (edid), ==, "0bb44865bb29984a4bae620656c31368"); g_assert_cmpstr (gcm_edid_get_pnp_id (edid), ==, "GSM"); g_assert_cmpint (gcm_edid_get_height (edid), ==, 30); g_assert_cmpint (gcm_edid_get_width (edid), ==, 47); g_assert_cmpfloat (gcm_edid_get_gamma (edid), >=, 2.2f - 0.01); g_assert_cmpfloat (gcm_edid_get_gamma (edid), <, 2.2f + 0.01); g_free (data); /* Lenovo T61 internal Panel */ ret = g_file_get_contents (TESTDATADIR "/Lenovo-T61-Internal.bin", &data, &length, &error); g_assert_no_error (error); g_assert (ret); ret = gcm_edid_parse (edid, (const guint8 *) data, length, &error); g_assert_no_error (error); g_assert (ret); g_assert_cmpstr (gcm_edid_get_monitor_name (edid), ==, NULL); g_assert_cmpstr (gcm_edid_get_vendor_name (edid), ==, "IBM France"); g_assert_cmpstr (gcm_edid_get_serial_number (edid), ==, NULL); g_assert_cmpstr (gcm_edid_get_eisa_id (edid), ==, "LTN154P2-L05"); g_assert_cmpstr (gcm_edid_get_checksum (edid), ==, "e1865128c7cd5e5ed49ecfc8102f6f9c"); g_assert_cmpstr (gcm_edid_get_pnp_id (edid), ==, "IBM"); g_assert_cmpint (gcm_edid_get_height (edid), ==, 21); g_assert_cmpint (gcm_edid_get_width (edid), ==, 33); g_assert_cmpfloat (gcm_edid_get_gamma (edid), >=, 2.2f - 0.01); g_assert_cmpfloat (gcm_edid_get_gamma (edid), <, 2.2f + 0.01); g_free (data); g_object_unref (edid); } int main (int argc, char **argv) { gtk_init (&argc, &argv); g_test_init (&argc, &argv, NULL); g_test_add_func ("/color/dmi", gcm_test_dmi_func); g_test_add_func ("/color/edid", gcm_test_edid_func); return g_test_run (); } ./plugins/wacom/0000755000004100000410000000000012735467763014056 5ustar www-datawww-data./plugins/wacom/gsd-wacom-device.c0000644000004100000410000017041212735467744017344 0ustar www-datawww-data/* * 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 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. * * Author: Bastien Nocera * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "gsd-input-helper.h" #include "gsd-enums.h" #include "gsd-wacom-device.h" #include "gsd-rr.h" #define GSD_WACOM_STYLUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_WACOM_STYLUS, GsdWacomStylusPrivate)) #define WACOM_TABLET_SCHEMA "org.gnome.settings-daemon.peripherals.wacom" #define WACOM_DEVICE_CONFIG_BASE "/org/gnome/settings-daemon/peripherals/wacom/%s-%s/" #define WACOM_STYLUS_SCHEMA "org.gnome.settings-daemon.peripherals.wacom.stylus" #define WACOM_ERASER_SCHEMA "org.gnome.settings-daemon.peripherals.wacom.eraser" #define WACOM_BUTTON_SCHEMA "org.gnome.settings-daemon.peripherals.wacom.tablet-button" static struct { GsdRRRotation rotation; GsdWacomRotation rotation_wacom; const gchar *rotation_string; } rotation_table[] = { { GSD_RR_ROTATION_0, GSD_WACOM_ROTATION_NONE, "none" }, { GSD_RR_ROTATION_90, GSD_WACOM_ROTATION_CCW, "ccw" }, { GSD_RR_ROTATION_180, GSD_WACOM_ROTATION_HALF, "half" }, { GSD_RR_ROTATION_270, GSD_WACOM_ROTATION_CW, "cw" } }; static WacomDeviceDatabase *db = NULL; struct GsdWacomStylusPrivate { GsdWacomDevice *device; int id; WacomStylusType type; char *name; const char *icon_name; GSettings *settings; gboolean has_eraser; int num_buttons; }; static void gsd_wacom_stylus_class_init (GsdWacomStylusClass *klass); static void gsd_wacom_stylus_init (GsdWacomStylus *wacom_stylus); static void gsd_wacom_stylus_finalize (GObject *object); G_DEFINE_TYPE (GsdWacomStylus, gsd_wacom_stylus, G_TYPE_OBJECT) static void gsd_wacom_stylus_class_init (GsdWacomStylusClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gsd_wacom_stylus_finalize; g_type_class_add_private (klass, sizeof (GsdWacomStylusPrivate)); } static void gsd_wacom_stylus_init (GsdWacomStylus *stylus) { stylus->priv = GSD_WACOM_STYLUS_GET_PRIVATE (stylus); } static void gsd_wacom_stylus_finalize (GObject *object) { GsdWacomStylus *stylus; GsdWacomStylusPrivate *p; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_WACOM_STYLUS (object)); stylus = GSD_WACOM_STYLUS (object); g_return_if_fail (stylus->priv != NULL); p = stylus->priv; if (p->settings != NULL) { g_object_unref (p->settings); p->settings = NULL; } g_free (p->name); p->name = NULL; G_OBJECT_CLASS (gsd_wacom_stylus_parent_class)->finalize (object); } static const char * get_icon_name_from_type (WacomStylusType type) { switch (type) { case WSTYLUS_INKING: case WSTYLUS_STROKE: /* The stroke pen is the same as the inking pen with * a different nib */ return "wacom-stylus-inking"; case WSTYLUS_AIRBRUSH: return "wacom-stylus-airbrush"; case WSTYLUS_MARKER: return "wacom-stylus-art-pen"; case WSTYLUS_CLASSIC: return "wacom-stylus-classic"; default: return "wacom-stylus"; } } static GsdWacomStylus * gsd_wacom_stylus_new (GsdWacomDevice *device, const WacomStylus *wstylus, GSettings *settings) { GsdWacomStylus *stylus; g_return_val_if_fail (G_IS_SETTINGS (settings), NULL); g_return_val_if_fail (wstylus != NULL, NULL); stylus = GSD_WACOM_STYLUS (g_object_new (GSD_TYPE_WACOM_STYLUS, NULL)); stylus->priv->device = device; stylus->priv->id = libwacom_stylus_get_id (wstylus); stylus->priv->name = g_strdup (libwacom_stylus_get_name (wstylus)); stylus->priv->settings = settings; stylus->priv->type = libwacom_stylus_get_type (wstylus); stylus->priv->icon_name = get_icon_name_from_type (stylus->priv->type); stylus->priv->has_eraser = libwacom_stylus_has_eraser (wstylus); stylus->priv->num_buttons = libwacom_stylus_get_num_buttons (wstylus); return stylus; } GSettings * gsd_wacom_stylus_get_settings (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->settings; } const char * gsd_wacom_stylus_get_name (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->name; } const char * gsd_wacom_stylus_get_icon_name (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->icon_name; } GsdWacomDevice * gsd_wacom_stylus_get_device (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), NULL); return stylus->priv->device; } gboolean gsd_wacom_stylus_get_has_eraser (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), FALSE); return stylus->priv->has_eraser; } guint gsd_wacom_stylus_get_num_buttons (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), -1); return stylus->priv->num_buttons; } GsdWacomStylusType gsd_wacom_stylus_get_stylus_type (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), WACOM_STYLUS_TYPE_UNKNOWN); switch (stylus->priv->type) { case WSTYLUS_UNKNOWN: return WACOM_STYLUS_TYPE_UNKNOWN; case WSTYLUS_GENERAL: return WACOM_STYLUS_TYPE_GENERAL; case WSTYLUS_INKING: return WACOM_STYLUS_TYPE_INKING; case WSTYLUS_AIRBRUSH: return WACOM_STYLUS_TYPE_AIRBRUSH; case WSTYLUS_CLASSIC: return WACOM_STYLUS_TYPE_CLASSIC; case WSTYLUS_MARKER: return WACOM_STYLUS_TYPE_MARKER; case WSTYLUS_STROKE: return WACOM_STYLUS_TYPE_STROKE; case WSTYLUS_PUCK: return WACOM_STYLUS_TYPE_PUCK; default: g_assert_not_reached (); } return WACOM_STYLUS_TYPE_UNKNOWN; } int gsd_wacom_stylus_get_id (GsdWacomStylus *stylus) { g_return_val_if_fail (GSD_IS_WACOM_STYLUS (stylus), -1); return stylus->priv->id; } /* Tablet buttons */ static GsdWacomTabletButton * gsd_wacom_tablet_button_new (const char *name, const char *id, const char *settings_path, GsdWacomTabletButtonType type, GsdWacomTabletButtonPos pos, int group_id, int idx, int status_led) { GsdWacomTabletButton *ret; ret = g_new0 (GsdWacomTabletButton, 1); ret->name = g_strdup (name); ret->id = g_strdup (id); if (type != WACOM_TABLET_BUTTON_TYPE_HARDCODED) { char *button_settings_path; button_settings_path = g_strdup_printf ("%s%s/", settings_path, id); ret->settings = g_settings_new_with_path (WACOM_BUTTON_SCHEMA, button_settings_path); g_free (button_settings_path); } ret->group_id = group_id; ret->idx = idx; ret->type = type; ret->pos = pos; ret->status_led = status_led; return ret; } void gsd_wacom_tablet_button_free (GsdWacomTabletButton *button) { g_return_if_fail (button != NULL); if (button->settings != NULL) g_object_unref (button->settings); g_free (button->name); g_free (button->id); g_free (button); } GsdWacomTabletButton * gsd_wacom_tablet_button_copy (GsdWacomTabletButton *button) { GsdWacomTabletButton *ret; g_return_val_if_fail (button != NULL, NULL); ret = g_new0 (GsdWacomTabletButton, 1); ret->name = g_strdup (button->name); if (button->settings != NULL) ret->settings = g_object_ref (button->settings); ret->id = button->id; ret->type = button->type; ret->group_id = button->group_id; return ret; } #define GSD_WACOM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_WACOM_DEVICE, GsdWacomDevicePrivate)) /* we support two types of settings: * Tablet-wide settings: applied to each tool on the tablet. e.g. rotation * Tool-specific settings: applied to one tool only. */ #define SETTINGS_WACOM_DIR "org.gnome.settings-daemon.peripherals.wacom" #define SETTINGS_STYLUS_DIR "stylus" #define SETTINGS_ERASER_DIR "eraser" struct GsdWacomDevicePrivate { GdkDevice *gdk_device; int device_id; int opcode; GsdWacomDeviceType type; char *name; char *path; char *machine_id; const char *icon_name; char *layout_path; char *tool_name; gboolean reversible; gboolean is_screen_tablet; gboolean is_isd; /* integrated system device */ gboolean is_fallback; GList *styli; GsdWacomStylus *last_stylus; GList *buttons; gint num_rings; gint num_strips; GHashTable *modes; /* key = int (group), value = int (index) */ GHashTable *num_modes; /* key = int (group), value = int (index) */ GSettings *wacom_settings; }; enum { PROP_0, PROP_GDK_DEVICE, PROP_LAST_STYLUS }; static void gsd_wacom_device_class_init (GsdWacomDeviceClass *klass); static void gsd_wacom_device_init (GsdWacomDevice *wacom_device); static void gsd_wacom_device_finalize (GObject *object); G_DEFINE_TYPE (GsdWacomDevice, gsd_wacom_device, G_TYPE_OBJECT) static GdkFilterReturn filter_events (XEvent *xevent, GdkEvent *event, GsdWacomDevice *device) { XIEvent *xiev; XIPropertyEvent *pev; XGenericEventCookie *cookie; char *name; int tool_id; /* verify we have a property event */ if (xevent->type != GenericEvent) return GDK_FILTER_CONTINUE; cookie = &xevent->xcookie; if (cookie->extension != device->priv->opcode) return GDK_FILTER_CONTINUE; xiev = (XIEvent *) xevent->xcookie.data; if (xiev->evtype != XI_PropertyEvent) return GDK_FILTER_CONTINUE; pev = (XIPropertyEvent *) xiev; /* Is the event for us? */ if (pev->deviceid != device->priv->device_id) return GDK_FILTER_CONTINUE; name = XGetAtomName (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), pev->property); if (name == NULL || g_strcmp0 (name, WACOM_SERIAL_IDS_PROP) != 0) { if (name) XFree (name); return GDK_FILTER_CONTINUE; } XFree (name); tool_id = xdevice_get_last_tool_id (device->priv->device_id); if (tool_id == -1) { g_warning ("Failed to get value for changed stylus ID on device '%d'", device->priv->device_id); return GDK_FILTER_CONTINUE; } gsd_wacom_device_set_current_stylus (device, tool_id); return GDK_FILTER_CONTINUE; } static gboolean setup_property_notify (GsdWacomDevice *device) { Display *dpy; XIEventMask evmask; int tool_id; evmask.deviceid = device->priv->device_id; evmask.mask_len = XIMaskLen (XI_PropertyEvent); evmask.mask = g_new0 (guchar, evmask.mask_len); XISetMask (evmask.mask, XI_PropertyEvent); dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XISelectEvents (dpy, DefaultRootWindow (dpy), &evmask, 1); g_free (evmask.mask); gdk_window_add_filter (NULL, (GdkFilterFunc) filter_events, device); tool_id = xdevice_get_last_tool_id (device->priv->device_id); if (tool_id == -1) { g_warning ("Failed to get value for changed stylus ID on device '%d", device->priv->device_id); return TRUE; } gsd_wacom_device_set_current_stylus (device, tool_id); return TRUE; } static GsdWacomDeviceType get_device_type (XDeviceInfo *dev) { GsdWacomDeviceType ret; static Atom stylus, cursor, eraser, pad, touch, prop; XDevice *device; Atom realtype; int realformat; unsigned long nitems, bytes_after; unsigned char *data = NULL; int rc; ret = WACOM_TYPE_INVALID; if ((dev->use == IsXPointer) || (dev->use == IsXKeyboard)) return ret; if (!stylus) stylus = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "STYLUS", False); if (!eraser) eraser = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "ERASER", False); if (!cursor) cursor = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "CURSOR", False); if (!pad) pad = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "PAD", False); if (!touch) touch = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "TOUCH", False); if (!prop) prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Wacom Tool Type", False); if (dev->type == stylus) ret = WACOM_TYPE_STYLUS; else if (dev->type == eraser) ret = WACOM_TYPE_ERASER; else if (dev->type == cursor) ret = WACOM_TYPE_CURSOR; else if (dev->type == pad) ret = WACOM_TYPE_PAD; else if (dev->type == touch) ret = WACOM_TYPE_TOUCH; if (ret == WACOM_TYPE_INVALID) return ret; /* There is currently no good way of detecting the driver for a device * other than checking for a driver-specific property. * Wacom Tool Type exists on all tools */ gdk_error_trap_push (); device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), dev->id); if (gdk_error_trap_pop () || (device == NULL)) return ret; gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device, prop, 0, 1, False, XA_ATOM, &realtype, &realformat, &nitems, &bytes_after, &data); if (gdk_error_trap_pop () || rc != Success || realtype == None) ret = WACOM_TYPE_INVALID; xdevice_close (device); XFree (data); return ret; } /* Finds an output which matches the given EDID information. Any NULL * parameter will be interpreted to match any value. */ static GsdRROutput * find_output_by_edid (GsdRRScreen *rr_screen, const gchar *vendor, const gchar *product, const gchar *serial) { GsdRROutput **rr_outputs; GsdRROutput *retval = NULL; guint i; rr_outputs = gsd_rr_screen_list_outputs (rr_screen); for (i = 0; rr_outputs[i] != NULL; i++) { gchar *o_vendor_s; gchar *o_product_s; int o_product; gchar *o_serial_s; int o_serial; gboolean match; if (!gsd_rr_output_is_connected (rr_outputs[i])) continue; if (!gsd_rr_output_get_ids_from_edid (rr_outputs[i], &o_vendor_s, &o_product, &o_serial)) continue; o_product_s = g_strdup_printf ("%d", o_product); o_serial_s = g_strdup_printf ("%d", o_serial); g_debug ("Checking for match between '%s','%s','%s' and '%s','%s','%s'", \ vendor, product, serial, o_vendor_s, o_product_s, o_serial_s); match = (vendor == NULL || g_strcmp0 (vendor, o_vendor_s) == 0) && \ (product == NULL || g_strcmp0 (product, o_product_s) == 0) && \ (serial == NULL || g_strcmp0 (serial, o_serial_s) == 0); g_free (o_vendor_s); g_free (o_product_s); g_free (o_serial_s); if (match) { retval = rr_outputs[i]; break; } } if (retval == NULL) g_debug ("Did not find a matching output for EDID '%s,%s,%s'", vendor, product, serial); return retval; } static GsdRROutput* find_builtin_output (GsdRRScreen *rr_screen) { GsdRROutput **rr_outputs; GsdRROutput *retval = NULL; guint i; rr_outputs = gsd_rr_screen_list_outputs (rr_screen); for (i = 0; rr_outputs[i] != NULL; i++) { if (!gsd_rr_output_is_connected (rr_outputs[i])) continue; if (gsd_rr_output_is_laptop(rr_outputs[i])) { retval = rr_outputs[i]; break; } } if (retval == NULL) g_debug ("Did not find a built-in monitor"); return retval; } static GsdRROutput * find_output_by_heuristic (GsdRRScreen *rr_screen, GsdWacomDevice *device) { GsdRROutput *rr_output; /* TODO: This heuristic will fail for non-Wacom display * tablets and may give the wrong result if multiple Wacom * display tablets are connected. */ rr_output = find_output_by_edid (rr_screen, "WAC", NULL, NULL); if (!rr_output) rr_output = find_builtin_output (rr_screen); return rr_output; } static GsdRROutput * find_output_by_display (GsdRRScreen *rr_screen, GsdWacomDevice *device) { gsize n; GSettings *tablet; GVariant *display; const gchar **edid; GsdRROutput *ret; if (device == NULL) return NULL; ret = NULL; tablet = device->priv->wacom_settings; display = g_settings_get_value (tablet, "display"); edid = g_variant_get_strv (display, &n); if (n != 3) { g_critical ("Expected 'display' key to store %d values; got %"G_GSIZE_FORMAT".", 3, n); goto out; } if (strlen (edid[0]) == 0 || strlen (edid[1]) == 0 || strlen (edid[2]) == 0) goto out; ret = find_output_by_edid (rr_screen, edid[0], edid[1], edid[2]); out: g_free (edid); g_variant_unref (display); return ret; } static gboolean is_on (GsdRROutput *output) { GsdRRCrtc *crtc; crtc = gsd_rr_output_get_crtc (output); if (!crtc) return FALSE; return gsd_rr_crtc_get_current_mode (crtc) != NULL; } static GsdRROutput * find_output_by_monitor (GsdRRScreen *rr_screen, GdkScreen *screen, int monitor) { GsdRROutput **rr_outputs; GsdRROutput *ret; guint i; ret = NULL; rr_outputs = gsd_rr_screen_list_outputs (rr_screen); for (i = 0; rr_outputs[i] != NULL; i++) { GsdRROutput *rr_output; GsdRRCrtc *crtc; int x, y; rr_output = rr_outputs[i]; if (!is_on (rr_output)) continue; crtc = gsd_rr_output_get_crtc (rr_output); if (!crtc) continue; gsd_rr_crtc_get_position (crtc, &x, &y); if (monitor == gdk_screen_get_monitor_at_point (screen, x, y)) { ret = rr_output; break; } } if (ret == NULL) g_warning ("No output found for monitor %d.", monitor); return ret; } static void set_display_by_output (GsdWacomDevice *device, GsdRROutput *rr_output) { GSettings *tablet; GVariant *c_array; GVariant *n_array; gsize nvalues; gchar *o_vendor_s, *o_product_s, *o_serial_s; int o_product, o_serial; const gchar *values[3]; tablet = gsd_wacom_device_get_settings (device); c_array = g_settings_get_value (tablet, "display"); g_variant_get_strv (c_array, &nvalues); if (nvalues != 3) { g_warning ("Unable set set display property. Got %"G_GSIZE_FORMAT" items; expected %d items.\n", nvalues, 4); return; } if (rr_output == NULL || !gsd_rr_output_get_ids_from_edid (rr_output, &o_vendor_s, &o_product, &o_serial)) { o_vendor_s = g_strdup (""); o_product_s = g_strdup (""); o_serial_s = g_strdup (""); } else { o_product_s = g_strdup_printf ("%d", o_product); o_serial_s = g_strdup_printf ("%d", o_serial); } values[0] = o_vendor_s; values[1] = o_product_s; values[2] = o_serial_s; n_array = g_variant_new_strv ((const gchar * const *) &values, 3); g_settings_set_value (tablet, "display", n_array); g_free (o_vendor_s); g_free (o_product_s); g_free (o_serial_s); } static GsdWacomRotation get_rotation_wacom (GsdRRRotation rotation) { guint i; for (i = 0; i < G_N_ELEMENTS (rotation_table); i++) { if (rotation_table[i].rotation & rotation) return (rotation_table[i].rotation_wacom); } g_assert_not_reached (); } void gsd_wacom_device_set_display (GsdWacomDevice *device, int monitor) { GError *error = NULL; GsdRRScreen *rr_screen; GsdRROutput *output = NULL; g_return_if_fail (GSD_IS_WACOM_DEVICE (device)); rr_screen = gsd_rr_screen_new (gdk_screen_get_default (), &error); if (rr_screen == NULL) { g_warning ("Failed to create GsdRRScreen: %s", error->message); g_error_free (error); return; } if (monitor > GSD_WACOM_SET_ALL_MONITORS) output = find_output_by_monitor (rr_screen, gdk_screen_get_default (), monitor); set_display_by_output (device, output); g_object_unref (rr_screen); } static GsdRROutput * find_output (GsdRRScreen *rr_screen, GsdWacomDevice *device) { GsdRROutput *rr_output; rr_output = find_output_by_display (rr_screen, device); if (rr_output == NULL) { if (gsd_wacom_device_is_screen_tablet (device)) { rr_output = find_output_by_heuristic (rr_screen, device); if (rr_output == NULL) g_warning ("No fuzzy match based on heuristics was found."); else g_warning ("Automatically mapping tablet to heuristically-found display."); } } return rr_output; } static void calculate_transformation_matrix (const GdkRectangle mapped, const GdkRectangle desktop, float matrix[NUM_ELEMS_MATRIX]) { float x_scale = (float)mapped.x / desktop.width; float y_scale = (float)mapped.y / desktop.height; float width_scale = (float)mapped.width / desktop.width; float height_scale = (float)mapped.height / desktop.height; matrix[0] = width_scale; matrix[1] = 0.0f; matrix[2] = x_scale; matrix[3] = 0.0f; matrix[4] = height_scale; matrix[5] = y_scale; matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f; g_debug ("Matrix is %f,%f,%f,%f,%f,%f,%f,%f,%f.", matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6], matrix[7], matrix[8]); return; } int gsd_wacom_device_get_display_monitor (GsdWacomDevice *device) { GError *error = NULL; GsdRRScreen *rr_screen; GsdRROutput *rr_output; GsdRRMode *mode; GsdRRCrtc *crtc; gint area[4]; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), GSD_WACOM_SET_ALL_MONITORS); rr_screen = gsd_rr_screen_new (gdk_screen_get_default (), &error); if (rr_screen == NULL) { g_warning ("Failed to create GsdRRScreen: %s", error->message); g_error_free (error); return GSD_WACOM_SET_ALL_MONITORS; } rr_output = find_output (rr_screen, device); if (rr_output == NULL) { g_object_unref (rr_screen); return GSD_WACOM_SET_ALL_MONITORS; } if (!is_on (rr_output)) { g_warning ("Output is not active."); g_object_unref (rr_screen); return GSD_WACOM_SET_ALL_MONITORS; } crtc = gsd_rr_output_get_crtc (rr_output); gsd_rr_crtc_get_position (crtc, &area[0], &area[1]); mode = gsd_rr_crtc_get_current_mode (crtc); area[2] = gsd_rr_mode_get_width (mode); area[3] = gsd_rr_mode_get_height (mode); g_object_unref (rr_screen); if (area[2] <= 0 || area[3] <= 0) { g_warning ("Output has non-positive area."); return GSD_WACOM_SET_ALL_MONITORS; } g_debug ("Area: %d,%d %dx%d", area[0], area[1], area[2], area[3]); return gdk_screen_get_monitor_at_point (gdk_screen_get_default (), area[0], area[1]); } gboolean gsd_wacom_device_get_display_matrix (GsdWacomDevice *device, float matrix[NUM_ELEMS_MATRIX]) { int monitor; GdkRectangle display; GdkRectangle desktop; GdkScreen *screen = gdk_screen_get_default (); matrix[0] = 1.0f; matrix[1] = 0.0f; matrix[2] = 0.0f; matrix[3] = 0.0f; matrix[4] = 1.0f; matrix[5] = 0.0f; matrix[6] = 0.0f; matrix[7] = 0.0f; matrix[8] = 1.0f; monitor = gsd_wacom_device_get_display_monitor (device); if (monitor < 0) return FALSE; desktop.x = 0; desktop.y = 0; desktop.width = gdk_screen_get_width (screen); desktop.height = gdk_screen_get_height (screen); gdk_screen_get_monitor_geometry (screen, monitor, &display); calculate_transformation_matrix (display, desktop, matrix); return TRUE; } GsdWacomRotation gsd_wacom_device_get_display_rotation (GsdWacomDevice *device) { GError *error = NULL; GsdRRScreen *rr_screen; GsdRROutput *rr_output; GsdRRRotation rotation = GSD_RR_ROTATION_0; rr_screen = gsd_rr_screen_new (gdk_screen_get_default (), &error); if (rr_screen == NULL) { g_warning ("Failed to create GsdRRScreen: %s", error->message); g_error_free (error); return GSD_WACOM_ROTATION_NONE; } rr_output = find_output (rr_screen, device); if (rr_output) { GsdRRCrtc *crtc = gsd_rr_output_get_crtc (rr_output); if (crtc) rotation = gsd_rr_crtc_get_current_rotation (crtc); } g_object_unref (rr_screen); return get_rotation_wacom (rotation); } static void add_stylus_to_device (GsdWacomDevice *device, const char *settings_path, int id) { const WacomStylus *wstylus; wstylus = libwacom_stylus_get_for_id (db, id); if (wstylus) { GsdWacomStylus *stylus; char *stylus_settings_path; GSettings *settings; if (device->priv->type == WACOM_TYPE_STYLUS && libwacom_stylus_is_eraser (wstylus)) return; if (device->priv->type == WACOM_TYPE_ERASER && libwacom_stylus_is_eraser (wstylus) == FALSE) return; stylus_settings_path = g_strdup_printf ("%s0x%x/", settings_path, id); if (device->priv->type == WACOM_TYPE_STYLUS) { settings = g_settings_new_with_path (WACOM_STYLUS_SCHEMA, stylus_settings_path); stylus = gsd_wacom_stylus_new (device, wstylus, settings); } else { settings = g_settings_new_with_path (WACOM_ERASER_SCHEMA, stylus_settings_path); stylus = gsd_wacom_stylus_new (device, wstylus, settings); } g_free (stylus_settings_path); device->priv->styli = g_list_prepend (device->priv->styli, stylus); } } int gsd_wacom_device_get_num_modes (GsdWacomDevice *device, int group_id) { int num_modes; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1); num_modes = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->num_modes, GINT_TO_POINTER(group_id))); return num_modes; } int gsd_wacom_device_get_current_mode (GsdWacomDevice *device, int group_id) { int current_idx; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1); current_idx = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER(group_id))); /* That means that the mode doesn't exist, see gsd_wacom_device_add_modes() */ g_return_val_if_fail (current_idx != 0, -1); return current_idx; } int gsd_wacom_device_set_next_mode (GsdWacomDevice *device, GsdWacomTabletButton *button) { GList *l; int current_idx; int num_modes; int num_switches; int group_id; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), -1); group_id = button->group_id; current_idx = 0; num_switches = 0; num_modes = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->num_modes, GINT_TO_POINTER(group_id))); /* * Check if we have multiple mode-switch buttons for that * group, and if so, compute the current index based on * the position in the list... */ for (l = device->priv->buttons; l != NULL; l = l->next) { GsdWacomTabletButton *b = l->data; if (b->type != WACOM_TABLET_BUTTON_TYPE_HARDCODED) continue; if (button->group_id == b->group_id) num_switches++; if (g_strcmp0 (button->id, b->id) == 0) current_idx = num_switches; } /* We should at least have found the current mode-switch button... * If not, then it means that the given button is not a valid * mode-switch. */ g_return_val_if_fail (num_switches != 0, -1); /* Only one mode-switch? cycle through the modes */ if (num_switches == 1) { current_idx = gsd_wacom_device_get_current_mode (device, group_id); /* gsd_wacom_device_get_current_mode() returns -1 when the mode doesn't exist */ g_return_val_if_fail (current_idx > 0, -1); current_idx++; } if (current_idx > num_modes) current_idx = 1; g_hash_table_insert (device->priv->modes, GINT_TO_POINTER (group_id), GINT_TO_POINTER (current_idx)); return current_idx; } static int flags_to_group (WacomButtonFlags flags) { if (flags & WACOM_BUTTON_RING_MODESWITCH) return 1; if (flags & WACOM_BUTTON_RING2_MODESWITCH) return 2; if (flags & WACOM_BUTTON_TOUCHSTRIP_MODESWITCH) return 3; if (flags & WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH) return 4; return 0; } static GList * gsd_wacom_device_add_ring_modes (WacomDevice *wacom_device, const char *settings_path, WacomButtonFlags direction) { GList *l; guint num_modes; guint group; guint i; char *name, *id; l = NULL; if ((direction & WACOM_BUTTON_POSITION_LEFT) && libwacom_has_ring (wacom_device)) { num_modes = libwacom_get_ring_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_RING_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "left-ring-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Left Ring"), "left-ring-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_LEFT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Left Ring Mode #%d"), i); id = g_strdup_printf ("left-ring-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_LEFT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } else if ((direction & WACOM_BUTTON_POSITION_RIGHT) && libwacom_has_ring2 (wacom_device)) { num_modes = libwacom_get_ring2_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_RING2_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "right-ring-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Right Ring"), "right-ring-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_RIGHT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Right Ring Mode #%d"), i); id = g_strdup_printf ("right-ring-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_POS_RIGHT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } return l; } static GList * gsd_wacom_device_add_strip_modes (WacomDevice *wacom_device, const char *settings_path, WacomButtonFlags direction) { GList *l; guint num_modes; guint num_strips; guint group; guint i; char *name, *id; l = NULL; num_strips = libwacom_get_num_strips (wacom_device); if (num_strips > 2) g_warning ("Unhandled number of touchstrips: %d", num_strips); if ((direction & WACOM_BUTTON_POSITION_LEFT) && num_strips >= 1) { num_modes = libwacom_get_strips_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_TOUCHSTRIP_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "left-strip-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Left Touchstrip"), "left-strip-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_LEFT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Left Touchstrip Mode #%d"), i); id = g_strdup_printf ("left-strip-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_LEFT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } else if ((direction & WACOM_BUTTON_POSITION_RIGHT) && num_strips >= 2) { num_modes = libwacom_get_strips_num_modes (wacom_device); group = flags_to_group (WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH); if (num_modes == 0) { /* If no mode is available, we use "right-strip-mode-1" for backward compat */ l = g_list_append (l, gsd_wacom_tablet_button_new (_("Right Touchstrip"), "right-strip-mode-1", settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_RIGHT, group, 0, GSD_WACOM_NO_LED)); } else { for (i = 1; i <= num_modes; i++) { name = g_strdup_printf (_("Right Touchstrip Mode #%d"), i); id = g_strdup_printf ("right-strip-mode-%d", i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_POS_RIGHT, group, i - 1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } } } return l; } static char * gsd_wacom_device_modeswitch_name (WacomButtonFlags flags, guint button_num) { if (flags & WACOM_BUTTON_RINGS_MODESWITCH) { if (flags & WACOM_BUTTON_POSITION_LEFT) return g_strdup_printf (_("Left Touchring Mode Switch")); else return g_strdup_printf (_("Right Touchring Mode Switch")); } else if (flags & WACOM_BUTTON_TOUCHSTRIPS_MODESWITCH) { if (flags & WACOM_BUTTON_POSITION_LEFT) return g_strdup_printf (_("Left Touchstrip Mode Switch")); else return g_strdup_printf (_("Right Touchstrip Mode Switch")); } g_warning ("Unhandled modeswitch and direction combination"); return g_strdup_printf (_("Mode Switch #%d"), button_num); } static GsdWacomTabletButtonType gsd_wacom_device_button_pos (WacomButtonFlags flags) { if (flags & WACOM_BUTTON_POSITION_LEFT) return WACOM_TABLET_BUTTON_POS_LEFT; else if (flags & WACOM_BUTTON_POSITION_RIGHT) return WACOM_TABLET_BUTTON_POS_RIGHT; else if (flags & WACOM_BUTTON_POSITION_TOP) return WACOM_TABLET_BUTTON_POS_TOP; else if (flags & WACOM_BUTTON_POSITION_BOTTOM) return WACOM_TABLET_BUTTON_POS_BOTTOM; g_warning ("Unhandled button position"); return WACOM_TABLET_BUTTON_POS_UNDEF; } static GList * gsd_wacom_device_add_buttons_dir (WacomDevice *wacom_device, const char *settings_path, WacomButtonFlags direction, const char *button_str, const char *button_str_id) { GList *l; guint num_buttons, i, button_num; char *name, *id; l = NULL; button_num = 1; num_buttons = libwacom_get_num_buttons (wacom_device); for (i = 'A'; i < 'A' + num_buttons; i++) { WacomButtonFlags flags; flags = libwacom_get_button_flag (wacom_device, i); if (!(flags & direction)) continue; /* Ignore mode switches */ if (flags & WACOM_BUTTON_MODESWITCH) continue; name = g_strdup_printf (button_str, button_num++); id = g_strdup_printf ("%s%c", button_str_id, i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_NORMAL, gsd_wacom_device_button_pos (flags), flags_to_group (flags), -1, GSD_WACOM_NO_LED)); g_free (name); g_free (id); } /* Handle modeswitches */ for (i = 'A'; i < 'A' + num_buttons; i++) { WacomButtonFlags flags; char *name, *id; int status_led; flags = libwacom_get_button_flag (wacom_device, i); if (!(flags & direction)) continue; /* Ignore non-mode switches */ if (!(flags & WACOM_BUTTON_MODESWITCH)) continue; name = gsd_wacom_device_modeswitch_name (flags, button_num++); id = g_strdup_printf ("%s%c", button_str_id, i); status_led = libwacom_get_button_led_group (wacom_device, i); l = g_list_append (l, gsd_wacom_tablet_button_new (name, id, settings_path, WACOM_TABLET_BUTTON_TYPE_HARDCODED, gsd_wacom_device_button_pos (flags), flags_to_group (flags), -1, status_led)); g_free (name); g_free (id); } /* Handle touch{strips,rings} */ if (libwacom_has_ring2 (wacom_device) || libwacom_has_ring (wacom_device)) l = g_list_concat (l, gsd_wacom_device_add_ring_modes (wacom_device, settings_path, direction)); if (libwacom_get_num_strips (wacom_device) > 0) l = g_list_concat (l, gsd_wacom_device_add_strip_modes (wacom_device, settings_path, direction)); return l; } static void gsd_wacom_device_add_buttons (GsdWacomDevice *device, WacomDevice *wacom_device, const char *settings_path) { GList *l, *ret; ret = NULL; l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_LEFT, _("Left Button #%d"), "button"); if (l) ret = l; l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_RIGHT, _("Right Button #%d"), "button"); if (l) ret = g_list_concat (ret, l); l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_TOP, _("Top Button #%d"), "button"); if (l) ret = g_list_concat (ret, l); l = gsd_wacom_device_add_buttons_dir (wacom_device, settings_path, WACOM_BUTTON_POSITION_BOTTOM, _("Bottom Button #%d"), "button"); if (l) ret = g_list_concat (ret, l); device->priv->buttons = ret; } static void gsd_wacom_device_get_modeswitches (WacomDevice *wacom_device, gint *num_rings, gint *num_strips) { *num_strips = libwacom_get_num_strips (wacom_device); if (libwacom_has_ring2 (wacom_device)) *num_rings = 2; else if (libwacom_has_ring (wacom_device)) *num_rings = 1; else *num_rings = 0; } static void gsd_wacom_device_add_modes (GsdWacomDevice *device, WacomDevice *wacom_device) { GList *l; device->priv->modes = g_hash_table_new (g_direct_hash, g_direct_equal); device->priv->num_modes = g_hash_table_new (g_direct_hash, g_direct_equal); for (l = device->priv->buttons; l != NULL; l = l->next) { GsdWacomTabletButton *button = l->data; if (button->group_id > 0) g_hash_table_insert (device->priv->modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (1)); /* See flags_to_group() for group ID/button type matches */ if (button->group_id == 1) { g_hash_table_insert (device->priv->num_modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (libwacom_get_ring_num_modes (wacom_device))); } else if (button->group_id == 2) { g_hash_table_insert (device->priv->num_modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (libwacom_get_ring2_num_modes (wacom_device))); } else if (button->group_id == 3 || button->group_id == 4) { g_hash_table_insert (device->priv->num_modes, GINT_TO_POINTER (button->group_id), GINT_TO_POINTER (libwacom_get_strips_num_modes (wacom_device))); } } } static void gsd_wacom_device_update_from_db (GsdWacomDevice *device, WacomDevice *wacom_device, const char *identifier) { char *settings_path; WacomIntegrationFlags integration_flags; settings_path = g_strdup_printf (WACOM_DEVICE_CONFIG_BASE, device->priv->machine_id, libwacom_get_match (wacom_device)); device->priv->wacom_settings = g_settings_new_with_path (WACOM_TABLET_SCHEMA, settings_path); device->priv->name = g_strdup (libwacom_get_name (wacom_device)); device->priv->layout_path = g_strdup (libwacom_get_layout_filename (wacom_device)); device->priv->reversible = libwacom_is_reversible (wacom_device); integration_flags = libwacom_get_integration_flags (wacom_device); device->priv->is_screen_tablet = (integration_flags & WACOM_DEVICE_INTEGRATED_DISPLAY); device->priv->is_isd = (integration_flags & WACOM_DEVICE_INTEGRATED_SYSTEM); if (device->priv->is_screen_tablet) { if (!device->priv->is_isd) device->priv->icon_name = "wacom-tablet-cintiq"; else device->priv->icon_name = "wacom-tablet-pc"; } else { device->priv->icon_name = "wacom-tablet"; } if (device->priv->type == WACOM_TYPE_PAD) { gsd_wacom_device_get_modeswitches (wacom_device, &device->priv->num_rings, &device->priv->num_strips); gsd_wacom_device_add_buttons (device, wacom_device, settings_path); gsd_wacom_device_add_modes (device, wacom_device); } if (device->priv->type == WACOM_TYPE_STYLUS || device->priv->type == WACOM_TYPE_ERASER) { const int *ids; int num_styli; guint i; ids = libwacom_get_supported_styli (wacom_device, &num_styli); g_assert (num_styli >= 1); for (i = 0; i < num_styli; i++) add_stylus_to_device (device, settings_path, ids[i]); device->priv->styli = g_list_reverse (device->priv->styli); } g_free (settings_path); } static GObject * gsd_wacom_device_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GsdWacomDevice *device; GdkDeviceManager *device_manager; XDeviceInfo *device_info; WacomDevice *wacom_device; int n_devices; guint i; device = GSD_WACOM_DEVICE (G_OBJECT_CLASS (gsd_wacom_device_parent_class)->constructor (type, n_construct_properties, construct_properties)); if (device->priv->gdk_device == NULL) return G_OBJECT (device); device_manager = gdk_display_get_device_manager (gdk_display_get_default ()); g_object_get (device_manager, "opcode", &device->priv->opcode, NULL); g_object_get (device->priv->gdk_device, "device-id", &device->priv->device_id, NULL); device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices); if (device_info == NULL) { g_warning ("Could not list any input devices through XListInputDevices()"); goto end; } for (i = 0; i < n_devices; i++) { if (device_info[i].id == device->priv->device_id) { device->priv->type = get_device_type (&device_info[i]); device->priv->tool_name = g_strdup (device_info[i].name); break; } } XFreeDeviceList (device_info); if (device->priv->type == WACOM_TYPE_INVALID) goto end; device->priv->path = xdevice_get_device_node (device->priv->device_id); if (device->priv->path == NULL) { g_warning ("Could not get the device node path for ID '%d'", device->priv->device_id); device->priv->type = WACOM_TYPE_INVALID; goto end; } if (db == NULL) db = libwacom_database_new (); wacom_device = libwacom_new_from_path (db, device->priv->path, FALSE, NULL); if (!wacom_device) { WacomError *wacom_error; g_debug ("Creating fallback driver for wacom tablet '%s' ('%s')", gdk_device_get_name (device->priv->gdk_device), device->priv->path); device->priv->is_fallback = TRUE; wacom_error = libwacom_error_new (); wacom_device = libwacom_new_from_path (db, device->priv->path, TRUE, wacom_error); if (wacom_device == NULL) { g_warning ("Failed to create fallback wacom device for '%s': %s (%d)", device->priv->path, libwacom_error_get_message (wacom_error), libwacom_error_get_code (wacom_error)); libwacom_error_free (&wacom_error); device->priv->type = WACOM_TYPE_INVALID; goto end; } } gsd_wacom_device_update_from_db (device, wacom_device, device->priv->path); libwacom_destroy (wacom_device); if (device->priv->type == WACOM_TYPE_STYLUS || device->priv->type == WACOM_TYPE_ERASER) { setup_property_notify (device); } end: return G_OBJECT (device); } static void gsd_wacom_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GsdWacomDevice *device; device = GSD_WACOM_DEVICE (object); switch (prop_id) { case PROP_GDK_DEVICE: device->priv->gdk_device = g_value_get_pointer (value); break; case PROP_LAST_STYLUS: device->priv->last_stylus = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_wacom_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsdWacomDevice *device; device = GSD_WACOM_DEVICE (object); switch (prop_id) { case PROP_GDK_DEVICE: g_value_set_pointer (value, device->priv->gdk_device); break; case PROP_LAST_STYLUS: g_value_set_pointer (value, device->priv->last_stylus); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsd_wacom_device_class_init (GsdWacomDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = gsd_wacom_device_constructor; object_class->finalize = gsd_wacom_device_finalize; object_class->set_property = gsd_wacom_device_set_property; object_class->get_property = gsd_wacom_device_get_property; g_type_class_add_private (klass, sizeof (GsdWacomDevicePrivate)); g_object_class_install_property (object_class, PROP_GDK_DEVICE, g_param_spec_pointer ("gdk-device", "gdk-device", "gdk-device", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_LAST_STYLUS, g_param_spec_pointer ("last-stylus", "last-stylus", "last-stylus", G_PARAM_READWRITE)); } static void gsd_wacom_device_init (GsdWacomDevice *device) { device->priv = GSD_WACOM_DEVICE_GET_PRIVATE (device); device->priv->type = WACOM_TYPE_INVALID; if (g_file_get_contents ("/etc/machine-id", &device->priv->machine_id, NULL, NULL) == FALSE) if (g_file_get_contents ("/var/lib/dbus/machine-id", &device->priv->machine_id, NULL, NULL) == FALSE) device->priv->machine_id = g_strdup ("00000000000000000000000000000000"); device->priv->machine_id = g_strstrip (device->priv->machine_id); } static void gsd_wacom_device_finalize (GObject *object) { GsdWacomDevice *device; GsdWacomDevicePrivate *p; g_return_if_fail (object != NULL); g_return_if_fail (GSD_IS_WACOM_DEVICE (object)); device = GSD_WACOM_DEVICE (object); g_return_if_fail (device->priv != NULL); p = device->priv; if (p->wacom_settings != NULL) { g_object_unref (p->wacom_settings); p->wacom_settings = NULL; } g_list_foreach (p->styli, (GFunc) g_object_unref, NULL); g_list_free (p->styli); g_list_foreach (p->buttons, (GFunc) gsd_wacom_tablet_button_free, NULL); g_list_free (p->buttons); g_free (p->name); p->name = NULL; g_free (p->tool_name); p->tool_name = NULL; g_free (p->path); p->path = NULL; g_free (p->machine_id); p->machine_id = NULL; if (p->modes) { g_hash_table_destroy (p->modes); p->modes = NULL; } if (p->num_modes) { g_hash_table_destroy (p->num_modes); p->num_modes = NULL; } g_clear_pointer (&p->layout_path, g_free); gdk_window_remove_filter (NULL, (GdkFilterFunc) filter_events, device); G_OBJECT_CLASS (gsd_wacom_device_parent_class)->finalize (object); } GsdWacomDevice * gsd_wacom_device_new (GdkDevice *device) { return GSD_WACOM_DEVICE (g_object_new (GSD_TYPE_WACOM_DEVICE, "gdk-device", device, NULL)); } GList * gsd_wacom_device_list_styli (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return g_list_copy (device->priv->styli); } GsdWacomStylus * gsd_wacom_device_get_stylus_for_type (GsdWacomDevice *device, GsdWacomStylusType type) { GList *l; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); for (l = device->priv->styli; l != NULL; l = l->next) { GsdWacomStylus *stylus = l->data; if (gsd_wacom_stylus_get_stylus_type (stylus) == type) return stylus; } return NULL; } const char * gsd_wacom_device_get_name (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->name; } const char * gsd_wacom_device_get_layout_path (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->layout_path; } const char * gsd_wacom_device_get_path (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->path; } const char * gsd_wacom_device_get_icon_name (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->icon_name; } const char * gsd_wacom_device_get_tool_name (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->tool_name; } gboolean gsd_wacom_device_reversible (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->reversible; } gboolean gsd_wacom_device_is_screen_tablet (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->is_screen_tablet; } gboolean gsd_wacom_device_is_isd (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->is_isd; } gboolean gsd_wacom_device_is_fallback (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), FALSE); return device->priv->is_fallback; } gint gsd_wacom_device_get_num_strips (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), 0); return device->priv->num_strips; } gint gsd_wacom_device_get_num_rings (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), 0); return device->priv->num_rings; } GSettings * gsd_wacom_device_get_settings (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return device->priv->wacom_settings; } void gsd_wacom_device_set_current_stylus (GsdWacomDevice *device, int stylus_id) { GList *l; GsdWacomStylus *stylus; g_return_if_fail (GSD_IS_WACOM_DEVICE (device)); /* Don't change anything if the stylus is already set */ if (device->priv->last_stylus != NULL) { GsdWacomStylus *stylus = device->priv->last_stylus; if (stylus->priv->id == stylus_id) return; } for (l = device->priv->styli; l; l = l->next) { stylus = l->data; /* Set a nice default if 0x0 */ if (stylus_id == 0x0 && stylus->priv->type == WSTYLUS_GENERAL) { g_object_set (device, "last-stylus", stylus, NULL); return; } if (stylus->priv->id == stylus_id) { g_object_set (device, "last-stylus", stylus, NULL); return; } } /* Setting the default stylus to be the generic one */ for (l = device->priv->styli; l; l = l->next) { stylus = l->data; /* Set a nice default if 0x0 */ if (stylus->priv->type == WSTYLUS_GENERAL) { g_debug ("Could not find stylus ID 0x%x for tablet '%s', setting general pen ID 0x%x instead", stylus_id, device->priv->name, stylus->priv->id); g_object_set (device, "last-stylus", stylus, NULL); return; } } g_warning ("Could not set the current stylus ID 0x%x for tablet '%s', no general pen found", stylus_id, device->priv->name); /* Setting the default stylus to be the first one */ g_assert (device->priv->styli); stylus = device->priv->styli->data; g_object_set (device, "last-stylus", stylus, NULL); } GsdWacomDeviceType gsd_wacom_device_get_device_type (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), WACOM_TYPE_INVALID); return device->priv->type; } gint * gsd_wacom_device_get_area (GsdWacomDevice *device) { int i, id; XDevice *xdevice; Atom area, realtype; int rc, realformat; unsigned long nitems, bytes_after; unsigned char *data = NULL; gint *device_area; g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); g_object_get (device->priv->gdk_device, "device-id", &id, NULL); area = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Wacom Tablet Area", False); gdk_error_trap_push (); xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id); if (gdk_error_trap_pop () || (device == NULL)) return NULL; gdk_error_trap_push (); rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, area, 0, 4, False, XA_INTEGER, &realtype, &realformat, &nitems, &bytes_after, &data); if (gdk_error_trap_pop () || rc != Success || realtype == None || bytes_after != 0 || nitems != 4) { xdevice_close (xdevice); return NULL; } device_area = g_new0 (int, nitems); for (i = 0; i < nitems; i++) device_area[i] = ((long *)data)[i]; XFree (data); xdevice_close (xdevice); return device_area; } const char * gsd_wacom_device_type_to_string (GsdWacomDeviceType type) { switch (type) { case WACOM_TYPE_INVALID: return "Invalid"; case WACOM_TYPE_STYLUS: return "Stylus"; case WACOM_TYPE_ERASER: return "Eraser"; case WACOM_TYPE_CURSOR: return "Cursor"; case WACOM_TYPE_PAD: return "Pad"; case WACOM_TYPE_TOUCH: return "Touch"; default: return "Unknown type"; } } GList * gsd_wacom_device_get_buttons (GsdWacomDevice *device) { g_return_val_if_fail (GSD_IS_WACOM_DEVICE (device), NULL); return g_list_copy (device->priv->buttons); } static GsdWacomTabletButton * find_button_with_id (GsdWacomDevice *device, const char *id) { GList *l; for (l = device->priv->buttons; l != NULL; l = l->next) { GsdWacomTabletButton *button = l->data; if (g_strcmp0 (button->id, id) == 0) return button; } return NULL; } static GsdWacomTabletButton * find_button_with_index (GsdWacomDevice *device, const char *id, int index) { GsdWacomTabletButton *button; char *str; str = g_strdup_printf ("%s-mode-%d", id, index); button = find_button_with_id (device, str); g_free (str); return button; } GsdWacomTabletButton * gsd_wacom_device_get_button (GsdWacomDevice *device, int button, GtkDirectionType *dir) { int index; if (button <= 26) { char *id; GsdWacomTabletButton *ret; int physical_button; /* mouse_button = physical_button < 4 ? physical_button : physical_button + 4 */ if (button > 4) physical_button = button - 4; else physical_button = button; id = g_strdup_printf ("button%c", 'A' + physical_button - 1); ret = find_button_with_id (device, id); g_free (id); return ret; } switch (button) { case 90: case 92: case 94: case 96: *dir = GTK_DIR_UP; break; case 91: case 93: case 95: case 97: *dir = GTK_DIR_DOWN; break; default: ;; } /* The group ID is implied by the button number */ switch (button) { case 90: case 91: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (1))); return find_button_with_index (device, "left-ring", index); case 92: case 93: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (2))); return find_button_with_index (device, "right-ring", index); case 94: case 95: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (3))); return find_button_with_index (device, "left-strip", index); case 96: case 97: index = GPOINTER_TO_INT (g_hash_table_lookup (device->priv->modes, GINT_TO_POINTER (4))); return find_button_with_index (device, "right-strip", index); default: return NULL; } } GsdWacomRotation gsd_wacom_device_rotation_name_to_type (const char *rotation) { guint i; g_return_val_if_fail (rotation != NULL, GSD_WACOM_ROTATION_NONE); for (i = 0; i < G_N_ELEMENTS (rotation_table); i++) { if (strcmp (rotation_table[i].rotation_string, rotation) == 0) return (rotation_table[i].rotation_wacom); } return GSD_WACOM_ROTATION_NONE; } const char * gsd_wacom_device_rotation_type_to_name (GsdWacomRotation type) { guint i; for (i = 0; i < G_N_ELEMENTS (rotation_table); i++) { if (rotation_table[i].rotation_wacom == type) return (rotation_table[i].rotation_string); } return "none"; } GsdWacomDevice * gsd_wacom_device_create_fake (GsdWacomDeviceType type, const char *name, const char *tool_name) { GsdWacomDevice *device; GsdWacomDevicePrivate *priv; WacomDevice *wacom_device; device = GSD_WACOM_DEVICE (g_object_new (GSD_TYPE_WACOM_DEVICE, NULL)); if (db == NULL) db = libwacom_database_new (); wacom_device = libwacom_new_from_name (db, name, NULL); if (wacom_device == NULL) return NULL; priv = device->priv; priv->type = type; priv->tool_name = g_strdup (tool_name); gsd_wacom_device_update_from_db (device, wacom_device, name); libwacom_destroy (wacom_device); return device; } GList * gsd_wacom_device_create_fake_cintiq (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Cintiq 21UX2", "Wacom Cintiq 21UX2 stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Cintiq 21UX2", "Wacom Cintiq 21UX2 eraser"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_PAD, "Wacom Cintiq 21UX2", "Wacom Cintiq 21UX2 pad"); devices = g_list_prepend (devices, device); return devices; } GList * gsd_wacom_device_create_fake_bt (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Graphire Wireless", "Graphire Wireless stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Graphire Wireless", "Graphire Wireless eraser"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_PAD, "Wacom Graphire Wireless", "Graphire Wireless pad"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_CURSOR, "Wacom Graphire Wireless", "Graphire Wireless cursor"); devices = g_list_prepend (devices, device); return devices; } GList * gsd_wacom_device_create_fake_x201 (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Serial Tablet WACf004", "Wacom Serial Tablet WACf004 stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Serial Tablet WACf004", "Wacom Serial Tablet WACf004 eraser"); devices = g_list_prepend (devices, device); return devices; } GList * gsd_wacom_device_create_fake_intuos4 (void) { GsdWacomDevice *device; GList *devices; device = gsd_wacom_device_create_fake (WACOM_TYPE_STYLUS, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 stylus"); devices = g_list_prepend (NULL, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_ERASER, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 eraser"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_PAD, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 pad"); devices = g_list_prepend (devices, device); device = gsd_wacom_device_create_fake (WACOM_TYPE_CURSOR, "Wacom Intuos4 6x9", "Wacom Intuos4 6x9 cursor"); devices = g_list_prepend (devices, device); return devices; } ./plugins/wacom/gsd-wacom-device.h0000644000004100000410000002041212735467744017343 0ustar www-datawww-data/* * 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 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. * * Author: Bastien Nocera * */ #ifndef __GSD_WACOM_DEVICE_MANAGER_H #define __GSD_WACOM_DEVICE_MANAGER_H #include #include "gsd-enums.h" G_BEGIN_DECLS #define NUM_ELEMS_MATRIX 9 #define GSD_TYPE_WACOM_DEVICE (gsd_wacom_device_get_type ()) #define GSD_WACOM_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_WACOM_DEVICE, GsdWacomDevice)) #define GSD_WACOM_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_WACOM_DEVICE, GsdWacomDeviceClass)) #define GSD_IS_WACOM_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_WACOM_DEVICE)) #define GSD_IS_WACOM_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_WACOM_DEVICE)) #define GSD_WACOM_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_WACOM_DEVICE, GsdWacomDeviceClass)) typedef struct GsdWacomDevicePrivate GsdWacomDevicePrivate; typedef struct { GObject parent; GsdWacomDevicePrivate *priv; } GsdWacomDevice; typedef struct { GObjectClass parent_class; } GsdWacomDeviceClass; #define GSD_TYPE_WACOM_STYLUS (gsd_wacom_stylus_get_type ()) #define GSD_WACOM_STYLUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_WACOM_STYLUS, GsdWacomStylus)) #define GSD_WACOM_STYLUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_WACOM_STYLUS, GsdWacomStylusClass)) #define GSD_IS_WACOM_STYLUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_WACOM_STYLUS)) #define GSD_IS_WACOM_STYLUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_WACOM_STYLUS)) #define GSD_WACOM_STYLUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_WACOM_STYLUS, GsdWacomStylusClass)) typedef struct GsdWacomStylusPrivate GsdWacomStylusPrivate; typedef struct { GObject parent; GsdWacomStylusPrivate *priv; } GsdWacomStylus; typedef struct { GObjectClass parent_class; } GsdWacomStylusClass; typedef enum { WACOM_STYLUS_TYPE_UNKNOWN, WACOM_STYLUS_TYPE_GENERAL, WACOM_STYLUS_TYPE_INKING, WACOM_STYLUS_TYPE_AIRBRUSH, WACOM_STYLUS_TYPE_CLASSIC, WACOM_STYLUS_TYPE_MARKER, WACOM_STYLUS_TYPE_STROKE, WACOM_STYLUS_TYPE_PUCK } GsdWacomStylusType; GType gsd_wacom_stylus_get_type (void); GSettings * gsd_wacom_stylus_get_settings (GsdWacomStylus *stylus); const char * gsd_wacom_stylus_get_name (GsdWacomStylus *stylus); const char * gsd_wacom_stylus_get_icon_name (GsdWacomStylus *stylus); GsdWacomDevice * gsd_wacom_stylus_get_device (GsdWacomStylus *stylus); gboolean gsd_wacom_stylus_get_has_eraser (GsdWacomStylus *stylus); guint gsd_wacom_stylus_get_num_buttons(GsdWacomStylus *stylus); int gsd_wacom_stylus_get_id (GsdWacomStylus *stylus); GsdWacomStylusType gsd_wacom_stylus_get_stylus_type (GsdWacomStylus *stylus); /* Tablet Buttons */ typedef enum { WACOM_TABLET_BUTTON_TYPE_NORMAL, WACOM_TABLET_BUTTON_TYPE_STRIP, WACOM_TABLET_BUTTON_TYPE_RING, WACOM_TABLET_BUTTON_TYPE_HARDCODED } GsdWacomTabletButtonType; /* * Positions of the buttons on the tablet in default right-handed mode * (ie with no rotation applied). */ typedef enum { WACOM_TABLET_BUTTON_POS_UNDEF = 0, WACOM_TABLET_BUTTON_POS_LEFT, WACOM_TABLET_BUTTON_POS_RIGHT, WACOM_TABLET_BUTTON_POS_TOP, WACOM_TABLET_BUTTON_POS_BOTTOM } GsdWacomTabletButtonPos; #define MAX_GROUP_ID 4 #define GSD_WACOM_NO_LED -1 typedef struct { char *name; char *id; GSettings *settings; GsdWacomTabletButtonType type; GsdWacomTabletButtonPos pos; int group_id, idx; int status_led; } GsdWacomTabletButton; void gsd_wacom_tablet_button_free (GsdWacomTabletButton *button); GsdWacomTabletButton *gsd_wacom_tablet_button_copy (GsdWacomTabletButton *button); /* Device types to apply a setting to */ typedef enum { WACOM_TYPE_INVALID = 0, WACOM_TYPE_STYLUS = (1 << 1), WACOM_TYPE_ERASER = (1 << 2), WACOM_TYPE_CURSOR = (1 << 3), WACOM_TYPE_PAD = (1 << 4), WACOM_TYPE_TOUCH = (1 << 5), WACOM_TYPE_ALL = WACOM_TYPE_STYLUS | WACOM_TYPE_ERASER | WACOM_TYPE_CURSOR | WACOM_TYPE_PAD | WACOM_TYPE_TOUCH } GsdWacomDeviceType; /* We use -1 for entire screen when setting/getting monitor value */ #define GSD_WACOM_SET_ALL_MONITORS -1 GType gsd_wacom_device_get_type (void); void gsd_wacom_device_set_display (GsdWacomDevice *device, int monitor); gint gsd_wacom_device_get_display_monitor (GsdWacomDevice *device); gboolean gsd_wacom_device_get_display_matrix (GsdWacomDevice *device, float matrix[NUM_ELEMS_MATRIX]); GsdWacomRotation gsd_wacom_device_get_display_rotation (GsdWacomDevice *device); GsdWacomDevice * gsd_wacom_device_new (GdkDevice *device); GList * gsd_wacom_device_list_styli (GsdWacomDevice *device); const char * gsd_wacom_device_get_name (GsdWacomDevice *device); const char * gsd_wacom_device_get_layout_path (GsdWacomDevice *device); const char * gsd_wacom_device_get_path (GsdWacomDevice *device); const char * gsd_wacom_device_get_icon_name (GsdWacomDevice *device); const char * gsd_wacom_device_get_tool_name (GsdWacomDevice *device); gboolean gsd_wacom_device_reversible (GsdWacomDevice *device); gboolean gsd_wacom_device_is_screen_tablet (GsdWacomDevice *device); gboolean gsd_wacom_device_is_isd (GsdWacomDevice *device); gboolean gsd_wacom_device_is_fallback (GsdWacomDevice *device); gint gsd_wacom_device_get_num_strips (GsdWacomDevice *device); gint gsd_wacom_device_get_num_rings (GsdWacomDevice *device); GSettings * gsd_wacom_device_get_settings (GsdWacomDevice *device); void gsd_wacom_device_set_current_stylus (GsdWacomDevice *device, int stylus_id); GsdWacomStylus * gsd_wacom_device_get_stylus_for_type (GsdWacomDevice *device, GsdWacomStylusType type); GsdWacomDeviceType gsd_wacom_device_get_device_type (GsdWacomDevice *device); gint * gsd_wacom_device_get_area (GsdWacomDevice *device); const char * gsd_wacom_device_type_to_string (GsdWacomDeviceType type); GList * gsd_wacom_device_get_buttons (GsdWacomDevice *device); GsdWacomTabletButton *gsd_wacom_device_get_button (GsdWacomDevice *device, int button, GtkDirectionType *dir); int gsd_wacom_device_get_num_modes (GsdWacomDevice *device, int group_id); int gsd_wacom_device_get_current_mode (GsdWacomDevice *device, int group_id); int gsd_wacom_device_set_next_mode (GsdWacomDevice *device, GsdWacomTabletButton *button); GsdWacomRotation gsd_wacom_device_rotation_name_to_type (const char *rotation); const char * gsd_wacom_device_rotation_type_to_name (GsdWacomRotation type); /* Helper and debug functions */ GsdWacomDevice * gsd_wacom_device_create_fake (GsdWacomDeviceType type, const char *name, const char *tool_name); GList * gsd_wacom_device_create_fake_cintiq (void); GList * gsd_wacom_device_create_fake_bt (void); GList * gsd_wacom_device_create_fake_x201 (void); GList * gsd_wacom_device_create_fake_intuos4 (void); G_END_DECLS #endif /* __GSD_WACOM_DEVICE_MANAGER_H */ ./plugins/wacom/tablet-layout.css0000644000004100000410000000162512735467744017361 0ustar www-datawww-data ./plugins/wacom/wacom.gresource.xml0000644000004100000410000000026512735467744017705 0ustar www-datawww-data tablet-layout.css ./plugins/wacom/README.config-storage0000644000004100000410000000323512735467744017646 0ustar www-datawww-dataConfiguration storage for Wacom tablets and styli Tablets ------- Configuration is stored on a per-device model basis, meaning that it's possible to use the same device, with same configuration on 2 machines with a shared home directory, or replace a malfunctioning device with the same model and have the same configuration. It does not allow having 2 separate tablets of the same model to have different configurations, whether on a single machine, or using a shared home directory. The configuration scheme is: schema: org.gnome.settings-daemon.peripherals.wacom path: /org/gnome/settings-daemon/peripherals/wacom/-/ where is the D-Bus machine-id for the machine, and is a unique identifier for the tablet. Stylus ------ Styli use a similar configuration scheme. The identifier for each stylus is the tool ID, for professional ranges, and a generic identifier for the consumer ranges that do not support tool ID. schema: org.gnome.settings-daemon.peripherals.wacom.stylus or: org.gnome.settings-daemon.peripherals.wacom.eraser path: /org/gnome/settings-daemon/peripherals/wacom/// So each tool can be configured per tablet (so the compatible airbrush stylus will have different configurations on a Cintiq and an Intuos tablet) Buttons ------- schema: org.gnome.settings-daemon.peripherals.wacom.tablet-button path: /org/gnome/settings-daemon/peripherals/wacom//