diff lisp/prim/itimer.el @ 108:360340f9fd5f r20-1b6

Import from CVS: tag r20-1b6
author cvs
date Mon, 13 Aug 2007 09:18:39 +0200
parents a145efe76779
children fe104dbd9147
line wrap: on
line diff
--- a/lisp/prim/itimer.el	Mon Aug 13 09:17:27 2007 +0200
+++ b/lisp/prim/itimer.el	Mon Aug 13 09:18:39 2007 +0200
@@ -1,34 +1,22 @@
-;;; itimer.el -- Interval timers for XEmacs
-
-;; Copyright (C) 1988, 1991, 1993, 1997 Kyle E. Jones
-
-;; Author: Kyle Jones <kyle_jones@wonderworks.com>
-;; Keywords: extensions
-
-;; This file is part of XEmacs
-
-;; XEmacs is free software; you can redistribute it and/or modify it
-;; under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
-
-;; XEmacs 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.
-
-;; A copy of the GNU General Public License can be obtained from this
-;; program's author (send electronic mail to kyle@uunet.uu.net) or
-;; from the Free Software Foundation, Inc., 59 Temple Place - Suite
-;; 330, Boston, MA  02111-1307, USA.
-
-;;; Synched up with:  Not in FSF
-
-;;; Commentary:
-
-;; Send bug reports to kyle_jones@wonderworks.com
-
-;;; Code:
+;;; Interval timers for GNU Emacs
+;;; Copyright (C) 1988, 1991, 1993, 1997 Kyle E. Jones
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; A copy of the GNU General Public License can be obtained from this
+;;; program's author (send electronic mail to kyle@uunet.uu.net) or from
+;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+;;; 02139, USA.
+;;;
+;;; Send bug reports to kyle_jones@wonderworks.com
 
 (provide 'itimer)
 
@@ -37,16 +25,18 @@
 ;;    itimer-value
 ;;    itimer-restart
 ;;    itimer-function
-;;    itimer-function-argument
+;;    itimer-uses-arguments
+;;    itimer-function-arguments
 ;;    set-itimer-value
 ;;    set-itimer-restart
 ;;    set-itimer-function
-;;    set-itimer-uses-argument
-;;    set-itimer-function-argument
+;;    set-itimer-uses-arguments
+;;    set-itimer-function-arguments
 ;;    get-itimer
 ;;    start-itimer
 ;;    read-itimer
 ;;    delete-itimer
+;;    activate-itimer
 ;;
 ;; Interactive users get these commands:
 ;;    edit-itimers
@@ -55,7 +45,7 @@
 ;;
 ;; See the doc strings of these functions for more information.
 
-(defvar itimer-version "1.02"
+(defvar itimer-version "1.03"
   "Version number of the itimer package.")
 
 (defvar itimer-list nil
@@ -167,7 +157,7 @@
 
 (defun itimerp (obj)
   "Returns non-nil iff OBJ is an itimer."
-  (and (consp obj) (stringp (car obj)) (eq (length obj) 6)))
+  (and (consp obj) (eq (length obj) 8)))
 
 (defun itimer-name (itimer)
   "Returns the name of ITIMER."
@@ -191,18 +181,31 @@
   (check-itimer itimer)
   (nth 3 itimer))
 
-(defun itimer-uses-argument (itimer)
-  "Returns non-nil if the function of ITIMER will be called with an argment.
-ITIMER's function is called with this argument each timer ITIMER expires."
+(defun itimer-is-idle (itimer)
+  "Returns non-nil if ITIMER is an idle timer.
+Normal timers eexpire after a set interval.  Idle timers expire
+only after Emacs has been idle for a specific interval.  ``Idle''
+means no command events within the interval."
   (check-itimer itimer)
   (nth 4 itimer))
 
-(defun itimer-function-argument (itimer)
-  "Returns the function argument of ITIMER.
-ITIMER's function is called with this argument each timer ITIMER expires."
+(defun itimer-uses-arguments (itimer)
+  "Returns non-nil if the function of ITIMER will be called with arguments.
+ITIMER's function is called with the arguments each time ITIMER expires.
+The arguments themselves are retrievable with `itimer-function-arguments'."
   (check-itimer itimer)
   (nth 5 itimer))
 
+(defun itimer-function-arguments (itimer)
+  "Returns the function arguments of ITIMER as a list.
+ITIMER's function is called with these argument each timer ITIMER expires."
+  (check-itimer itimer)
+  (nth 6 itimer))
+
+(defun itimer-recorded-run-time (itimer)
+  (check-itimer itimer)
+  (nth 7 itimer))
+
 (defun set-itimer-value (itimer value)
   "Set the timeout value of ITIMER to be VALUE.
 Itimer will expire is this many seconds.
@@ -248,23 +251,33 @@
 FUNCTION will be called when itimer expires.
 Returns FUNCTION."
   (check-itimer itimer)
-  (setcar (cdr (cdr (cdr itimer))) function))
+  (setcar (nthcdr 3 itimer) function))
 
-(defun set-itimer-uses-argument (itimer flag)
-  "Sets when the function of ITIMER is called with an argument.
+(defun set-itimer-is-idle (itimer flag)
+  "Sets flag that says whether ITIMER is an idle timer.
+If FLAG is non-nil, then ITIMER will eb considered an idle timer.
+Returns FLAG."
+  (check-itimer itimer)
+  (setcar (nthcdr 4 itimer) flag))
+
+(defun set-itimer-uses-arguments (itimer flag)
+  "Sets flag that says whether the function of ITIMER is called with arguments.
 If FLAG is non-nil, then the function will be called with one argument,
 otherwise the function will be called with no arguments.
 Returns FLAG."
   (check-itimer itimer)
-  (setcar (nthcdr 4 itimer) flag))
+  (setcar (nthcdr 5 itimer) flag))
 
-(defun set-itimer-function-argument (itimer argument)
-  "Set the function of ITIMER to be ARGUMENT.
-The function of ITIMER will be called with ARGUMENT as its solt argument
-when itimer expires.
-Returns ARGUMENT."
+(defun set-itimer-function-arguments (itimer &rest arguments)
+  "Set the function arguments of ITIMER to be ARGUMENTS.
+The function of ITIMER will be called with ARGUMENTS when itimer expires.
+Returns ARGUMENTS."
   (check-itimer itimer)
-  (setcar (nthcdr 5 itimer) argument))
+  (setcar (nthcdr 6 itimer) arguments))
+
+(defun set-itimer-recorded-run-time (itimer time)
+  (check-itimer itimer)
+  (setcar (nthcdr 7 itimer) time))
 
 (defun get-itimer (name)
   "Return itimer named NAME, or nil if there is none."
@@ -284,16 +297,17 @@
   (setq itimer-list (delq itimer itimer-list)))
 
 (defun start-itimer (name function value &optional restart
-		     with-arg function-argument)
+		     is-idle with-args &rest function-arguments)
   "Start an itimer.
-Args are NAME, FUNCTION, VALUE &optional RESTART, WITH-ARG, FUNCTION-ARGUMENT.
+Arguments are
+  NAME, FUNCTION, VALUE &optional RESTART, IS-IDLE, WITH-ARGS, &rest FUNCTION-ARGUMENTS.
 NAME is an identifier for the itimer.  It must be a string.  If an itimer
   already exists with this name, NAME will be modified slightly to until
   it is unique.
-FUNCTION should be a function (or symbol naming one) of one argument.  It
-  will be called each time the itimer expires with an argument of
-  FUNCTION-ARGUMENT.  The function can access the itimer that
-  invoked it through the variable `current-itimer'.  If WITH-ARG
+FUNCTION should be a function (or symbol naming one).  It
+  will be called each time the itimer expires with arguments of
+  FUNCTION-ARGUMENTS.  The function can access the itimer that
+  invoked it through the variable `current-itimer'.  If WITH-ARGS
   is nil then FUNCTION is called with no arguments.  This is for
   backward compatibility with older versions of the itimer
   package which always called FUNCTION with no arguments.
@@ -306,6 +320,10 @@
   is deleted at expiration after its function has returned. 
   If non-nil RESTART should be a number indicating the value at which the
   itimer should be set at restart time.
+Optional fifth arg IS-IDLE specified if this is an idle timer.
+  Normal timers eexpire after a set interval.  Idle timers expire
+  only after Emacs has been idle for specific interval.  ``Idle''
+  means no command events within the interval.
 Returns the newly created itimer."
   (interactive
    (list (completing-read "Start itimer: " itimer-list)
@@ -331,23 +349,58 @@
     (while (get-itimer name)
       (setq name (concat oname "<" num ">"))
       (itimer-increment num)))
-  ;; If there's no itimer process, start one now.
-  ;; Otherwise wake up the itimer process so that seconds slept before
+  (activate-itimer (list name value restart function is-idle
+			 with-args function-arguments (list 0 0 0)))
+  (car itimer-list))
+
+(defun make-itimer ()
+  "Create an unactivated itimer.
+The itimer will not begin running until activated with `activate-itimer'.
+Set the itimer's expire interval with `set-itimer-value'.
+Set the itimer's function interval with `set-itimer-function'.
+Once this is done, the timer can be activated."
+  (list nil 0 nil 'ignore nil nil nil (list 0 0 0)))
+
+(defun activate-itimer (itimer)
+  "Activate ITIMER, which was previously created with `make-itimer'.
+ITIMER will be added to the global list of running itimers,
+its FUNCTION will be called when it expires, and so on."
+  (check-itimer itimer)
+  (if (memq itimer itimer-list)
+      (error "itimer already activated"))
+  (if (not (numberp (itimer-value itimer)))
+      (error "itimer timeout value not a number: %s" (itimer-value itimer)))
+  (if (<= (itimer-value itimer) 0)
+      (error "itimer timeout value not positive: %s" (itimer-value itimer)))
+  ;; If there's no itimer driver/process, start one now.
+  ;; Otherwise wake up the itimer driver so that seconds slept before
   ;; the new itimer is created won't be counted against it.
   (if (or itimer-process itimer-timer)
       (itimer-driver-wakeup)
     (itimer-driver-start))
+  ;; Roll a unique name for the timer if it doesn't have a name
+  ;; already.
+  (if (not (stringp (car itimer)))
+      (let ((name "itimer-0")
+	    (oname "itimer-")
+	    (num 1))
+	(while (get-itimer name)
+	  (setq name (concat oname "<" num ">"))
+	  (itimer-increment num))
+	(setcar itimer name))
+    ;; signal an error if the timer's name matches an already
+    ;; activated timer.
+    (if (get-itimer (itimer-name itimer))
+	(error "itimer named \"%s\" already existing and activated"
+	       (itimer-name itimer))))
   (let ((inhibit-quit t))
     ;; add the itimer to the global list
-    (setq itimer-list
-	  (cons (list name value restart function with-arg function-argument)
-		itimer-list))
-    ;; If the itimer process is scheduled to wake up too late for the itimer
-    ;; we wake it up to calculate a correct wakeup value giving consideration
-    ;; to the newly added itimer.
-    (if (< value itimer-next-wakeup)
-	(itimer-driver-wakeup)))
-  (car itimer-list))
+    (setq itimer-list (cons itimer itimer-list))
+    ;; If the itimer process is scheduled to wake up too late for
+    ;; the itimer we wake it up to calculate a correct wakeup
+    ;; value giving consideration to the newly added itimer.
+    (if (< (itimer-value itimer) itimer-next-wakeup)
+	(itimer-driver-wakeup))))
 
 ;; User level functions to list and modify existing itimers.
 ;; Itimer Edit major mode, and the editing commands thereof.
@@ -367,8 +420,9 @@
     (setq buffer-read-only nil)
     (erase-buffer)
     (insert
-"Name                  Value   Restart   Function                   Argument\n"
-"----                  -----   -------   --------                   --------")
+"Name                  Value   Restart   Function            Idle   Arguments"
+"\n"
+"----                  -----   -------   --------            ----   --------")
     (if (null itimer-edit-start-marker)
 	(setq itimer-edit-start-marker (point)))
     (while itimers
@@ -382,10 +436,14 @@
 	       (format "%5.5s" (itimer-restart (car itimers))) 5))
       (tab-to-tab-stop)
       (insert (itimer-truncate-string
-	       (format "%.26s" (itimer-function (car itimers))) 26))
+	       (format "%.19s" (itimer-function (car itimers))) 19))
       (tab-to-tab-stop)
-      (if (itimer-uses-argument (car itimers))
-	  (prin1 (itimer-function-argument (car itimers)))
+      (if (itimer-is-idle (car itimers))
+	  (insert "yes")
+	(insert "no"))
+      (tab-to-tab-stop)
+      (if (itimer-uses-arguments (car itimers))
+	  (prin1 (itimer-function-arguments (car itimers)))
 	(prin1 'NONE))
       (setq itimers (cdr itimers)))
     ;; restore point
@@ -416,7 +474,7 @@
 ;; no point in making this interactive.
 (defun itimer-edit-mode ()
   "Major mode for manipulating itimers.
-Atrributes of running itimers are changed by moving the cursor to the
+Attributes of running itimers are changed by moving the cursor to the
 desired field and typing `s' to set that field.  The field will then be
 set to the value read from the minibuffer.
 
@@ -432,7 +490,7 @@
   (setq major-mode 'itimer-edit-mode
 	mode-name "Itimer Edit"
 	truncate-lines t
-	tab-stop-list '(22 32 40 67))
+	tab-stop-list '(22 32 40 60 67))
   (abbrev-mode 0)
   (auto-fill-mode 0)
   (buffer-flush-undo (current-buffer))
@@ -478,7 +536,7 @@
 		    ;; and use this info to determine which field the user
 		    ;; wants to modify.
 		    (beginning-of-line)
-		    (while (and (>= opoint (point)) (< n 5))
+		    (while (and (>= opoint (point)) (< n 6))
 		      (forward-sexp 2)
 		      (backward-sexp)
 		      (itimer-increment n))
@@ -486,6 +544,7 @@
 			  ((eq n 2) 'value)
 			  ((eq n 3) 'restart)
 			  ((eq n 4) 'function)
+			  ((eq n 5) 'is-idle)
 			  (t 'function-argument)))))
     (cond ((eq field 'value)
 	   (let ((prompt "Set itimer value: "))
@@ -502,10 +561,16 @@
 				  (memq (car field-value) '(lambda macro)))))
 	       (setq field-value
 		     (read (completing-read prompt obarray 'fboundp nil))))))
+	  ((eq field 'is-idle)
+	   (setq field-value (not (itimer-is-idle itimer))))
 	  ((eq field 'function-argument)
 	   (let ((prompt "Set itimer function argument: "))
 	     (setq field-value (read-expression prompt))
-	     (set-itimer-uses-argument itimer t))))
+	     (cond ((not (listp field-value))
+		    (setq field-value (list field-value))))
+	     (if (null field-value)
+		 (set-itimer-uses-arguments itimer nil)
+	       (set-itimer-uses-arguments itimer t)))))
     ;; set the itimer field
     (funcall (intern (concat "set-itimer-" (symbol-name field)))
 	     itimer field-value)
@@ -586,19 +651,44 @@
   (let ((itimers (copy-sequence itimer-list))
 	(itimer)
 	(next-wakeup 600)
+	(idle-time)
+	(last-event-time)
+	(recorded-run-time)
 	;; process filters can be hit by stray C-g's from the user,
 	;; so we must protect this stuff appropriately.
 	;; Quit's are allowed from within itimer functions, but we
 	;; catch them and print a message.
 	(inhibit-quit t))
     (setq next-wakeup 600)
+    (if (and (boundp 'last-input-time) (consp last-input-time))
+	(setq last-event-time (list (car last-input-time)
+				    (cdr last-input-time)
+				    0)
+	      idle-time (itimer-time-difference (current-time)
+						last-event-time))
+      ;; no way to do this under FSF Emacs yet.
+      (setq last-event-time '(0 0 0)
+	    idle-time 0))
     (while itimers
       (setq itimer (car itimers))
-      (set-itimer-value-internal itimer (max 0 (- (itimer-value itimer)
-						  time-elapsed)))
-      (if (> (itimer-value itimer) 0)
+      (if (itimer-is-idle itimer)
+	  (setq recorded-run-time (itimer-recorded-run-time itimer))
+	(set-itimer-value-internal itimer (max 0 (- (itimer-value itimer)
+						    time-elapsed))))
+      (if (if (itimer-is-idle itimer)
+	      (or (> (itimer-time-difference recorded-run-time
+					     last-event-time)
+		     0)
+		  (< idle-time (itimer-value itimer)))
+	    (> (itimer-value itimer) 0))
 	  (setq next-wakeup
-		(min next-wakeup (itimer-value itimer)))
+		(if (itimer-is-idle itimer)
+		    (if (< idle-time (itimer-value itimer))
+			(min next-wakeup (- (itimer-value itimer) idle-time))
+		      (min next-wakeup (itimer-value itimer)))
+		  (min next-wakeup (itimer-value itimer))))
+	(and (itimer-is-idle itimer)
+	     (set-itimer-recorded-run-time itimer (current-time)))
 	;; itimer has expired, we must call its function.
 	;; protect our local vars from the itimer function.
 	;; allow keyboard quit to occur, but catch and report it.
@@ -610,9 +700,9 @@
 		     (quit-flag nil)
 		     (inhibit-quit nil)
 		     itimer itimers time-elapsed)
-		(if (itimer-uses-argument current-itimer)
-		    (funcall (itimer-function current-itimer)
-			     (itimer-function-argument current-itimer))
+		(if (itimer-uses-arguments current-itimer)
+		    (apply (itimer-function current-itimer)
+			   (itimer-function-arguments current-itimer))
 		  (funcall (itimer-function current-itimer)))))
 	  (error (message "itimer \"%s\" signaled: %s" (itimer-name itimer)
 			  (prin1-to-string condition-data)))
@@ -701,7 +791,7 @@
 				    'itimer-timer-driver nil nil))))
 
 (defun itimer-time-difference (t1 t2)
-  (let (usecs secs 65536-secs)
+  (let (usecs secs 65536-secs carry)
     (setq usecs (- (nth 2 t1) (nth 2 t2)))
     (if (< usecs 0)
 	(setq carry 1
@@ -709,8 +799,8 @@
       (setq carry 0))
     (setq secs (- (nth 1 t1) (nth 1 t2) carry))
     (if (< secs 0)
-	(setq carry 1
-	      secs (+ secs 65536))
+	 (setq carry 1
+	       secs (+ secs 65536))
       (setq carry 0))
     (setq 65536-secs (- (nth 0 t1) (nth 0 t2) carry))
     ;; loses for interval larger than the maximum signed Lisp integer.
@@ -743,5 +833,3 @@
   (if (fboundp 'add-timeout)
       (itimer-timer-wakeup)
     (itimer-process-wakeup)))
-
-;;; itimer.el ends here