From a08530e838218b3c5859550058e78b8f1abee434 Mon Sep 17 00:00:00 2001 From: William Jon McCann Date: Wed, 25 Oct 2006 14:38:19 -0400 Subject: Initial import --- src/ck-seat.c | 724 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 724 insertions(+) create mode 100644 src/ck-seat.c (limited to 'src/ck-seat.c') diff --git a/src/ck-seat.c b/src/ck-seat.c new file mode 100644 index 0000000..fa73c40 --- /dev/null +++ b/src/ck-seat.c @@ -0,0 +1,724 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2006 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 + +#define DBUS_API_SUBJECT_TO_CHANGE +#include +#include + +#include "ck-seat.h" +#include "ck-seat-glue.h" +#include "ck-marshal.h" + +#include "ck-session.h" +#include "ck-vt-monitor.h" +#include "ck-debug.h" + +#define CK_SEAT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_SEAT, CkSeatPrivate)) + +#define CK_DBUS_PATH "/org/freedesktop/ConsoleKit" +#define CK_DBUS_NAME "org.freedesktop.ConsoleKit" + +struct CkSeatPrivate +{ + char *id; + CkSeatKind kind; + GHashTable *sessions; + + CkSession *active_session; + + CkVtMonitor *vt_monitor; + + DBusGConnection *connection; +}; + +enum { + ACTIVE_SESSION_CHANGED, + SESSION_ADDED, + SESSION_REMOVED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_ID, + PROP_KIND, +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + +static void ck_seat_class_init (CkSeatClass *klass); +static void ck_seat_init (CkSeat *seat); +static void ck_seat_finalize (GObject *object); + +G_DEFINE_TYPE (CkSeat, ck_seat, G_TYPE_OBJECT) + +GQuark +ck_seat_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("ck_seat_error"); + } + + return ret; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +ck_seat_kind_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) { + static const GEnumValue values[] = { + ENUM_ENTRY (CK_SEAT_KIND_STATIC, "Fixed single instance local seat"), + ENUM_ENTRY (CK_SEAT_KIND_DYNAMIC, "Transient seat"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("CkSeatKindType", values); + } + + return etype; +} + +gboolean +ck_seat_get_active_session (CkSeat *seat, + char **ssid, + GError **error) +{ + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + if (seat->priv->active_session != NULL) { + ck_session_get_id (seat->priv->active_session, ssid, NULL); + } else { + if (ssid != NULL) { + *ssid = NULL; + } + } + + return TRUE; +} + +typedef struct +{ + gulong handler_id; + CkSeat *seat; + guint num; + DBusGMethodInvocation *context; +} ActivateData; + +static void +activated_cb (CkVtMonitor *vt_monitor, + guint num, + ActivateData *adata) +{ + if (adata->num == num) { + dbus_g_method_return (adata->context, TRUE); + } else { + GError *error; + + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Another session was activated while waiting")); + dbus_g_method_return_error (adata->context, error); + g_error_free (error); + } + + g_signal_handler_disconnect (vt_monitor, adata->handler_id); +} + +static gboolean +_seat_activate_session (CkSeat *seat, + CkSession *session, + DBusGMethodInvocation *context) +{ + gboolean ret; + guint num; + char *device; + ActivateData *adata; + GError *vt_error; + + device = NULL; + adata = NULL; + ret = FALSE; + + /* for now, only support switching on static seat */ + if (seat->priv->kind != CK_SEAT_KIND_STATIC) { + GError *error; + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Activation is not supported for this kind of seat")); + dbus_g_method_return_error (context, error); + g_error_free (error); + goto out; + } + + if (session == NULL) { + GError *error; + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Unknown session id")); + dbus_g_method_return_error (context, error); + g_error_free (error); + goto out; + } + + ck_session_get_display_device (session, &device, NULL); + + if (device == NULL || (sscanf (device, "/dev/tty%u", &num) != 1)) { + GError *error; + error = g_error_new (CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Unable to activate session")); + dbus_g_method_return_error (context, error); + g_error_free (error); + goto out; + } + + adata = g_new0 (ActivateData, 1); + adata->context = context; + adata->seat = seat; + adata->num = num; + adata->handler_id = g_signal_connect_data (seat->priv->vt_monitor, + "active-changed", + G_CALLBACK (activated_cb), + adata, + (GClosureNotify)g_free, + 0); + + + ck_debug ("Attempting to activate VT %u", num); + + vt_error = NULL; + ret = ck_vt_monitor_set_active (seat->priv->vt_monitor, num, &vt_error); + if (! ret) { + ck_debug ("Unable to activate session: %s", vt_error->message); + dbus_g_method_return_error (context, vt_error); + g_signal_handler_disconnect (seat->priv->vt_monitor, adata->handler_id); + g_error_free (vt_error); + goto out; + } + + out: + g_free (device); + + return ret; +} + +/* + Example: + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Seat1 \ + org.freedesktop.ConsoleKit.Seat.ActivateSession \ + objpath:/org/freedesktop/ConsoleKit/Session2 +*/ + +gboolean +ck_seat_activate_session (CkSeat *seat, + const char *ssid, + DBusGMethodInvocation *context) +{ + CkSession *session; + gboolean ret; + + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + session = NULL; + + if (ssid != NULL) { + session = g_hash_table_lookup (seat->priv->sessions, ssid); + } + + ret = _seat_activate_session (seat, session, context); + + return ret; +} + +static gboolean +match_session_display_device (const char *key, + CkSession *session, + const char *display_device) +{ + char *device; + gboolean ret; + + device = NULL; + ret = FALSE; + + if (session == NULL) { + goto out; + } + + ck_session_get_display_device (session, &device, NULL); + + if (device != NULL + && display_device != NULL + && strcmp (device, display_device) == 0) { + ret = TRUE; + } +out: + + g_free (device); + + return ret; +} + +static CkSession * +find_session_for_display_device (CkSeat *seat, + const char *device) +{ + CkSession *session; + + session = g_hash_table_find (seat->priv->sessions, + (GHRFunc) match_session_display_device, + (gpointer)device); + return session; +} + +static void +change_active_session (CkSeat *seat, + CkSession *session) +{ + char *ssid; + + if (seat->priv->active_session == session) { + return; + } + + if (seat->priv->active_session != NULL) { + ck_session_set_active (seat->priv->active_session, FALSE, NULL); + g_object_unref (seat->priv->active_session); + } + + seat->priv->active_session = session; + + ssid = NULL; + if (session != NULL) { + g_object_ref (session); + ck_session_get_id (session, &ssid, NULL); + ck_session_set_active (session, TRUE, NULL); + } + + ck_debug ("Active session changed: %s", ssid); + + g_signal_emit (seat, signals [ACTIVE_SESSION_CHANGED], 0, ssid); + + g_free (ssid); +} + +static void +maybe_update_active_session (CkSeat *seat) +{ + guint num; + + if (seat->priv->kind != CK_SEAT_KIND_STATIC) { + return; + } + + if (ck_vt_monitor_get_active (seat->priv->vt_monitor, &num, NULL)) { + CkSession *session; + char *device; + + device = g_strdup_printf ("/dev/tty%u", num); + + ck_debug ("Active device: %s", device); + + session = find_session_for_display_device (seat, device); + if (session != NULL) { + change_active_session (seat, session); + } + + g_free (device); + } +} + +static gboolean +session_activate (CkSession *session, + DBusGMethodInvocation *context, + CkSeat *seat) +{ + return _seat_activate_session (seat, session, context); +} + +gboolean +ck_seat_remove_session (CkSeat *seat, + CkSession *session, + GError **error) +{ + char *ssid; + + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + ssid = NULL; + ck_session_get_id (session, &ssid, NULL); + + if (g_hash_table_lookup (seat->priv->sessions, ssid) == NULL) { + ck_debug ("Session %s is not attached to seat %s", ssid, seat->priv->id); + g_set_error (error, + CK_SEAT_ERROR, + CK_SEAT_ERROR_GENERAL, + _("Session is not attached to this seat")); + g_free (ssid); + return FALSE; + } + + g_signal_handlers_disconnect_by_func (session, session_activate, seat); + + ck_debug ("Emitting removed signal: %s", ssid); + + g_signal_emit (seat, signals [SESSION_REMOVED], 0, ssid); + + /* try to change the active session */ + maybe_update_active_session (seat); + + /* if the active session is still the one to be removed, unset it */ + if (seat->priv->active_session == session) { + change_active_session (seat, NULL); + } + + g_hash_table_remove (seat->priv->sessions, ssid); + + return TRUE; +} + +gboolean +ck_seat_add_session (CkSeat *seat, + CkSession *session, + GError **error) +{ + char *ssid; + + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + ck_session_get_id (session, &ssid, NULL); + + g_hash_table_insert (seat->priv->sessions, g_strdup (ssid), g_object_ref (session)); + + ck_session_set_seat_id (session, seat->priv->id, NULL); + + g_signal_connect_object (session, "activate", G_CALLBACK (session_activate), seat, 0); + /* FIXME: attach to property notify signals? */ + + ck_debug ("Emitting added signal: %s", ssid); + + g_signal_emit (seat, signals [SESSION_ADDED], 0, ssid); + + maybe_update_active_session (seat); + + return TRUE; +} + +gboolean +ck_seat_get_kind (CkSeat *seat, + CkSeatKind *kind, + GError **error) +{ + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + if (kind != NULL) { + *kind = seat->priv->kind; + } + + return TRUE; +} + +gboolean +ck_seat_get_id (CkSeat *seat, + char **id, + GError **error) +{ + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + if (id != NULL) { + *id = g_strdup (seat->priv->id); + } + + return TRUE; +} + +static void +active_vt_changed (CkVtMonitor *vt_monitor, + guint num, + CkSeat *seat) +{ + ck_debug ("Active vt changed: %u", num); + + maybe_update_active_session (seat); +} + +static gboolean +register_seat (CkSeat *seat) +{ + GError *error = NULL; + + error = NULL; + seat->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (seat->priv->connection == NULL) { + if (error != NULL) { + g_critical ("error getting system bus: %s", error->message); + g_error_free (error); + } + return FALSE; + } + + dbus_g_connection_register_g_object (seat->priv->connection, seat->priv->id, G_OBJECT (seat)); + + return TRUE; +} + +static void +listify_session_ids (char *id, + CkSession *session, + GPtrArray **array) +{ + g_ptr_array_add (*array, g_strdup (id)); +} + +gboolean +ck_seat_get_sessions (CkSeat *seat, + GPtrArray **sessions, + GError **error) +{ + g_return_val_if_fail (CK_IS_SEAT (seat), FALSE); + + if (sessions == NULL) { + return FALSE; + } + + *sessions = g_ptr_array_new (); + g_hash_table_foreach (seat->priv->sessions, (GHFunc)listify_session_ids, sessions); + + return TRUE; +} + +static void +_ck_seat_set_id (CkSeat *seat, + const char *id) +{ + g_free (seat->priv->id); + seat->priv->id = g_strdup (id); +} + +static void +_ck_seat_set_kind (CkSeat *seat, + CkSeatKind kind) +{ + seat->priv->kind = kind; +} + +static void +ck_seat_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CkSeat *self; + + self = CK_SEAT (object); + + switch (prop_id) { + case PROP_ID: + _ck_seat_set_id (self, g_value_get_string (value)); + break; + case PROP_KIND: + _ck_seat_set_kind (self, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ck_seat_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CkSeat *self; + + self = CK_SEAT (object); + + switch (prop_id) { + case PROP_ID: + g_value_set_string (value, self->priv->id); + break; + case PROP_KIND: + g_value_set_string (value, self->priv->id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +ck_seat_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + CkSeat *seat; + CkSeatClass *klass; + + klass = CK_SEAT_CLASS (g_type_class_peek (CK_TYPE_SEAT)); + + seat = CK_SEAT (G_OBJECT_CLASS (ck_seat_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + if (seat->priv->kind == CK_SEAT_KIND_STATIC) { + seat->priv->vt_monitor = ck_vt_monitor_new (); + g_signal_connect (seat->priv->vt_monitor, "active-changed", G_CALLBACK (active_vt_changed), seat); + } + + return G_OBJECT (seat); +} + +static void +ck_seat_class_init (CkSeatClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = ck_seat_get_property; + object_class->set_property = ck_seat_set_property; + object_class->constructor = ck_seat_constructor; + object_class->finalize = ck_seat_finalize; + + signals [ACTIVE_SESSION_CHANGED] = g_signal_new ("active-session-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkSeatClass, active_session_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + signals [SESSION_ADDED] = g_signal_new ("session-added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkSeatClass, session_added), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + signals [SESSION_REMOVED] = g_signal_new ("session-removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkSeatClass, session_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, + 1, G_TYPE_STRING); + + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", + "id", + "id", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, + PROP_KIND, + g_param_spec_enum ("kind", + "kind", + "kind", + CK_TYPE_SEAT_KIND, + CK_SEAT_KIND_DYNAMIC, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_type_class_add_private (klass, sizeof (CkSeatPrivate)); + + dbus_g_object_type_install_info (CK_TYPE_SEAT, &dbus_glib_ck_seat_object_info); +} + +static void +ck_seat_init (CkSeat *seat) +{ + seat->priv = CK_SEAT_GET_PRIVATE (seat); + + seat->priv->sessions = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_object_unref); +} + +static void +ck_seat_finalize (GObject *object) +{ + CkSeat *seat; + + g_return_if_fail (object != NULL); + g_return_if_fail (CK_IS_SEAT (object)); + + seat = CK_SEAT (object); + + g_return_if_fail (seat->priv != NULL); + + if (seat->priv->vt_monitor != NULL) { + g_object_unref (seat->priv->vt_monitor); + } + + if (seat->priv->active_session != NULL) { + g_object_unref (seat->priv->active_session); + } + + g_hash_table_destroy (seat->priv->sessions); + g_free (seat->priv->id); + + G_OBJECT_CLASS (ck_seat_parent_class)->finalize (object); +} + +CkSeat * +ck_seat_new (const char *sid, + CkSeatKind kind) +{ + GObject *object; + gboolean res; + + object = g_object_new (CK_TYPE_SEAT, + "id", sid, + "kind", kind, + NULL); + + res = register_seat (CK_SEAT (object)); + if (! res) { + g_object_unref (object); + return NULL; + } + + return CK_SEAT (object); +} -- cgit