summaryrefslogtreecommitdiffstats
path: root/src/ck-vt-monitor.c
diff options
context:
space:
mode:
authorWilliam Jon McCann <mccannwj@acsnb12.pha.jhu.edu>2006-10-25 14:38:19 -0400
committerWilliam Jon McCann <mccannwj@acsnb12.pha.jhu.edu>2006-10-25 14:38:19 -0400
commita08530e838218b3c5859550058e78b8f1abee434 (patch)
treec54037c29cd86ebbecf6d9e10eb86da1ba657c9e /src/ck-vt-monitor.c
Initial import
Diffstat (limited to 'src/ck-vt-monitor.c')
-rw-r--r--src/ck-vt-monitor.c360
1 files changed, 360 insertions, 0 deletions
diff --git a/src/ck-vt-monitor.c b/src/ck-vt-monitor.c
new file mode 100644
index 0000000..eaaa9f4
--- /dev/null
+++ b/src/ck-vt-monitor.c
@@ -0,0 +1,360 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/vt.h>
+
+#if defined (__linux__)
+#include <linux/tty.h>
+#include <linux/kd.h>
+#endif /* linux */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "ck-vt-monitor.h"
+#include "ck-marshal.h"
+#include "ck-debug.h"
+
+#define ERROR -1
+#define DEBUG_ENABLED 1
+
+#define CK_VT_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_VT_MONITOR, CkVtMonitorPrivate))
+
+struct CkVtMonitorPrivate
+{
+ int vfd;
+ GHashTable *vt_thread_hash;
+ guint active_num;
+};
+
+enum {
+ ACTIVE_CHANGED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void ck_vt_monitor_class_init (CkVtMonitorClass *klass);
+static void ck_vt_monitor_init (CkVtMonitor *vt_monitor);
+static void ck_vt_monitor_finalize (GObject *object);
+
+G_DEFINE_TYPE (CkVtMonitor, ck_vt_monitor, G_TYPE_OBJECT)
+
+static void watch_vts (CkVtMonitor *vt_monitor);
+
+static gpointer vt_object = NULL;
+
+GQuark
+ck_vt_monitor_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("ck_vt_monitor_error");
+ }
+
+ return ret;
+}
+
+gboolean
+ck_vt_monitor_set_active (CkVtMonitor *vt_monitor,
+ guint32 num,
+ GError **error)
+{
+ gboolean ret;
+ int res;
+
+ g_return_val_if_fail (CK_IS_VT_MONITOR (vt_monitor), FALSE);
+
+ if (num == vt_monitor->priv->active_num) {
+ g_set_error (error,
+ CK_VT_MONITOR_ERROR,
+ CK_VT_MONITOR_ERROR_GENERAL,
+ _("Session is already active"));
+ return FALSE;
+ }
+
+ res = ioctl (vt_monitor->priv->vfd, VT_ACTIVATE, num);
+ if (res == 0) {
+ ret = TRUE;
+ } else {
+ g_set_error (error,
+ CK_VT_MONITOR_ERROR,
+ CK_VT_MONITOR_ERROR_GENERAL,
+ _("Unable to activate session"));
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+gboolean
+ck_vt_monitor_get_active (CkVtMonitor *vt_monitor,
+ guint32 *num,
+ GError **error)
+{
+ g_return_val_if_fail (CK_IS_VT_MONITOR (vt_monitor), FALSE);
+
+ if (num != NULL) {
+ *num = vt_monitor->priv->active_num;
+ }
+
+ return TRUE;
+}
+
+static void
+change_active_num (CkVtMonitor *vt_monitor,
+ guint num)
+{
+
+ if (vt_monitor->priv->active_num != num) {
+
+ vt_monitor->priv->active_num = num;
+
+ g_hash_table_remove (vt_monitor->priv->vt_thread_hash, GUINT_TO_POINTER (num));
+ watch_vts (vt_monitor);
+
+ g_signal_emit (vt_monitor, signals[ACTIVE_CHANGED], 0, num);
+ }
+}
+
+typedef struct {
+ gint32 num;
+ CkVtMonitor *vt_monitor;
+} ThreadData;
+
+static gboolean
+vt_activated (ThreadData *data)
+{
+ change_active_num (data->vt_monitor, data->num);
+
+ g_free (data);
+
+ return FALSE;
+}
+
+static void *
+vt_thread_start (ThreadData *data)
+{
+ CkVtMonitor *vt_monitor;
+ int ret;
+ gint32 num;
+
+ vt_monitor = data->vt_monitor;
+ num = data->num;
+
+ again:
+ ck_debug ("VT_WAITACTIVE for vt %d", num);
+ ret = ioctl (vt_monitor->priv->vfd, VT_WAITACTIVE, num);
+
+ ck_debug ("VT_WAITACTIVE returned %d", ret);
+
+ if (ret == ERROR) {
+
+ if (errno == EINTR) {
+ ck_debug ("Interrupted waiting for native console %d activation: %s",
+ num,
+ g_strerror (errno));
+ goto again;
+ } else {
+ ck_debug ("Error waiting for native console %d activation: %s",
+ num,
+ g_strerror (errno));
+
+ g_warning ("Error waiting for native console %d activation: %s",
+ num,
+ g_strerror (errno));
+ }
+
+ g_free (data);
+
+ if (vt_monitor->priv->vt_thread_hash != NULL) {
+ g_hash_table_remove (vt_monitor->priv->vt_thread_hash, GUINT_TO_POINTER (num));
+ }
+ } else {
+ g_idle_add ((GSourceFunc)vt_activated, data);
+ }
+
+ g_thread_exit (NULL);
+
+ return NULL;
+}
+
+static guint
+get_active_native (CkVtMonitor *vt_monitor)
+{
+ int ret;
+ struct vt_stat stat;
+
+ ret = ioctl (vt_monitor->priv->vfd, VT_GETSTATE, &stat);
+ if (ret == ERROR) {
+ perror ("ioctl VT_GETSTATE");
+ return -1;
+ }
+
+ {
+ int i;
+
+ ck_debug ("Current VT: tty%d", stat.v_active);
+ for (i = 1; i <= 16; i++) {
+ gboolean is_on;
+ is_on = stat.v_state & (1 << i);
+
+ ck_debug ("VT %d:%s", i, is_on ? "on" : "off");
+ }
+ }
+
+ return stat.v_active;
+}
+
+static void
+watch_vts (CkVtMonitor *vt_monitor)
+{
+ int i;
+ gint32 current_num;
+
+ current_num = vt_monitor->priv->active_num;
+
+ for (i = 1; i < MAX_NR_CONSOLES; i++) {
+ gpointer id;
+
+ /* don't wait on the active vc */
+ if (i == current_num) {
+ continue;
+ }
+
+ id = GINT_TO_POINTER (i);
+ if (g_hash_table_lookup (vt_monitor->priv->vt_thread_hash, id) == NULL) {
+ GThread *thread;
+ GError *error;
+ ThreadData *data;
+
+ data = g_new0 (ThreadData, 1);
+ data->num = i;
+ data->vt_monitor = vt_monitor;
+
+ ck_debug ("Creating thread for vt %d", i);
+
+ error = NULL;
+ thread = g_thread_create ((GThreadFunc)vt_thread_start, data, FALSE, &error);
+ if (thread == NULL) {
+ ck_debug ("Unable to create thread: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_hash_table_insert (vt_monitor->priv->vt_thread_hash, id, thread);
+ }
+ }
+ }
+}
+
+static void
+ck_vt_monitor_class_init (CkVtMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ck_vt_monitor_finalize;
+
+ signals [ACTIVE_CHANGED] = g_signal_new ("active-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CkVtMonitorClass, active_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE,
+ 1, G_TYPE_UINT);
+
+ g_type_class_add_private (klass, sizeof (CkVtMonitorPrivate));
+}
+
+extern int getfd (void);
+
+static void
+ck_vt_monitor_init (CkVtMonitor *vt_monitor)
+{
+ int fd;
+
+ vt_monitor->priv = CK_VT_MONITOR_GET_PRIVATE (vt_monitor);
+
+ fd = getfd ();
+ if (fd == ERROR) {
+ ck_debug ("Unable to open console: %s", g_strerror (errno));
+ g_critical ("Unable to open console: %s", g_strerror (errno));
+ }
+
+ vt_monitor->priv->vfd = fd;
+ vt_monitor->priv->vt_thread_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ vt_monitor->priv->active_num = get_active_native (vt_monitor);
+
+ watch_vts (vt_monitor);
+}
+
+static void
+ck_vt_monitor_finalize (GObject *object)
+{
+ CkVtMonitor *vt_monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (CK_IS_VT_MONITOR (object));
+
+ vt_monitor = CK_VT_MONITOR (object);
+
+ g_return_if_fail (vt_monitor->priv != NULL);
+
+ g_hash_table_destroy (vt_monitor->priv->vt_thread_hash);
+
+ close (vt_monitor->priv->vfd);
+
+ G_OBJECT_CLASS (ck_vt_monitor_parent_class)->finalize (object);
+}
+
+CkVtMonitor *
+ck_vt_monitor_new (void)
+{
+ if (vt_object != NULL) {
+ g_object_ref (vt_object);
+ } else {
+ vt_object = g_object_new (CK_TYPE_VT_MONITOR, NULL);
+
+ g_object_add_weak_pointer (vt_object,
+ (gpointer *) &vt_object);
+ }
+
+ return CK_VT_MONITOR (vt_object);
+}