From e0244a8f6dd0b7f8ebecc6bec52c013ce5286279 Mon Sep 17 00:00:00 2001 From: William Jon McCann Date: Thu, 5 Apr 2007 15:21:14 -0400 Subject: use inotify to detect activity on tty when possible --- .gitignore | 3 +- configure.ac | 26 +- doc/.gitignore | 1 + src/.gitignore | 1 + src/Makefile.am | 32 ++ src/ck-file-monitor-dummy.c | 118 ++++++++ src/ck-file-monitor-inotify.c | 669 ++++++++++++++++++++++++++++++++++++++++++ src/ck-file-monitor.h | 84 ++++++ src/ck-session.c | 82 ++---- src/ck-tty-idle-monitor.c | 389 ++++++++++++++++++++++++ src/ck-tty-idle-monitor.h | 71 +++++ src/test-tty-idle-monitor.c | 82 ++++++ 12 files changed, 1496 insertions(+), 62 deletions(-) create mode 100644 src/ck-file-monitor-dummy.c create mode 100644 src/ck-file-monitor-inotify.c create mode 100644 src/ck-file-monitor.h create mode 100644 src/ck-tty-idle-monitor.c create mode 100644 src/ck-tty-idle-monitor.h create mode 100644 src/test-tty-idle-monitor.c diff --git a/.gitignore b/.gitignore index a6456d1..a08eb02 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,8 @@ version.h intl macros INSTALL -console-kit-*.tar.gz +ChangeLog +ConsoleKit-*.tar.gz xml-i18n-extract.in xml-i18n-extract xml-i18n-merge.in diff --git a/configure.ac b/configure.ac index a3dbae0..c2cba96 100644 --- a/configure.ac +++ b/configure.ac @@ -41,7 +41,6 @@ AC_CHECK_HEADERS(paths.h) AC_TYPE_UID_T -AC_CHECK_FUNCS([setresuid setenv unsetenv clearenv]) PKG_CHECK_MODULES(CONSOLE_KIT, dbus-glib-1 >= $DBUS_REQUIRED_VERSION gobject-2.0 >= $GLIB_REQUIRED_VERSION @@ -121,7 +120,7 @@ dnl --------------------------------------------------------------------------- dnl - PID file dnl --------------------------------------------------------------------------- -AC_ARG_WITH(pid-file, +AC_ARG_WITH(pid-file, [AC_HELP_STRING([--with-pid-file=], [pid file location])]) @@ -171,7 +170,7 @@ AC_CHECK_LIB(pam, pam_syslog, [AC_DEFINE(HAVE_PAM_SYSLOG, [], [Define to 1 if yo # Check if we should build the PAM module msg_pam_module=no -AC_ARG_ENABLE(pam-module, +AC_ARG_ENABLE(pam-module, [AC_HELP_STRING([--enable-pam-module], [build PAM module])], , enable_pam_module=no) @@ -228,6 +227,23 @@ fi AM_CONDITIONAL(DOCBOOK_DOCS_ENABLED, test x$enable_docbook_docs = xyes) AC_MSG_RESULT(yes) +dnl --------------------------------------------------------------------------- +dnl check for inotify +dnl --------------------------------------------------------------------------- + +enable_inotify=no +AC_CHECK_FUNC(inotify_init, + [AC_CHECK_HEADER([sys/inotify.h], + [enable_inotify=yes], + [])], + []) + +AM_CONDITIONAL(ENABLE_INOTIFY, test "x$enable_inotify" = "xyes") + +if test "x$enable_inotify" = "xyes" ; then + AC_DEFINE(ENABLE_INOTIFY, [], [Define if we have inotify]) +fi + dnl --------------------------------------------------------------------------- dnl Finish dnl --------------------------------------------------------------------------- @@ -235,7 +251,7 @@ dnl --------------------------------------------------------------------------- # Turn on the additional warnings last, so -Werror doesn't affect other tests. AC_ARG_ENABLE(more-warnings, - [AC_HELP_STRING([--enable-more-warnings], + [AC_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings])], set_more_warnings="$enableval",[ if test -d $srcdir/.git; then @@ -276,7 +292,7 @@ fi # # Enable Debug # -AC_ARG_ENABLE(debug, +AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug], [turn on debugging])], , enable_debug=yes) diff --git a/doc/.gitignore b/doc/.gitignore index 958a222..4337b46 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -1,5 +1,6 @@ Makefile Makefile.in ConsoleKit.xml +ConsoleKit.html *.o *~ diff --git a/src/.gitignore b/src/.gitignore index 6f7eeb8..68ff01f 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -6,6 +6,7 @@ Makefile Makefile.in console-kit-daemon +test-tty-idle-monitor ck-marshal.c ck-marshal.h ck-manager-glue.h diff --git a/src/Makefile.am b/src/Makefile.am index 26eb8e7..0977591 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -59,6 +59,9 @@ console_kit_daemon_SOURCES = \ ck-manager.c \ ck-vt-monitor.h \ ck-vt-monitor.c \ + ck-tty-idle-monitor.h \ + ck-tty-idle-monitor.c \ + ck-file-monitor.h \ ck-job.h \ ck-job.c \ ck-seat.h \ @@ -73,10 +76,39 @@ console_kit_daemon_SOURCES = \ $(BUILT_SOURCES) \ $(NULL) +if ENABLE_INOTIFY +FILE_MONITOR_BACKEND = ck-file-monitor-inotify.c +else +FILE_MONITOR_BACKEND = ck-file-monitor-dummy.c +endif + +console_kit_daemon_SOURCES += $(FILE_MONITOR_BACKEND) + +EXTRA_console_kit_daemon_SOURCES = \ + ck-file-monitor-inotify.c \ + ck-file-monitor-dummy.c \ + $(NULL) + console_kit_daemon_LDADD = \ $(CONSOLE_KIT_LIBS) \ $(NULL) +noinst_PROGRAMS = \ + test-tty-idle-monitor \ + $(NULL) + +test_tty_idle_monitor_SOURCES = \ + ck-tty-idle-monitor.h \ + ck-tty-idle-monitor.c \ + ck-file-monitor.h \ + $(FILE_MONITOR_BACKEND) \ + test-tty-idle-monitor.c \ + $(NULL) + +test_tty_idle_monitor_LDADD = \ + $(CONSOLE_KIT_LIBS) \ + $(NULL) + EXTRA_DIST = \ ck-marshal.list \ ck-manager.xml \ diff --git a/src/ck-file-monitor-dummy.c b/src/ck-file-monitor-dummy.c new file mode 100644 index 0000000..27efb01 --- /dev/null +++ b/src/ck-file-monitor-dummy.c @@ -0,0 +1,118 @@ +/* -*- 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 "ck-file-monitor.h" + +#define CK_FILE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_FILE_MONITOR, CkFileMonitorPrivate)) + +struct CkFileMonitorPrivate +{ + gpointer dummy; +}; + +static void ck_file_monitor_class_init (CkFileMonitorClass *klass); +static void ck_file_monitor_init (CkFileMonitor *file_monitor); +static void ck_file_monitor_finalize (GObject *object); + +G_DEFINE_TYPE (CkFileMonitor, ck_file_monitor, G_TYPE_OBJECT) + +static gpointer monitor_object = NULL; + +GQuark +ck_file_monitor_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("ck_file_monitor_error"); + } + + return ret; +} + +guint +ck_file_monitor_add_notify (CkFileMonitor *monitor, + const char *path, + int mask, + CkFileMonitorNotifyFunc notify_func, + gpointer data) +{ + return 0; +} + +void +ck_file_monitor_remove_notify (CkFileMonitor *monitor, + guint id) +{ + return; +} + +static void +ck_file_monitor_class_init (CkFileMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = ck_file_monitor_finalize; + + g_type_class_add_private (klass, sizeof (CkFileMonitorPrivate)); +} + +static void +ck_file_monitor_init (CkFileMonitor *monitor) +{ + monitor->priv = CK_FILE_MONITOR_GET_PRIVATE (monitor); +} + +static void +ck_file_monitor_finalize (GObject *object) +{ + CkFileMonitor *monitor; + + g_return_if_fail (object != NULL); + g_return_if_fail (CK_IS_FILE_MONITOR (object)); + + monitor = CK_FILE_MONITOR (object); + + g_return_if_fail (monitor->priv != NULL); + + G_OBJECT_CLASS (ck_file_monitor_parent_class)->finalize (object); +} + +CkFileMonitor * +ck_file_monitor_new (void) +{ + if (monitor_object != NULL) { + g_object_ref (monitor_object); + } else { + monitor_object = g_object_new (CK_TYPE_FILE_MONITOR, NULL); + + g_object_add_weak_pointer (monitor_object, + (gpointer *) &monitor_object); + } + + return CK_FILE_MONITOR (monitor_object); +} diff --git a/src/ck-file-monitor-inotify.c b/src/ck-file-monitor-inotify.c new file mode 100644 index 0000000..6e775e2 --- /dev/null +++ b/src/ck-file-monitor-inotify.c @@ -0,0 +1,669 @@ +/* -*- 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 "ck-file-monitor.h" + +#define CK_FILE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_FILE_MONITOR, CkFileMonitorPrivate)) + +typedef struct +{ + int wd; + char *path; + GSList *notifies; +} FileInotifyWatch; + +typedef struct +{ + guint id; + int mask; + CkFileMonitorNotifyFunc notify_func; + gpointer user_data; + FileInotifyWatch *watch; +} FileMonitorNotify; + +typedef struct +{ + FileInotifyWatch *watch; + CkFileMonitorEvent event; + char *path; +} FileMonitorEventInfo; + +#define DEFAULT_NOTIFY_BUFLEN (32 * (sizeof (struct inotify_event) + 16)) +#define MAX_NOTIFY_BUFLEN (32 * DEFAULT_NOTIFY_BUFLEN) + +struct CkFileMonitorPrivate +{ + guint serial; + + gboolean initialized_inotify; + + int inotify_fd; + guint io_watch; + + GHashTable *wd_to_watch; + GHashTable *path_to_watch; + GHashTable *notifies; + + guint buflen; + guchar *buffer; + + guint events_idle_id; + GQueue *notify_events; +}; + +enum { + PROP_0, +}; + +static void ck_file_monitor_class_init (CkFileMonitorClass *klass); +static void ck_file_monitor_init (CkFileMonitor *file_monitor); +static void ck_file_monitor_finalize (GObject *object); + +G_DEFINE_TYPE (CkFileMonitor, ck_file_monitor, G_TYPE_OBJECT) + +static gpointer monitor_object = NULL; + +GQuark +ck_file_monitor_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("ck_file_monitor_error"); + } + + return ret; +} + +/* most of this is adapted from libgnome-menu */ + +static int +our_event_mask_to_inotify_mask (int our_mask) +{ + int mask; + + mask = 0; + + if (our_mask & CK_FILE_MONITOR_EVENT_ACCESS) { + mask |= IN_ACCESS; + } + + if (our_mask & CK_FILE_MONITOR_EVENT_CREATE) { + mask |= IN_CREATE | IN_MOVED_TO; + } + + if (our_mask & CK_FILE_MONITOR_EVENT_DELETE) { + mask |= IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF; + } + + if (our_mask & CK_FILE_MONITOR_EVENT_CHANGE) { + mask |= IN_MODIFY | IN_ATTRIB; + } + + return mask; +} + +static char * +imask_to_string (guint32 mask) +{ + GString *out; + + out = g_string_new (NULL); + + if (mask & IN_ACCESS) { + g_string_append (out, "ACCESS "); + } + if (mask & IN_MODIFY) { + g_string_append (out, "MODIFY "); + } + if (mask & IN_ATTRIB) { + g_string_append (out, "ATTRIB "); + } + if (mask & IN_CLOSE_WRITE) { + g_string_append (out, "CLOSE_WRITE "); + } + if (mask & IN_CLOSE_NOWRITE) { + g_string_append (out, "CLOSE_NOWRITE "); + } + if (mask & IN_OPEN) { + g_string_append (out, "OPEN "); + } + if (mask & IN_MOVED_FROM) { + g_string_append (out, "MOVED_FROM "); + } + if (mask & IN_MOVED_TO) { + g_string_append (out, "MOVED_TO "); + } + if (mask & IN_DELETE) { + g_string_append (out, "DELETE "); + } + if (mask & IN_CREATE) { + g_string_append (out, "CREATE "); + } + if (mask & IN_DELETE_SELF) { + g_string_append (out, "DELETE_SELF "); + } + if (mask & IN_UNMOUNT) { + g_string_append (out, "UNMOUNT "); + } + if (mask & IN_Q_OVERFLOW) { + g_string_append (out, "Q_OVERFLOW "); + } + if (mask & IN_IGNORED) { + g_string_append (out, "IGNORED "); + } + + return g_string_free (out, FALSE); +} + +static FileInotifyWatch * +file_monitor_add_watch_for_path (CkFileMonitor *monitor, + const char *path, + int mask) + +{ + FileInotifyWatch *watch; + int wd; + int imask; + char *mask_str; + + imask = our_event_mask_to_inotify_mask (mask); + + mask_str = imask_to_string (imask); + g_debug ("adding inotify watch %s", mask_str); + g_free (mask_str); + + wd = inotify_add_watch (monitor->priv->inotify_fd, path, IN_MASK_ADD | imask); + if (wd < 0) { + /* FIXME: remove watch etc */ + return NULL; + } + + watch = g_hash_table_lookup (monitor->priv->path_to_watch, path); + if (watch == NULL) { + watch = g_new0 (FileInotifyWatch, 1); + + watch->wd = wd; + watch->path = g_strdup (path); + + g_hash_table_insert (monitor->priv->path_to_watch, watch->path, watch); + g_hash_table_insert (monitor->priv->wd_to_watch, GINT_TO_POINTER (wd), watch); + } + + return watch; +} + +static void +monitor_release_watch (CkFileMonitor *monitor, + FileInotifyWatch *watch) +{ + g_slist_free (watch->notifies); + watch->notifies = NULL; + + g_free (watch->path); + watch->path = NULL; + + inotify_rm_watch (monitor->priv->inotify_fd, watch->wd); + watch->wd = -1; +} + +static void +file_monitor_remove_watch (CkFileMonitor *monitor, + FileInotifyWatch *watch) +{ + g_hash_table_remove (monitor->priv->path_to_watch, + watch->path); + g_hash_table_remove (monitor->priv->wd_to_watch, + GINT_TO_POINTER (watch->wd)); + monitor_release_watch (monitor, watch); +} + +static gboolean +remove_watch_foreach (const char *path, + FileInotifyWatch *watch, + CkFileMonitor *monitor) +{ + monitor_release_watch (monitor, watch); + return TRUE; +} + +static void +close_inotify (CkFileMonitor *monitor) +{ + if (! monitor->priv->initialized_inotify) { + return; + } + + monitor->priv->initialized_inotify = FALSE; + + g_hash_table_foreach_remove (monitor->priv->path_to_watch, + (GHRFunc) remove_watch_foreach, + monitor); + monitor->priv->path_to_watch = NULL; + + if (monitor->priv->wd_to_watch != NULL) { + g_hash_table_destroy (monitor->priv->wd_to_watch); + } + monitor->priv->wd_to_watch = NULL; + + g_free (monitor->priv->buffer); + monitor->priv->buffer = NULL; + monitor->priv->buflen = 0; + + if (monitor->priv->io_watch) { + g_source_remove (monitor->priv->io_watch); + } + monitor->priv->io_watch = 0; + + if (monitor->priv->inotify_fd > 0) { + close (monitor->priv->inotify_fd); + } + monitor->priv->inotify_fd = 0; +} + +static gboolean +emit_events_in_idle (CkFileMonitor *monitor) +{ + FileMonitorEventInfo *event_info; + + monitor->priv->events_idle_id = 0; + + while ((event_info = g_queue_pop_head (monitor->priv->notify_events)) != NULL) { + GSList *l; + FileInotifyWatch *watch; + + watch = event_info->watch; + + for (l = watch->notifies; l != NULL; l = l->next) { + FileMonitorNotify *notify; + + notify = g_hash_table_lookup (monitor->priv->notifies, + GUINT_TO_POINTER (l->data)); + if (notify == NULL) { + continue; + } + + if (! (notify->mask & event_info->event)) { + continue; + } + + if (notify->notify_func) { + notify->notify_func (monitor, event_info->event, event_info->path, notify->user_data); + } + } + + g_free (event_info->path); + event_info->path = NULL; + + event_info->event = CK_FILE_MONITOR_EVENT_NONE; + + g_free (event_info); + } + + return FALSE; +} + +static void +file_monitor_queue_event (CkFileMonitor *monitor, + FileMonitorEventInfo *event_info) +{ + g_queue_push_tail (monitor->priv->notify_events, event_info); + + if (monitor->priv->events_idle_id == 0) { + monitor->priv->events_idle_id = g_idle_add ((GSourceFunc) emit_events_in_idle, monitor); + } +} + +static void +queue_watch_event (CkFileMonitor *monitor, + FileInotifyWatch *watch, + CkFileMonitorEvent event, + const char *path) +{ + FileMonitorEventInfo *event_info; + + event_info = g_new0 (FileMonitorEventInfo, 1); + + event_info->watch = watch; + event_info->path = g_strdup (path); + event_info->event = event; + + file_monitor_queue_event (monitor, event_info); +} + +static void +handle_inotify_event (CkFileMonitor *monitor, + FileInotifyWatch *watch, + struct inotify_event *ievent) +{ + CkFileMonitorEvent event; + const char *path; + char *freeme; + char *mask_str; + + freeme = NULL; + + if (ievent->len > 0) { + path = freeme = g_build_filename (watch->path, ievent->name, NULL); + } else { + path = watch->path; + } + + mask_str = imask_to_string (ievent->mask); + g_debug ("handing inotify event %s for %s", mask_str, path); + g_free (mask_str); + + event = CK_FILE_MONITOR_EVENT_NONE; + + if (ievent->mask & (IN_CREATE | IN_MOVED_TO)) { + event = CK_FILE_MONITOR_EVENT_CREATE; + } else if (ievent->mask & (IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF)) { + event = CK_FILE_MONITOR_EVENT_DELETE; + } else if (ievent->mask & (IN_MODIFY | IN_ATTRIB)) { + event = CK_FILE_MONITOR_EVENT_CHANGE; + } else if (ievent->mask & IN_ACCESS) { + event = CK_FILE_MONITOR_EVENT_ACCESS; + } + + if (event != CK_FILE_MONITOR_EVENT_NONE) { + queue_watch_event (monitor, watch, event, path); + } + + if (ievent->mask & IN_IGNORED) { + file_monitor_remove_watch (monitor, watch); + } +} + +static gboolean +inotify_data_pending (GIOChannel *source, + GIOCondition condition, + CkFileMonitor *monitor) +{ + int len; + int i; + + g_debug ("Inotify data pending"); + + g_assert (monitor->priv->inotify_fd > 0); + g_assert (monitor->priv->buffer != NULL); + + do { + while ((len = read (monitor->priv->inotify_fd, monitor->priv->buffer, monitor->priv->buflen)) < 0 && errno == EINTR); + + if (len > 0) { + break; + } else if (len < 0) { + g_warning ("Error reading inotify event: %s", + g_strerror (errno)); + goto error_cancel; + } + + g_assert (len == 0); + + if ((monitor->priv->buflen << 1) > MAX_NOTIFY_BUFLEN) { + g_warning ("Error reading inotify event: Exceded maximum buffer size"); + goto error_cancel; + } + + g_debug ("Buffer size %u too small, trying again at %u\n", + monitor->priv->buflen, monitor->priv->buflen << 1); + + monitor->priv->buflen <<= 1; + monitor->priv->buffer = g_realloc (monitor->priv->buffer, monitor->priv->buflen); + } while (TRUE); + + g_debug ("Inotify buffer filled"); + + i = 0; + while (i < len) { + struct inotify_event *ievent = (struct inotify_event *) &monitor->priv->buffer [i]; + FileInotifyWatch *watch; + + g_debug ("Got event wd = %d, mask = 0x%x, cookie = %d, len = %d, name= %s\n", + ievent->wd, + ievent->mask, + ievent->cookie, + ievent->len, + ievent->len > 0 ? ievent->name : ""); + + watch = g_hash_table_lookup (monitor->priv->wd_to_watch, + GINT_TO_POINTER (ievent->wd)); + if (watch != NULL) { + handle_inotify_event (monitor, watch, ievent); + } + + i += sizeof (struct inotify_event) + ievent->len; + } + + return TRUE; + + error_cancel: + monitor->priv->io_watch = 0; + + close_inotify (monitor); + + return FALSE; +} + +static FileMonitorNotify * +file_monitor_add_notify_for_path (CkFileMonitor *monitor, + const char *path, + int mask, + CkFileMonitorNotifyFunc notify_func, + gpointer data) +{ + FileMonitorNotify *notify; + FileInotifyWatch *watch; + + notify = NULL; + + watch = file_monitor_add_watch_for_path (monitor, path, mask); + if (watch != NULL) { + notify = g_new0 (FileMonitorNotify, 1); + notify->notify_func = notify_func; + notify->user_data = data; + notify->id = monitor->priv->serial++; + notify->watch = watch; + notify->mask = mask; + + g_debug ("Adding notify for %s mask:%d", path, mask); + + g_hash_table_insert (monitor->priv->notifies, GUINT_TO_POINTER (notify->id), notify); + watch->notifies = g_slist_prepend (watch->notifies, GUINT_TO_POINTER (notify->id)); + } + + return notify; +} + +static void +file_monitor_remove_notify (CkFileMonitor *monitor, + guint id) +{ + FileMonitorNotify *notify; + + g_debug ("removing notify for %u", id); + + notify = g_hash_table_lookup (monitor->priv->notifies, + GUINT_TO_POINTER (id)); + if (notify == NULL) { + return; + } + + g_hash_table_steal (monitor->priv->notifies, + GUINT_TO_POINTER (id)); + + notify->watch->notifies = g_slist_remove (notify->watch->notifies, GUINT_TO_POINTER (id)); + + if (g_slist_length (notify->watch->notifies) == 0) { + file_monitor_remove_watch (monitor, notify->watch); + g_free (notify->watch); + } + + g_free (notify); +} + +guint +ck_file_monitor_add_notify (CkFileMonitor *monitor, + const char *path, + int mask, + CkFileMonitorNotifyFunc notify_func, + gpointer data) +{ + FileMonitorNotify *notify; + + if (! monitor->priv->initialized_inotify) { + return 0; + } + + notify = file_monitor_add_notify_for_path (monitor, + path, + mask, + notify_func, + data); + if (notify == NULL) { + g_warning ("Failed to add monitor on '%s': %s", + path, + g_strerror (errno)); + return 0; + } + + return notify->id; +} + +void +ck_file_monitor_remove_notify (CkFileMonitor *monitor, + guint id) +{ + if (! monitor->priv->initialized_inotify) { + return; + } + + file_monitor_remove_notify (monitor, id); +} + +static void +ck_file_monitor_class_init (CkFileMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = ck_file_monitor_finalize; + + g_type_class_add_private (klass, sizeof (CkFileMonitorPrivate)); +} + + +static void +setup_inotify (CkFileMonitor *monitor) +{ + GIOChannel *io_channel; + int fd; + + if (monitor->priv->initialized_inotify) { + return; + } + + if ((fd = inotify_init ()) < 0) { + g_warning ("Failed to initialize inotify: %s", + g_strerror (errno)); + return; + } + + monitor->priv->inotify_fd = fd; + + io_channel = g_io_channel_unix_new (fd); + monitor->priv->io_watch = g_io_add_watch (io_channel, + G_IO_IN|G_IO_PRI, + (GIOFunc) inotify_data_pending, + monitor); + g_io_channel_unref (io_channel); + + monitor->priv->buflen = DEFAULT_NOTIFY_BUFLEN; + monitor->priv->buffer = g_malloc (DEFAULT_NOTIFY_BUFLEN); + + monitor->priv->notifies = g_hash_table_new (g_direct_hash, + g_direct_equal); + + monitor->priv->wd_to_watch = g_hash_table_new (g_direct_hash, + g_direct_equal); + monitor->priv->path_to_watch = g_hash_table_new (g_str_hash, + g_str_equal); + + monitor->priv->initialized_inotify = TRUE; +} + +static void +ck_file_monitor_init (CkFileMonitor *monitor) +{ + monitor->priv = CK_FILE_MONITOR_GET_PRIVATE (monitor); + + monitor->priv->serial = 1; + monitor->priv->notify_events = g_queue_new (); + + setup_inotify (monitor); +} + +static void +ck_file_monitor_finalize (GObject *object) +{ + CkFileMonitor *monitor; + + g_return_if_fail (object != NULL); + g_return_if_fail (CK_IS_FILE_MONITOR (object)); + + monitor = CK_FILE_MONITOR (object); + + g_return_if_fail (monitor->priv != NULL); + + close_inotify (monitor); + + g_hash_table_destroy (monitor->priv->notifies); + g_queue_free (monitor->priv->notify_events); + + G_OBJECT_CLASS (ck_file_monitor_parent_class)->finalize (object); +} + +CkFileMonitor * +ck_file_monitor_new (void) +{ + if (monitor_object != NULL) { + g_object_ref (monitor_object); + } else { + monitor_object = g_object_new (CK_TYPE_FILE_MONITOR, NULL); + + g_object_add_weak_pointer (monitor_object, + (gpointer *) &monitor_object); + } + + return CK_FILE_MONITOR (monitor_object); +} diff --git a/src/ck-file-monitor.h b/src/ck-file-monitor.h new file mode 100644 index 0000000..b04585e --- /dev/null +++ b/src/ck-file-monitor.h @@ -0,0 +1,84 @@ +/* -*- 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 __CK_FILE_MONITOR_H +#define __CK_FILE_MONITOR_H + +#include + +G_BEGIN_DECLS + +#define CK_TYPE_FILE_MONITOR (ck_file_monitor_get_type ()) +#define CK_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_FILE_MONITOR, CkFileMonitor)) +#define CK_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_FILE_MONITOR, CkFileMonitorClass)) +#define CK_IS_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_FILE_MONITOR)) +#define CK_IS_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_FILE_MONITOR)) +#define CK_FILE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_FILE_MONITOR, CkFileMonitorClass)) + +typedef struct CkFileMonitorPrivate CkFileMonitorPrivate; + +typedef struct +{ + GObject parent; + CkFileMonitorPrivate *priv; +} CkFileMonitor; + +typedef struct +{ + GObjectClass parent_class; +} CkFileMonitorClass; + +typedef enum +{ + CK_FILE_MONITOR_EVENT_NONE = 1 << 0, + CK_FILE_MONITOR_EVENT_ACCESS = 1 << 1, + CK_FILE_MONITOR_EVENT_CREATE = 1 << 2, + CK_FILE_MONITOR_EVENT_DELETE = 1 << 3, + CK_FILE_MONITOR_EVENT_CHANGE = 1 << 4, +} CkFileMonitorEvent; + +typedef enum +{ + CK_FILE_MONITOR_ERROR_GENERAL +} CkFileMonitorError; + +#define CK_FILE_MONITOR_ERROR ck_file_monitor_error_quark () + +typedef void (*CkFileMonitorNotifyFunc) (CkFileMonitor *monitor, + CkFileMonitorEvent event, + const char *path, + gpointer user_data); + +GQuark ck_file_monitor_error_quark (void); +GType ck_file_monitor_get_type (void); + +CkFileMonitor * ck_file_monitor_new (void); + +guint ck_file_monitor_add_notify (CkFileMonitor *monitor, + const char *path, + int mask, + CkFileMonitorNotifyFunc notify_func, + gpointer data); +void ck_file_monitor_remove_notify (CkFileMonitor *monitor, + guint id); + +G_END_DECLS + +#endif /* __CK_FILE_MONITOR_H */ diff --git a/src/ck-session.c b/src/ck-session.c index 02211e7..c4e286a 100644 --- a/src/ck-session.c +++ b/src/ck-session.c @@ -36,6 +36,7 @@ #include #include +#include "ck-tty-idle-monitor.h" #include "ck-session.h" #include "ck-session-glue.h" #include "ck-marshal.h" @@ -45,6 +46,8 @@ #define CK_DBUS_PATH "/org/freedesktop/ConsoleKit" #define CK_DBUS_NAME "org.freedesktop.ConsoleKit" +#define IDLE_TIME_SECS 60 + struct CkSessionPrivate { char *id; @@ -63,11 +66,11 @@ struct CkSessionPrivate GTimeVal creation_time; + CkTtyIdleMonitor *idle_monitor; + gboolean idle_hint; GTimeVal idle_since_hint; - guint idle_timeout_id; - DBusGConnection *connection; DBusGProxy *bus_proxy; }; @@ -141,7 +144,6 @@ register_session (CkSession *session) return TRUE; } - /* lock and unlock are separate functions because: 1. we don't maintain state for locked @@ -818,68 +820,37 @@ session_is_text (CkSession *session) } static void -maybe_update_idle_hint_from_access_time (CkSession *session, - time_t last_access) +tty_idle_changed_cb (CkTtyIdleMonitor *monitor, + gboolean idle_hint, + CkSession *session) { - time_t now; - time_t idletime; - gboolean is_idle; - - time (&now); - if (last_access > now) { - last_access = now; - } - - idletime = now - last_access; - - is_idle = (idletime > 60); - - session_set_idle_hint_internal (session, is_idle); + session_set_idle_hint_internal (session, idle_hint); } -static gboolean -check_tty_idle (CkSession *session) +static void +session_add_activity_watch (CkSession *session) { - struct stat sb; + if (session->priv->idle_monitor == NULL) { + session->priv->idle_monitor = ck_tty_idle_monitor_new (session->priv->display_device); + g_signal_connect (session->priv->idle_monitor, + "idle-hint-changed", + G_CALLBACK (tty_idle_changed_cb), + session); - if (session->priv->display_device == NULL) { - return FALSE; } - - if (g_stat (session->priv->display_device, &sb) < 0) { - g_debug ("Unable to stat: %s: %s", session->priv->display_device, g_strerror (errno)); - return FALSE; - } - - maybe_update_idle_hint_from_access_time (session, sb.st_atime); - - return TRUE; + ck_tty_idle_monitor_start (session->priv->idle_monitor); } static void -add_idle_hint_timeout (CkSession *session) +session_remove_activity_watch (CkSession *session) { - g_debug ("Adding watch for text session idle"); - /* yes this sucks - we'll add file monitoring when it is in glib */ - if (session->priv->idle_timeout_id == 0) { -#if GLIB_CHECK_VERSION(2,14,0) - session->priv->idle_timeout_id = g_timeout_add_seconds (30, - (GSourceFunc)check_tty_idle, - session); -#else - session->priv->idle_timeout_id = g_timeout_add (30000, - (GSourceFunc)check_tty_idle, - session); -#endif + if (session->priv->idle_monitor == NULL) { + return; } -} -static void -remove_idle_hint_timeout (CkSession *session) -{ - if (session->priv->idle_timeout_id > 0) { - g_source_remove (session->priv->idle_timeout_id); - } + ck_tty_idle_monitor_stop (session->priv->idle_monitor); + g_object_unref (session->priv->idle_monitor); + session->priv->idle_monitor = NULL; } static GObject * @@ -896,8 +867,7 @@ ck_session_constructor (GType type, n_construct_properties, construct_properties)); if (session_is_text (session)) { - /* FIXME: use file monitoring once it is in glib */ - add_idle_hint_timeout (session); + session_add_activity_watch (session); } return G_OBJECT (session); @@ -1082,7 +1052,7 @@ ck_session_finalize (GObject *object) g_return_if_fail (session->priv != NULL); - remove_idle_hint_timeout (session); + session_remove_activity_watch (session); g_free (session->priv->id); g_free (session->priv->cookie); diff --git a/src/ck-tty-idle-monitor.c b/src/ck-tty-idle-monitor.c new file mode 100644 index 0000000..eb071e2 --- /dev/null +++ b/src/ck-tty-idle-monitor.c @@ -0,0 +1,389 @@ +/* -*- 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 "ck-file-monitor.h" +#include "ck-tty-idle-monitor.h" + +#define CK_TTY_IDLE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitorPrivate)) + +#define DEFAULT_THRESHOLD_SECONDS 30 + +struct CkTtyIdleMonitorPrivate +{ + char *device; + guint threshold; + + guint timeout_id; + gboolean idle_hint; + GTimeVal idle_since_hint; + + CkFileMonitor *file_monitor; + guint file_notify_id; +}; + +enum { + IDLE_HINT_CHANGED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_DEVICE, + PROP_THRESHOLD, +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + +static void ck_tty_idle_monitor_class_init (CkTtyIdleMonitorClass *klass); +static void ck_tty_idle_monitor_init (CkTtyIdleMonitor *tty_idle_monitor); +static void ck_tty_idle_monitor_finalize (GObject *object); + +static void schedule_tty_check (CkTtyIdleMonitor *monitor, + guint seconds); + +G_DEFINE_TYPE (CkTtyIdleMonitor, ck_tty_idle_monitor, G_TYPE_OBJECT) + +GQuark +ck_tty_idle_monitor_error_quark (void) +{ + static GQuark ret = 0; + if (ret == 0) { + ret = g_quark_from_static_string ("ck_tty_idle_monitor_error"); + } + + return ret; +} + +static gboolean +tty_idle_monitor_set_idle_hint_internal (CkTtyIdleMonitor *tty_idle_monitor, + gboolean idle_hint) +{ + if (tty_idle_monitor->priv->idle_hint != idle_hint) { + tty_idle_monitor->priv->idle_hint = idle_hint; + + g_debug ("Emitting idle-changed for tty_idle_monitor %s: %d", tty_idle_monitor->priv->device, idle_hint); + g_signal_emit (tty_idle_monitor, signals [IDLE_HINT_CHANGED], 0, idle_hint); + return TRUE; + } + + return FALSE; +} + +static void +ck_tty_idle_monitor_set_device (CkTtyIdleMonitor *monitor, + const char *device) +{ + g_return_if_fail (CK_IS_TTY_IDLE_MONITOR (monitor)); + + g_free (monitor->priv->device); + monitor->priv->device = g_strdup (device); +} + +void +ck_tty_idle_monitor_set_threshold (CkTtyIdleMonitor *monitor, + guint threshold) +{ + g_return_if_fail (CK_IS_TTY_IDLE_MONITOR (monitor)); + + monitor->priv->threshold = threshold; +} + +static void +ck_tty_idle_monitor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CkTtyIdleMonitor *self; + + self = CK_TTY_IDLE_MONITOR (object); + + switch (prop_id) { + case PROP_DEVICE: + ck_tty_idle_monitor_set_device (self, g_value_get_string (value)); + break; + case PROP_THRESHOLD: + ck_tty_idle_monitor_set_threshold (self, g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +ck_tty_idle_monitor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CkTtyIdleMonitor *self; + + self = CK_TTY_IDLE_MONITOR (object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string (value, self->priv->device); + break; + case PROP_THRESHOLD: + g_value_set_uint (value, self->priv->threshold); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +remove_idle_hint_timeout (CkTtyIdleMonitor *tty_idle_monitor) +{ + if (tty_idle_monitor->priv->timeout_id > 0) { + g_source_remove (tty_idle_monitor->priv->timeout_id); + } +} + +static void +file_access_cb (CkFileMonitor *file_monitor, + CkFileMonitorEvent event, + const char *path, + CkTtyIdleMonitor *monitor) +{ + g_debug ("File access callback for %s", path); + + tty_idle_monitor_set_idle_hint_internal (monitor, FALSE); + + /* this resets timers */ + ck_tty_idle_monitor_start (monitor); +} + +static gboolean +monitor_add_watch (CkTtyIdleMonitor *monitor) +{ + if (monitor->priv->file_monitor == NULL) { + monitor->priv->file_monitor = ck_file_monitor_new (); + } + monitor->priv->file_notify_id = ck_file_monitor_add_notify (monitor->priv->file_monitor, + monitor->priv->device, + CK_FILE_MONITOR_EVENT_ACCESS, + (CkFileMonitorNotifyFunc)file_access_cb, + monitor); + return monitor->priv->file_notify_id > 0; +} + +static gboolean +monitor_remove_watch (CkTtyIdleMonitor *monitor) +{ + if (monitor->priv->file_monitor != NULL && + monitor->priv->file_notify_id > 0) { + g_debug ("Removing notify"); + ck_file_monitor_remove_notify (monitor->priv->file_monitor, + monitor->priv->file_notify_id); + } + + return FALSE; +} + +static gboolean +check_tty_idle (CkTtyIdleMonitor *monitor) +{ + struct stat sb; + gboolean is_idle; + gboolean changed; + time_t now; + time_t idletime; + time_t last_access; + + if (monitor->priv->device == NULL) { + return FALSE; + } + + if (g_stat (monitor->priv->device, &sb) < 0) { + g_debug ("Unable to stat: %s: %s", monitor->priv->device, g_strerror (errno)); + return FALSE; + } + + last_access = sb.st_atime; + + time (&now); + if (last_access > now) { + last_access = now; + } + + idletime = now - last_access; + is_idle = (idletime >= monitor->priv->threshold); + + changed = tty_idle_monitor_set_idle_hint_internal (monitor, is_idle); + + monitor->priv->timeout_id = 0; + + if (is_idle) { + if (! monitor_add_watch (monitor)) { + /* if we can't add a watch just add a new timer */ + g_debug ("Couldn't add watch: rescheduling check for %u sec", monitor->priv->threshold); + schedule_tty_check (monitor, monitor->priv->threshold); + } + } else { + guint remaining; + remaining = monitor->priv->threshold - idletime; + if (remaining > 0) { + g_debug ("Time left, rescheduling check for %u sec", remaining); + /* reschedule for time left */ + schedule_tty_check (monitor, remaining); + } else { + g_debug ("restarting check for %u sec", monitor->priv->threshold); + schedule_tty_check (monitor, monitor->priv->threshold); + + } + } + + return FALSE; +} + +static void +schedule_tty_check (CkTtyIdleMonitor *monitor, + guint seconds) +{ + if (monitor->priv->timeout_id == 0) { +#if GLIB_CHECK_VERSION(2,14,0) + monitor->priv->timeout_id = g_timeout_add_seconds (seconds, + (GSourceFunc)check_tty_idle, + monitor); +#else + monitor->priv->timeout_id = g_timeout_add (seconds * 1000, + (GSourceFunc)check_tty_idle, + monitor); +#endif + } +} + +static void +add_idle_hint_timeout (CkTtyIdleMonitor *monitor) +{ + schedule_tty_check (monitor, monitor->priv->threshold); +} + +void +ck_tty_idle_monitor_stop (CkTtyIdleMonitor *monitor) +{ + remove_idle_hint_timeout (monitor); + monitor_remove_watch (monitor); +} + +void +ck_tty_idle_monitor_start (CkTtyIdleMonitor *monitor) +{ + ck_tty_idle_monitor_stop (monitor); + + add_idle_hint_timeout (monitor); +} + +static void +ck_tty_idle_monitor_class_init (CkTtyIdleMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = ck_tty_idle_monitor_get_property; + object_class->set_property = ck_tty_idle_monitor_set_property; + object_class->finalize = ck_tty_idle_monitor_finalize; + + signals [IDLE_HINT_CHANGED] = + g_signal_new ("idle-hint-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CkTtyIdleMonitorClass, idle_hint_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN); + + g_object_class_install_property (object_class, + PROP_DEVICE, + g_param_spec_string ("device", + "device", + "device", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_THRESHOLD, + g_param_spec_uint ("threshold", + "Threshold", + "Threshold", + 0, + G_MAXINT, + DEFAULT_THRESHOLD_SECONDS, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (CkTtyIdleMonitorPrivate)); +} + +static void +ck_tty_idle_monitor_init (CkTtyIdleMonitor *monitor) +{ + monitor->priv = CK_TTY_IDLE_MONITOR_GET_PRIVATE (monitor); + + monitor->priv->threshold = DEFAULT_THRESHOLD_SECONDS; +} + +static void +ck_tty_idle_monitor_finalize (GObject *object) +{ + CkTtyIdleMonitor *monitor; + + g_return_if_fail (object != NULL); + g_return_if_fail (CK_IS_TTY_IDLE_MONITOR (object)); + + monitor = CK_TTY_IDLE_MONITOR (object); + + g_return_if_fail (monitor->priv != NULL); + + ck_tty_idle_monitor_stop (monitor); + + g_free (monitor->priv->device); + + G_OBJECT_CLASS (ck_tty_idle_monitor_parent_class)->finalize (object); +} + +CkTtyIdleMonitor * +ck_tty_idle_monitor_new (const char *device) +{ + GObject *object; + + object = g_object_new (CK_TYPE_TTY_IDLE_MONITOR, + "device", device, + NULL); + + return CK_TTY_IDLE_MONITOR (object); +} diff --git a/src/ck-tty-idle-monitor.h b/src/ck-tty-idle-monitor.h new file mode 100644 index 0000000..51cefe6 --- /dev/null +++ b/src/ck-tty-idle-monitor.h @@ -0,0 +1,71 @@ +/* -*- 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 __CK_TTY_IDLE_MONITOR_H +#define __CK_TTY_IDLE_MONITOR_H + +#include +#include + +G_BEGIN_DECLS + +#define CK_TYPE_TTY_IDLE_MONITOR (ck_tty_idle_monitor_get_type ()) +#define CK_TTY_IDLE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitor)) +#define CK_TTY_IDLE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitorClass)) +#define CK_IS_TTY_IDLE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CK_TYPE_TTY_IDLE_MONITOR)) +#define CK_IS_TTY_IDLE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CK_TYPE_TTY_IDLE_MONITOR)) +#define CK_TTY_IDLE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CK_TYPE_TTY_IDLE_MONITOR, CkTtyIdleMonitorClass)) + +typedef struct CkTtyIdleMonitorPrivate CkTtyIdleMonitorPrivate; + +typedef struct +{ + GObject parent; + CkTtyIdleMonitorPrivate *priv; +} CkTtyIdleMonitor; + +typedef struct +{ + GObjectClass parent_class; + + void (* idle_hint_changed) (CkTtyIdleMonitor *monitor, + gboolean idle_hint); +} CkTtyIdleMonitorClass; + +typedef enum +{ + CK_TTY_IDLE_MONITOR_ERROR_GENERAL +} CkTtyIdleMonitorError; + +#define CK_TTY_IDLE_MONITOR_ERROR ck_tty_idle_monitor_error_quark () + +GQuark ck_tty_idle_monitor_error_quark (void); +GType ck_tty_idle_monitor_get_type (void); + +CkTtyIdleMonitor * ck_tty_idle_monitor_new (const char *device); +void ck_tty_idle_monitor_set_threshold (CkTtyIdleMonitor *monitor, + guint seconds); +void ck_tty_idle_monitor_start (CkTtyIdleMonitor *monitor); +void ck_tty_idle_monitor_stop (CkTtyIdleMonitor *monitor); + +G_END_DECLS + +#endif /* __CK_TTY_IDLE_MONITOR_H */ diff --git a/src/test-tty-idle-monitor.c b/src/test-tty-idle-monitor.c new file mode 100644 index 0000000..7d44b26 --- /dev/null +++ b/src/test-tty-idle-monitor.c @@ -0,0 +1,82 @@ +/* -*- 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 "ck-tty-idle-monitor.h" + +static void +idle_changed_cb (CkTtyIdleMonitor *monitor, + gboolean idle_hint, + gpointer data) +{ + g_message ("idle hint changed: %s", idle_hint ? "idle" : "not idle"); +} + +int +main (int argc, char **argv) +{ + GMainLoop *loop; + CkTtyIdleMonitor *monitor; + char *device; + + g_type_init (); + + if (argc < 2) { + device = g_file_read_link ("/proc/self/fd/0", NULL); + if (device == NULL) { + g_warning ("%s not a link", "/proc/self/fd/0"); + exit (1); + } + } else { + device = g_strdup (argv[1]); + } + + monitor = ck_tty_idle_monitor_new (device); + + g_signal_connect (monitor, + "idle-hint-changed", + G_CALLBACK (idle_changed_cb), + NULL); + ck_tty_idle_monitor_set_threshold (monitor, 5); + ck_tty_idle_monitor_start (monitor); + + loop = g_main_loop_new (NULL, FALSE); + + g_main_loop_run (loop); + + g_object_unref (monitor); + + g_main_loop_unref (loop); + + return 0; +} -- cgit