diff options
| author | William Jon McCann <mccann@jhu.edu> | 2007-04-05 15:21:14 -0400 | 
|---|---|---|
| committer | William Jon McCann <mccann@jhu.edu> | 2007-04-05 15:21:14 -0400 | 
| commit | e0244a8f6dd0b7f8ebecc6bec52c013ce5286279 (patch) | |
| tree | 9e890e4d7bba3ed70194c012fccbd159357afa1f | |
| parent | 33dcd02c399e3255a7a64c1e90b258d79c14f2c4 (diff) | |
use inotify to detect activity on tty when possible
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | configure.ac | 26 | ||||
| -rw-r--r-- | doc/.gitignore | 1 | ||||
| -rw-r--r-- | src/.gitignore | 1 | ||||
| -rw-r--r-- | src/Makefile.am | 32 | ||||
| -rw-r--r-- | src/ck-file-monitor-dummy.c | 118 | ||||
| -rw-r--r-- | src/ck-file-monitor-inotify.c | 669 | ||||
| -rw-r--r-- | src/ck-file-monitor.h | 84 | ||||
| -rw-r--r-- | src/ck-session.c | 82 | ||||
| -rw-r--r-- | src/ck-tty-idle-monitor.c | 389 | ||||
| -rw-r--r-- | src/ck-tty-idle-monitor.h | 71 | ||||
| -rw-r--r-- | src/test-tty-idle-monitor.c | 82 | 
12 files changed, 1496 insertions, 62 deletions
@@ -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=<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) @@ -229,13 +228,30 @@ 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 ---------------------------------------------------------------------------  # 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 <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 <glib.h> +#include <glib-object.h> + +#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 <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/inotify.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#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 : "<none>"); + +                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 <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. + * + */ + +#ifndef __CK_FILE_MONITOR_H +#define __CK_FILE_MONITOR_H + +#include <glib-object.h> + +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 <dbus/dbus-glib.h>  #include <dbus/dbus-glib-lowlevel.h> +#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 <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 <signal.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> +#include <glib-object.h> + +#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 <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. + * + */ + + +#ifndef __CK_TTY_IDLE_MONITOR_H +#define __CK_TTY_IDLE_MONITOR_H + +#include <glib-object.h> +#include <dbus/dbus-glib.h> + +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 <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 <sys/types.h> +#include <pwd.h> +#include <string.h> +#include <errno.h> + +#include <locale.h> + +#include <glib.h> + +#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; +}  | 
